aboutsummaryrefslogtreecommitdiff
path: root/net
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2011-05-20 13:43:21 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2011-05-20 13:43:21 -0700
commit06f4e926d256d902dd9a53dcb400fd74974ce087 (patch)
tree0b438b67f5f0eff6fd617bc497a9dace6164a488 /net
parent8e7bfcbab3825d1b404d615cb1b54f44ff81f981 (diff)
parentd93515611bbc70c2fe4db232e5feb448ed8e4cc9 (diff)
downloadlinux-06f4e926d256d902dd9a53dcb400fd74974ce087.tar.gz
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next-2.6: (1446 commits) macvlan: fix panic if lowerdev in a bond tg3: Add braces around 5906 workaround. tg3: Fix NETIF_F_LOOPBACK error macvlan: remove one synchronize_rcu() call networking: NET_CLS_ROUTE4 depends on INET irda: Fix error propagation in ircomm_lmp_connect_response() irda: Kill set but unused variable 'bytes' in irlan_check_command_param() irda: Kill set but unused variable 'clen' in ircomm_connect_indication() rxrpc: Fix set but unused variable 'usage' in rxrpc_get_transport() be2net: Kill set but unused variable 'req' in lancer_fw_download() irda: Kill set but unused vars 'saddr' and 'daddr' in irlan_provider_connect_indication() atl1c: atl1c_resume() is only used when CONFIG_PM_SLEEP is defined. rxrpc: Fix set but unused variable 'usage' in rxrpc_get_peer(). rxrpc: Kill set but unused variable 'local' in rxrpc_UDP_error_handler() rxrpc: Kill set but unused variable 'sp' in rxrpc_process_connection() rxrpc: Kill set but unused variable 'sp' in rxrpc_rotate_tx_window() pkt_sched: Kill set but unused variable 'protocol' in tc_classify() isdn: capi: Use pr_debug() instead of ifdefs. tg3: Update version to 3.119 tg3: Apply rx_discards fix to 5719/5720 ... Fix up trivial conflicts in arch/x86/Kconfig and net/mac80211/agg-tx.c as per Davem.
Diffstat (limited to 'net')
-rw-r--r--net/802/garp.c22
-rw-r--r--net/8021q/vlan.c38
-rw-r--r--net/8021q/vlan.h2
-rw-r--r--net/8021q/vlan_core.c85
-rw-r--r--net/8021q/vlan_dev.c225
-rw-r--r--net/9p/client.c9
-rw-r--r--net/9p/trans_fd.c2
-rw-r--r--net/9p/trans_rdma.c2
-rw-r--r--net/Kconfig14
-rw-r--r--net/atm/lec.c4
-rw-r--r--net/ax25/af_ax25.c16
-rw-r--r--net/ax25/ax25_iface.c3
-rw-r--r--net/batman-adv/aggregation.c31
-rw-r--r--net/batman-adv/aggregation.h4
-rw-r--r--net/batman-adv/bat_debugfs.c4
-rw-r--r--net/batman-adv/bat_sysfs.c16
-rw-r--r--net/batman-adv/gateway_client.c296
-rw-r--r--net/batman-adv/gateway_client.h2
-rw-r--r--net/batman-adv/hard-interface.c107
-rw-r--r--net/batman-adv/hard-interface.h18
-rw-r--r--net/batman-adv/icmp_socket.c37
-rw-r--r--net/batman-adv/main.c20
-rw-r--r--net/batman-adv/main.h44
-rw-r--r--net/batman-adv/originator.c82
-rw-r--r--net/batman-adv/originator.h1
-rw-r--r--net/batman-adv/packet.h5
-rw-r--r--net/batman-adv/routing.c572
-rw-r--r--net/batman-adv/routing.h6
-rw-r--r--net/batman-adv/send.c72
-rw-r--r--net/batman-adv/send.h2
-rw-r--r--net/batman-adv/soft-interface.c463
-rw-r--r--net/batman-adv/translation-table.c474
-rw-r--r--net/batman-adv/translation-table.h24
-rw-r--r--net/batman-adv/types.h56
-rw-r--r--net/batman-adv/unicast.c20
-rw-r--r--net/batman-adv/vis.c146
-rw-r--r--net/bluetooth/bnep/bnep.h148
-rw-r--r--net/bluetooth/bnep/core.c71
-rw-r--r--net/bluetooth/bnep/sock.c2
-rw-r--r--net/bluetooth/cmtp/capi.c6
-rw-r--r--net/bluetooth/cmtp/cmtp.h11
-rw-r--r--net/bluetooth/cmtp/core.c28
-rw-r--r--net/bluetooth/cmtp/sock.c2
-rw-r--r--net/bluetooth/hci_conn.c95
-rw-r--r--net/bluetooth/hci_core.c154
-rw-r--r--net/bluetooth/hci_event.c267
-rw-r--r--net/bluetooth/hci_sysfs.c71
-rw-r--r--net/bluetooth/hidp/core.c96
-rw-r--r--net/bluetooth/hidp/hidp.h6
-rw-r--r--net/bluetooth/hidp/sock.c7
-rw-r--r--net/bluetooth/l2cap_core.c1966
-rw-r--r--net/bluetooth/l2cap_sock.c330
-rw-r--r--net/bluetooth/mgmt.c612
-rw-r--r--net/bluetooth/rfcomm/core.c23
-rw-r--r--net/bluetooth/rfcomm/sock.c5
-rw-r--r--net/bridge/br.c1
-rw-r--r--net/bridge/br_device.c100
-rw-r--r--net/bridge/br_fdb.c311
-rw-r--r--net/bridge/br_if.c104
-rw-r--r--net/bridge/br_input.c5
-rw-r--r--net/bridge/br_ioctl.c40
-rw-r--r--net/bridge/br_multicast.c12
-rw-r--r--net/bridge/br_netfilter.c4
-rw-r--r--net/bridge/br_netlink.c60
-rw-r--r--net/bridge/br_notify.c11
-rw-r--r--net/bridge/br_private.h22
-rw-r--r--net/bridge/br_private_stp.h13
-rw-r--r--net/bridge/br_stp.c48
-rw-r--r--net/bridge/br_stp_if.c21
-rw-r--r--net/bridge/br_sysfs_br.c39
-rw-r--r--net/bridge/br_sysfs_if.c26
-rw-r--r--net/caif/Makefile2
-rw-r--r--net/caif/caif_config_util.c99
-rw-r--r--net/caif/caif_dev.c387
-rw-r--r--net/caif/caif_socket.c106
-rw-r--r--net/caif/cfcnfg.c507
-rw-r--r--net/caif/cfctrl.c196
-rw-r--r--net/caif/cfdgml.c7
-rw-r--r--net/caif/cffrml.c60
-rw-r--r--net/caif/cfmuxl.c162
-rw-r--r--net/caif/cfpkt_skbuff.c205
-rw-r--r--net/caif/cfrfml.c4
-rw-r--r--net/caif/cfserl.c7
-rw-r--r--net/caif/cfsrvl.c40
-rw-r--r--net/caif/cfutill.c7
-rw-r--r--net/caif/cfveil.c11
-rw-r--r--net/caif/cfvidl.c5
-rw-r--r--net/caif/chnl_net.c45
-rw-r--r--net/can/af_can.c67
-rw-r--r--net/can/bcm.c2
-rw-r--r--net/can/raw.c2
-rw-r--r--net/compat.c16
-rw-r--r--net/core/dev.c187
-rw-r--r--net/core/dst.c54
-rw-r--r--net/core/ethtool.c196
-rw-r--r--net/core/fib_rules.c3
-rw-r--r--net/core/filter.c65
-rw-r--r--net/core/net-sysfs.c26
-rw-r--r--net/core/net_namespace.c12
-rw-r--r--net/core/netpoll.c28
-rw-r--r--net/core/pktgen.c199
-rw-r--r--net/core/rtnetlink.c25
-rw-r--r--net/core/skbuff.c3
-rw-r--r--net/core/sysctl_net_core.c9
-rw-r--r--net/core/utils.c24
-rw-r--r--net/dccp/ipv4.c53
-rw-r--r--net/dccp/ipv6.c10
-rw-r--r--net/dccp/output.c4
-rw-r--r--net/decnet/dn_dev.c10
-rw-r--r--net/decnet/dn_route.c15
-rw-r--r--net/decnet/dn_table.c4
-rw-r--r--net/dsa/slave.c1
-rw-r--r--net/econet/af_econet.c8
-rw-r--r--net/ipv4/Makefile2
-rw-r--r--net/ipv4/af_inet.c53
-rw-r--r--net/ipv4/ah4.c7
-rw-r--r--net/ipv4/cipso_ipv4.c113
-rw-r--r--net/ipv4/datagram.c22
-rw-r--r--net/ipv4/devinet.c4
-rw-r--r--net/ipv4/esp4.c7
-rw-r--r--net/ipv4/fib_frontend.c16
-rw-r--r--net/ipv4/fib_trie.c110
-rw-r--r--net/ipv4/icmp.c133
-rw-r--r--net/ipv4/igmp.c22
-rw-r--r--net/ipv4/inet_connection_sock.c59
-rw-r--r--net/ipv4/inet_diag.c2
-rw-r--r--net/ipv4/inet_lro.c4
-rw-r--r--net/ipv4/ip_forward.c2
-rw-r--r--net/ipv4/ip_fragment.c58
-rw-r--r--net/ipv4/ip_gre.c70
-rw-r--r--net/ipv4/ip_input.c4
-rw-r--r--net/ipv4/ip_options.c57
-rw-r--r--net/ipv4/ip_output.c158
-rw-r--r--net/ipv4/ip_sockglue.c37
-rw-r--r--net/ipv4/ipcomp.c4
-rw-r--r--net/ipv4/ipconfig.c35
-rw-r--r--net/ipv4/ipip.c36
-rw-r--r--net/ipv4/ipmr.c39
-rw-r--r--net/ipv4/netfilter/arp_tables.c18
-rw-r--r--net/ipv4/netfilter/ip_tables.c28
-rw-r--r--net/ipv4/netfilter/nf_nat_helper.c2
-rw-r--r--net/ipv4/ping.c935
-rw-r--r--net/ipv4/raw.c92
-rw-r--r--net/ipv4/route.c385
-rw-r--r--net/ipv4/syncookies.c22
-rw-r--r--net/ipv4/sysctl_net_ipv4.c68
-rw-r--r--net/ipv4/tcp.c7
-rw-r--r--net/ipv4/tcp_ipv4.c98
-rw-r--r--net/ipv4/tcp_output.c2
-rw-r--r--net/ipv4/udp.c78
-rw-r--r--net/ipv4/xfrm4_policy.c38
-rw-r--r--net/ipv4/xfrm4_state.c2
-rw-r--r--net/ipv6/addrconf.c42
-rw-r--r--net/ipv6/af_inet6.c2
-rw-r--r--net/ipv6/anycast.c16
-rw-r--r--net/ipv6/esp6.c5
-rw-r--r--net/ipv6/icmp.c8
-rw-r--r--net/ipv6/inet6_connection_sock.c2
-rw-r--r--net/ipv6/ip6_fib.c20
-rw-r--r--net/ipv6/ip6_input.c6
-rw-r--r--net/ipv6/ip6_output.c50
-rw-r--r--net/ipv6/ip6_tunnel.c46
-rw-r--r--net/ipv6/ip6mr.c4
-rw-r--r--net/ipv6/ipcomp6.c5
-rw-r--r--net/ipv6/mcast.c36
-rw-r--r--net/ipv6/mip6.c8
-rw-r--r--net/ipv6/ndisc.c51
-rw-r--r--net/ipv6/netfilter.c10
-rw-r--r--net/ipv6/netfilter/ip6_tables.c21
-rw-r--r--net/ipv6/netfilter/ip6table_mangle.c3
-rw-r--r--net/ipv6/proc.c40
-rw-r--r--net/ipv6/raw.c18
-rw-r--r--net/ipv6/reassembly.c4
-rw-r--r--net/ipv6/route.c157
-rw-r--r--net/ipv6/sit.c40
-rw-r--r--net/ipv6/syncookies.c13
-rw-r--r--net/ipv6/tcp_ipv6.c50
-rw-r--r--net/ipv6/udp.c22
-rw-r--r--net/ipv6/xfrm6_mode_beet.c2
-rw-r--r--net/ipv6/xfrm6_mode_tunnel.c6
-rw-r--r--net/ipv6/xfrm6_policy.c2
-rw-r--r--net/ipv6/xfrm6_tunnel.c10
-rw-r--r--net/irda/ircomm/ircomm_core.c6
-rw-r--r--net/irda/ircomm/ircomm_lmp.c5
-rw-r--r--net/irda/ircomm/ircomm_tty.c14
-rw-r--r--net/irda/irlan/irlan_filter.c4
-rw-r--r--net/irda/irlan/irlan_provider.c3
-rw-r--r--net/irda/irlap_event.c3
-rw-r--r--net/irda/irproc.c5
-rw-r--r--net/iucv/af_iucv.c9
-rw-r--r--net/iucv/iucv.c77
-rw-r--r--net/key/af_key.c2
-rw-r--r--net/l2tp/l2tp_core.c28
-rw-r--r--net/l2tp/l2tp_ip.c52
-rw-r--r--net/l2tp/l2tp_netlink.c3
-rw-r--r--net/mac80211/Kconfig1
-rw-r--r--net/mac80211/aes_ccm.c6
-rw-r--r--net/mac80211/agg-rx.c3
-rw-r--r--net/mac80211/agg-tx.c59
-rw-r--r--net/mac80211/cfg.c188
-rw-r--r--net/mac80211/debugfs.c91
-rw-r--r--net/mac80211/debugfs_key.c21
-rw-r--r--net/mac80211/debugfs_sta.c26
-rw-r--r--net/mac80211/driver-ops.h87
-rw-r--r--net/mac80211/driver-trace.h275
-rw-r--r--net/mac80211/ht.c27
-rw-r--r--net/mac80211/ibss.c21
-rw-r--r--net/mac80211/ieee80211_i.h51
-rw-r--r--net/mac80211/iface.c7
-rw-r--r--net/mac80211/key.c51
-rw-r--r--net/mac80211/key.h4
-rw-r--r--net/mac80211/main.c69
-rw-r--r--net/mac80211/mesh.c62
-rw-r--r--net/mac80211/mesh.h9
-rw-r--r--net/mac80211/mesh_hwmp.c42
-rw-r--r--net/mac80211/mesh_pathtbl.c166
-rw-r--r--net/mac80211/mesh_plink.c112
-rw-r--r--net/mac80211/mlme.c48
-rw-r--r--net/mac80211/pm.c29
-rw-r--r--net/mac80211/rc80211_minstrel.c4
-rw-r--r--net/mac80211/rc80211_minstrel_ht.c27
-rw-r--r--net/mac80211/rx.c94
-rw-r--r--net/mac80211/scan.c122
-rw-r--r--net/mac80211/sta_info.c52
-rw-r--r--net/mac80211/sta_info.h56
-rw-r--r--net/mac80211/status.c19
-rw-r--r--net/mac80211/tkip.c4
-rw-r--r--net/mac80211/tkip.h4
-rw-r--r--net/mac80211/tx.c27
-rw-r--r--net/mac80211/util.c21
-rw-r--r--net/mac80211/wep.c34
-rw-r--r--net/mac80211/wep.h4
-rw-r--r--net/mac80211/work.c6
-rw-r--r--net/mac80211/wpa.c62
-rw-r--r--net/netfilter/ipset/ip_set_getport.c16
-rw-r--r--net/netfilter/ipset/ip_set_hash_ipport.c2
-rw-r--r--net/netfilter/ipset/ip_set_hash_ipportip.c2
-rw-r--r--net/netfilter/ipset/ip_set_hash_ipportnet.c2
-rw-r--r--net/netfilter/ipset/ip_set_hash_netport.c2
-rw-r--r--net/netfilter/ipvs/ip_vs_core.c24
-rw-r--r--net/netfilter/ipvs/ip_vs_ctl.c3
-rw-r--r--net/netfilter/ipvs/ip_vs_xmit.c97
-rw-r--r--net/netfilter/nf_conntrack_sip.c16
-rw-r--r--net/netfilter/nf_conntrack_standalone.c2
-rw-r--r--net/netfilter/nfnetlink_log.c2
-rw-r--r--net/netfilter/x_tables.c9
-rw-r--r--net/netlabel/netlabel_cipso_v4.c4
-rw-r--r--net/netrom/af_netrom.c12
-rw-r--r--net/packet/af_packet.c2
-rw-r--r--net/phonet/pn_dev.c6
-rw-r--r--net/phonet/pn_netlink.c4
-rw-r--r--net/phonet/socket.c45
-rw-r--r--net/rfkill/Kconfig11
-rw-r--r--net/rfkill/Makefile1
-rw-r--r--net/rfkill/core.c2
-rw-r--r--net/rfkill/rfkill-regulator.c164
-rw-r--r--net/rose/af_rose.c16
-rw-r--r--net/rxrpc/ar-ack.c2
-rw-r--r--net/rxrpc/ar-connevent.c3
-rw-r--r--net/rxrpc/ar-error.c5
-rw-r--r--net/rxrpc/ar-peer.c6
-rw-r--r--net/rxrpc/ar-transport.c3
-rw-r--r--net/sched/Kconfig12
-rw-r--r--net/sched/Makefile1
-rw-r--r--net/sched/sch_api.c2
-rw-r--r--net/sched/sch_qfq.c1137
-rw-r--r--net/sched/sch_sfq.c2
-rw-r--r--net/sctp/bind_addr.c10
-rw-r--r--net/sctp/debug.c1
-rw-r--r--net/sctp/endpointola.c20
-rw-r--r--net/sctp/input.c19
-rw-r--r--net/sctp/ipv6.c185
-rw-r--r--net/sctp/outqueue.c19
-rw-r--r--net/sctp/protocol.c71
-rw-r--r--net/sctp/sm_make_chunk.c62
-rw-r--r--net/sctp/sm_sideeffect.c9
-rw-r--r--net/sctp/sm_statefuns.c89
-rw-r--r--net/sctp/sm_statetable.c78
-rw-r--r--net/sctp/socket.c95
-rw-r--r--net/sctp/transport.c27
-rw-r--r--net/sctp/ulpevent.c30
-rw-r--r--net/socket.c219
-rw-r--r--net/tipc/addr.h7
-rw-r--r--net/tipc/bcast.c22
-rw-r--r--net/tipc/bearer.c45
-rw-r--r--net/tipc/core.c3
-rw-r--r--net/tipc/discover.c150
-rw-r--r--net/tipc/discover.h11
-rw-r--r--net/tipc/link.c104
-rw-r--r--net/tipc/link.h1
-rw-r--r--net/tipc/msg.c25
-rw-r--r--net/tipc/msg.h161
-rw-r--r--net/tipc/port.c55
-rw-r--r--net/tipc/port.h14
-rw-r--r--net/tipc/socket.c27
-rw-r--r--net/tipc/subscr.c4
-rw-r--r--net/wireless/core.c106
-rw-r--r--net/wireless/core.h33
-rw-r--r--net/wireless/lib80211_crypt_wep.c3
-rw-r--r--net/wireless/mesh.c23
-rw-r--r--net/wireless/mlme.c19
-rw-r--r--net/wireless/nl80211.c790
-rw-r--r--net/wireless/nl80211.h11
-rw-r--r--net/wireless/reg.c74
-rw-r--r--net/wireless/scan.c77
-rw-r--r--net/wireless/sysfs.c2
-rw-r--r--net/wireless/util.c126
-rw-r--r--net/xfrm/xfrm_policy.c3
-rw-r--r--net/xfrm/xfrm_state.c12
309 files changed, 13444 insertions, 7259 deletions
diff --git a/net/802/garp.c b/net/802/garp.c
index c1df2dad8c6..f8300a8b5fb 100644
--- a/net/802/garp.c
+++ b/net/802/garp.c
@@ -544,6 +544,11 @@ static int garp_init_port(struct net_device *dev)
return 0;
}
+static void garp_kfree_rcu(struct rcu_head *head)
+{
+ kfree(container_of(head, struct garp_port, rcu));
+}
+
static void garp_release_port(struct net_device *dev)
{
struct garp_port *port = rtnl_dereference(dev->garp_port);
@@ -554,8 +559,7 @@ static void garp_release_port(struct net_device *dev)
return;
}
rcu_assign_pointer(dev->garp_port, NULL);
- synchronize_rcu();
- kfree(port);
+ call_rcu(&port->rcu, garp_kfree_rcu);
}
int garp_init_applicant(struct net_device *dev, struct garp_application *appl)
@@ -599,6 +603,11 @@ err1:
}
EXPORT_SYMBOL_GPL(garp_init_applicant);
+static void garp_app_kfree_rcu(struct rcu_head *head)
+{
+ kfree(container_of(head, struct garp_applicant, rcu));
+}
+
void garp_uninit_applicant(struct net_device *dev, struct garp_application *appl)
{
struct garp_port *port = rtnl_dereference(dev->garp_port);
@@ -607,7 +616,6 @@ void garp_uninit_applicant(struct net_device *dev, struct garp_application *appl
ASSERT_RTNL();
rcu_assign_pointer(port->applicants[appl->type], NULL);
- synchronize_rcu();
/* Delete timer and generate a final TRANSMIT_PDU event to flush out
* all pending messages before the applicant is gone. */
@@ -617,7 +625,7 @@ void garp_uninit_applicant(struct net_device *dev, struct garp_application *appl
garp_queue_xmit(app);
dev_mc_del(dev, appl->proto.group_address);
- kfree(app);
+ call_rcu(&app->rcu, garp_app_kfree_rcu);
garp_release_port(dev);
}
EXPORT_SYMBOL_GPL(garp_uninit_applicant);
@@ -635,3 +643,9 @@ void garp_unregister_application(struct garp_application *appl)
stp_proto_unregister(&appl->proto);
}
EXPORT_SYMBOL_GPL(garp_unregister_application);
+
+static void __exit garp_cleanup_module(void)
+{
+ rcu_barrier(); /* Wait for completion of call_rcu()'s */
+}
+module_exit(garp_cleanup_module);
diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c
index 0eb1a886b37..b2274d1fd60 100644
--- a/net/8021q/vlan.c
+++ b/net/8021q/vlan.c
@@ -49,11 +49,6 @@ const char vlan_version[] = DRV_VERSION;
static const char vlan_copyright[] = "Ben Greear <greearb@candelatech.com>";
static const char vlan_buggyright[] = "David S. Miller <davem@redhat.com>";
-static struct packet_type vlan_packet_type __read_mostly = {
- .type = cpu_to_be16(ETH_P_8021Q),
- .func = vlan_skb_recv, /* VLAN receive method */
-};
-
/* End of global variables definitions. */
static void vlan_group_free(struct vlan_group *grp)
@@ -128,9 +123,10 @@ void unregister_vlan_dev(struct net_device *dev, struct list_head *head)
vlan_gvrp_request_leave(dev);
vlan_group_set_device(grp, vlan_id, NULL);
- if (!grp->killall)
- synchronize_net();
-
+ /* Because unregister_netdevice_queue() makes sure at least one rcu
+ * grace period is respected before device freeing,
+ * we dont need to call synchronize_net() here.
+ */
unregister_netdevice_queue(dev, head);
/* If the group is now empty, kill off the group. */
@@ -330,10 +326,6 @@ static void vlan_sync_address(struct net_device *dev,
static void vlan_transfer_features(struct net_device *dev,
struct net_device *vlandev)
{
- u32 old_features = vlandev->features;
-
- vlandev->features &= ~dev->vlan_features;
- vlandev->features |= dev->features & dev->vlan_features;
vlandev->gso_max_size = dev->gso_max_size;
if (dev->features & NETIF_F_HW_VLAN_TX)
@@ -344,8 +336,8 @@ static void vlan_transfer_features(struct net_device *dev,
#if defined(CONFIG_FCOE) || defined(CONFIG_FCOE_MODULE)
vlandev->fcoe_ddp_xid = dev->fcoe_ddp_xid;
#endif
- if (old_features != vlandev->features)
- netdev_features_change(vlandev);
+
+ netdev_update_features(vlandev);
}
static void __vlan_device_event(struct net_device *dev, unsigned long event)
@@ -490,9 +482,6 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event,
if (dev->reg_state != NETREG_UNREGISTERING)
break;
- /* Delete all VLANs for this dev. */
- grp->killall = 1;
-
for (i = 0; i < VLAN_N_VID; i++) {
vlandev = vlan_group_get_device(grp, i);
if (!vlandev)
@@ -511,6 +500,18 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event,
case NETDEV_PRE_TYPE_CHANGE:
/* Forbid underlaying device to change its type. */
return NOTIFY_BAD;
+
+ case NETDEV_NOTIFY_PEERS:
+ case NETDEV_BONDING_FAILOVER:
+ /* Propagate to vlan devices */
+ for (i = 0; i < VLAN_N_VID; i++) {
+ vlandev = vlan_group_get_device(grp, i);
+ if (!vlandev)
+ continue;
+
+ call_netdevice_notifiers(event, vlandev);
+ }
+ break;
}
out:
@@ -691,7 +692,6 @@ static int __init vlan_proto_init(void)
if (err < 0)
goto err4;
- dev_add_pack(&vlan_packet_type);
vlan_ioctl_set(vlan_ioctl_handler);
return 0;
@@ -712,8 +712,6 @@ static void __exit vlan_cleanup_module(void)
unregister_netdevice_notifier(&vlan_notifier_block);
- dev_remove_pack(&vlan_packet_type);
-
unregister_pernet_subsys(&vlan_net_ops);
rcu_barrier(); /* Wait for completion of call_rcu()'s */
diff --git a/net/8021q/vlan.h b/net/8021q/vlan.h
index 5687c9b95f3..c3408def8a1 100644
--- a/net/8021q/vlan.h
+++ b/net/8021q/vlan.h
@@ -75,8 +75,6 @@ static inline struct vlan_dev_info *vlan_dev_info(const struct net_device *dev)
}
/* found in vlan_dev.c */
-int vlan_skb_recv(struct sk_buff *skb, struct net_device *dev,
- struct packet_type *ptype, struct net_device *orig_dev);
void vlan_dev_set_ingress_priority(const struct net_device *dev,
u32 skb_prio, u16 vlan_prio);
int vlan_dev_set_egress_priority(const struct net_device *dev,
diff --git a/net/8021q/vlan_core.c b/net/8021q/vlan_core.c
index ce8e3ab3e7a..41495dc2a4c 100644
--- a/net/8021q/vlan_core.c
+++ b/net/8021q/vlan_core.c
@@ -4,7 +4,7 @@
#include <linux/netpoll.h>
#include "vlan.h"
-bool vlan_hwaccel_do_receive(struct sk_buff **skbp)
+bool vlan_do_receive(struct sk_buff **skbp)
{
struct sk_buff *skb = *skbp;
u16 vlan_id = skb->vlan_tci & VLAN_VID_MASK;
@@ -88,3 +88,86 @@ gro_result_t vlan_gro_frags(struct napi_struct *napi, struct vlan_group *grp,
return napi_gro_frags(napi);
}
EXPORT_SYMBOL(vlan_gro_frags);
+
+static struct sk_buff *vlan_check_reorder_header(struct sk_buff *skb)
+{
+ if (vlan_dev_info(skb->dev)->flags & VLAN_FLAG_REORDER_HDR) {
+ if (skb_cow(skb, skb_headroom(skb)) < 0)
+ skb = NULL;
+ if (skb) {
+ /* Lifted from Gleb's VLAN code... */
+ memmove(skb->data - ETH_HLEN,
+ skb->data - VLAN_ETH_HLEN, 12);
+ skb->mac_header += VLAN_HLEN;
+ }
+ }
+ return skb;
+}
+
+static void vlan_set_encap_proto(struct sk_buff *skb, struct vlan_hdr *vhdr)
+{
+ __be16 proto;
+ unsigned char *rawp;
+
+ /*
+ * Was a VLAN packet, grab the encapsulated protocol, which the layer
+ * three protocols care about.
+ */
+
+ proto = vhdr->h_vlan_encapsulated_proto;
+ if (ntohs(proto) >= 1536) {
+ skb->protocol = proto;
+ return;
+ }
+
+ rawp = skb->data;
+ if (*(unsigned short *) rawp == 0xFFFF)
+ /*
+ * This is a magic hack to spot IPX packets. Older Novell
+ * breaks the protocol design and runs IPX over 802.3 without
+ * an 802.2 LLC layer. We look for FFFF which isn't a used
+ * 802.2 SSAP/DSAP. This won't work for fault tolerant netware
+ * but does for the rest.
+ */
+ skb->protocol = htons(ETH_P_802_3);
+ else
+ /*
+ * Real 802.2 LLC
+ */
+ skb->protocol = htons(ETH_P_802_2);
+}
+
+struct sk_buff *vlan_untag(struct sk_buff *skb)
+{
+ struct vlan_hdr *vhdr;
+ u16 vlan_tci;
+
+ if (unlikely(vlan_tx_tag_present(skb))) {
+ /* vlan_tci is already set-up so leave this for another time */
+ return skb;
+ }
+
+ skb = skb_share_check(skb, GFP_ATOMIC);
+ if (unlikely(!skb))
+ goto err_free;
+
+ if (unlikely(!pskb_may_pull(skb, VLAN_HLEN)))
+ goto err_free;
+
+ vhdr = (struct vlan_hdr *) skb->data;
+ vlan_tci = ntohs(vhdr->h_vlan_TCI);
+ __vlan_hwaccel_put_tag(skb, vlan_tci);
+
+ skb_pull_rcsum(skb, VLAN_HLEN);
+ vlan_set_encap_proto(skb, vhdr);
+
+ skb = vlan_check_reorder_header(skb);
+ if (unlikely(!skb))
+ goto err_free;
+
+ return skb;
+
+err_free:
+ kfree_skb(skb);
+ return NULL;
+}
diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c
index b2ff6c8d360..f247f5bff88 100644
--- a/net/8021q/vlan_dev.c
+++ b/net/8021q/vlan_dev.c
@@ -65,179 +65,6 @@ static int vlan_dev_rebuild_header(struct sk_buff *skb)
return 0;
}
-static inline struct sk_buff *vlan_check_reorder_header(struct sk_buff *skb)
-{
- if (vlan_dev_info(skb->dev)->flags & VLAN_FLAG_REORDER_HDR) {
- if (skb_cow(skb, skb_headroom(skb)) < 0)
- skb = NULL;
- if (skb) {
- /* Lifted from Gleb's VLAN code... */
- memmove(skb->data - ETH_HLEN,
- skb->data - VLAN_ETH_HLEN, 12);
- skb->mac_header += VLAN_HLEN;
- }
- }
-
- return skb;
-}
-
-static inline void vlan_set_encap_proto(struct sk_buff *skb,
- struct vlan_hdr *vhdr)
-{
- __be16 proto;
- unsigned char *rawp;
-
- /*
- * Was a VLAN packet, grab the encapsulated protocol, which the layer
- * three protocols care about.
- */
-
- proto = vhdr->h_vlan_encapsulated_proto;
- if (ntohs(proto) >= 1536) {
- skb->protocol = proto;
- return;
- }
-
- rawp = skb->data;
- if (*(unsigned short *)rawp == 0xFFFF)
- /*
- * This is a magic hack to spot IPX packets. Older Novell
- * breaks the protocol design and runs IPX over 802.3 without
- * an 802.2 LLC layer. We look for FFFF which isn't a used
- * 802.2 SSAP/DSAP. This won't work for fault tolerant netware
- * but does for the rest.
- */
- skb->protocol = htons(ETH_P_802_3);
- else
- /*
- * Real 802.2 LLC
- */
- skb->protocol = htons(ETH_P_802_2);
-}
-
-/*
- * Determine the packet's protocol ID. The rule here is that we
- * assume 802.3 if the type field is short enough to be a length.
- * This is normal practice and works for any 'now in use' protocol.
- *
- * Also, at this point we assume that we ARE dealing exclusively with
- * VLAN packets, or packets that should be made into VLAN packets based
- * on a default VLAN ID.
- *
- * NOTE: Should be similar to ethernet/eth.c.
- *
- * SANITY NOTE: This method is called when a packet is moving up the stack
- * towards userland. To get here, it would have already passed
- * through the ethernet/eth.c eth_type_trans() method.
- * SANITY NOTE 2: We are referencing to the VLAN_HDR frields, which MAY be
- * stored UNALIGNED in the memory. RISC systems don't like
- * such cases very much...
- * SANITY NOTE 2a: According to Dave Miller & Alexey, it will always be
- * aligned, so there doesn't need to be any of the unaligned
- * stuff. It has been commented out now... --Ben
- *
- */
-int vlan_skb_recv(struct sk_buff *skb, struct net_device *dev,
- struct packet_type *ptype, struct net_device *orig_dev)
-{
- struct vlan_hdr *vhdr;
- struct vlan_pcpu_stats *rx_stats;
- struct net_device *vlan_dev;
- u16 vlan_id;
- u16 vlan_tci;
-
- skb = skb_share_check(skb, GFP_ATOMIC);
- if (skb == NULL)
- goto err_free;
-
- if (unlikely(!pskb_may_pull(skb, VLAN_HLEN)))
- goto err_free;
-
- vhdr = (struct vlan_hdr *)skb->data;
- vlan_tci = ntohs(vhdr->h_vlan_TCI);
- vlan_id = vlan_tci & VLAN_VID_MASK;
-
- rcu_read_lock();
- vlan_dev = vlan_find_dev(dev, vlan_id);
-
- /* If the VLAN device is defined, we use it.
- * If not, and the VID is 0, it is a 802.1p packet (not
- * really a VLAN), so we will just netif_rx it later to the
- * original interface, but with the skb->proto set to the
- * wrapped proto: we do nothing here.
- */
-
- if (!vlan_dev) {
- if (vlan_id) {
- pr_debug("%s: ERROR: No net_device for VID: %u on dev: %s\n",
- __func__, vlan_id, dev->name);
- goto err_unlock;
- }
- rx_stats = NULL;
- } else {
- skb->dev = vlan_dev;
-
- rx_stats = this_cpu_ptr(vlan_dev_info(skb->dev)->vlan_pcpu_stats);
-
- u64_stats_update_begin(&rx_stats->syncp);
- rx_stats->rx_packets++;
- rx_stats->rx_bytes += skb->len;
-
- skb->priority = vlan_get_ingress_priority(skb->dev, vlan_tci);
-
- pr_debug("%s: priority: %u for TCI: %hu\n",
- __func__, skb->priority, vlan_tci);
-
- switch (skb->pkt_type) {
- case PACKET_BROADCAST:
- /* Yeah, stats collect these together.. */
- /* stats->broadcast ++; // no such counter :-( */
- break;
-
- case PACKET_MULTICAST:
- rx_stats->rx_multicast++;
- break;
-
- case PACKET_OTHERHOST:
- /* Our lower layer thinks this is not local, let's make
- * sure.
- * This allows the VLAN to have a different MAC than the
- * underlying device, and still route correctly.
- */
- if (!compare_ether_addr(eth_hdr(skb)->h_dest,
- skb->dev->dev_addr))
- skb->pkt_type = PACKET_HOST;
- break;
- default:
- break;
- }
- u64_stats_update_end(&rx_stats->syncp);
- }
-
- skb_pull_rcsum(skb, VLAN_HLEN);
- vlan_set_encap_proto(skb, vhdr);
-
- if (vlan_dev) {
- skb = vlan_check_reorder_header(skb);
- if (!skb) {
- rx_stats->rx_errors++;
- goto err_unlock;
- }
- }
-
- netif_rx(skb);
-
- rcu_read_unlock();
- return NET_RX_SUCCESS;
-
-err_unlock:
- rcu_read_unlock();
-err_free:
- atomic_long_inc(&dev->rx_dropped);
- kfree_skb(skb);
- return NET_RX_DROP;
-}
-
static inline u16
vlan_dev_get_egress_qos_mask(struct net_device *dev, struct sk_buff *skb)
{
@@ -701,8 +528,8 @@ static int vlan_dev_init(struct net_device *dev)
(1<<__LINK_STATE_DORMANT))) |
(1<<__LINK_STATE_PRESENT);
- dev->features |= real_dev->features & real_dev->vlan_features;
- dev->features |= NETIF_F_LLTX;
+ dev->hw_features = NETIF_F_ALL_TX_OFFLOADS;
+ dev->features |= real_dev->vlan_features | NETIF_F_LLTX;
dev->gso_max_size = real_dev->gso_max_size;
/* ipv6 shared card related stuff */
@@ -756,6 +583,19 @@ static void vlan_dev_uninit(struct net_device *dev)
}
}
+static u32 vlan_dev_fix_features(struct net_device *dev, u32 features)
+{
+ struct net_device *real_dev = vlan_dev_info(dev)->real_dev;
+
+ features &= real_dev->features;
+ features &= real_dev->vlan_features;
+ if (dev_ethtool_get_rx_csum(real_dev))
+ features |= NETIF_F_RXCSUM;
+ features |= NETIF_F_LLTX;
+
+ return features;
+}
+
static int vlan_ethtool_get_settings(struct net_device *dev,
struct ethtool_cmd *cmd)
{
@@ -771,18 +611,6 @@ static void vlan_ethtool_get_drvinfo(struct net_device *dev,
strcpy(info->fw_version, "N/A");
}
-static u32 vlan_ethtool_get_rx_csum(struct net_device *dev)
-{
- const struct vlan_dev_info *vlan = vlan_dev_info(dev);
- return dev_ethtool_get_rx_csum(vlan->real_dev);
-}
-
-static u32 vlan_ethtool_get_flags(struct net_device *dev)
-{
- const struct vlan_dev_info *vlan = vlan_dev_info(dev);
- return dev_ethtool_get_flags(vlan->real_dev);
-}
-
static struct rtnl_link_stats64 *vlan_dev_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
{
@@ -820,32 +648,10 @@ static struct rtnl_link_stats64 *vlan_dev_get_stats64(struct net_device *dev, st
return stats;
}
-static int vlan_ethtool_set_tso(struct net_device *dev, u32 data)
-{
- if (data) {
- struct net_device *real_dev = vlan_dev_info(dev)->real_dev;
-
- /* Underlying device must support TSO for VLAN-tagged packets
- * and must have TSO enabled now.
- */
- if (!(real_dev->vlan_features & NETIF_F_TSO))
- return -EOPNOTSUPP;
- if (!(real_dev->features & NETIF_F_TSO))
- return -EINVAL;
- dev->features |= NETIF_F_TSO;
- } else {
- dev->features &= ~NETIF_F_TSO;
- }
- return 0;
-}
-
static const struct ethtool_ops vlan_ethtool_ops = {
.get_settings = vlan_ethtool_get_settings,
.get_drvinfo = vlan_ethtool_get_drvinfo,
.get_link = ethtool_op_get_link,
- .get_rx_csum = vlan_ethtool_get_rx_csum,
- .get_flags = vlan_ethtool_get_flags,
- .set_tso = vlan_ethtool_set_tso,
};
static const struct net_device_ops vlan_netdev_ops = {
@@ -871,6 +677,7 @@ static const struct net_device_ops vlan_netdev_ops = {
.ndo_fcoe_get_wwn = vlan_dev_fcoe_get_wwn,
.ndo_fcoe_ddp_target = vlan_dev_fcoe_ddp_target,
#endif
+ .ndo_fix_features = vlan_dev_fix_features,
};
void vlan_setup(struct net_device *dev)
diff --git a/net/9p/client.c b/net/9p/client.c
index a9aa2dd6648..ceab943dfc4 100644
--- a/net/9p/client.c
+++ b/net/9p/client.c
@@ -1281,7 +1281,7 @@ int
p9_client_read(struct p9_fid *fid, char *data, char __user *udata, u64 offset,
u32 count)
{
- int err, rsize, total;
+ int err, rsize;
struct p9_client *clnt;
struct p9_req_t *req;
char *dataptr;
@@ -1290,7 +1290,6 @@ p9_client_read(struct p9_fid *fid, char *data, char __user *udata, u64 offset,
(long long unsigned) offset, count);
err = 0;
clnt = fid->clnt;
- total = 0;
rsize = fid->iounit;
if (!rsize || rsize > clnt->msize-P9_IOHDRSZ)
@@ -1346,7 +1345,7 @@ int
p9_client_write(struct p9_fid *fid, char *data, const char __user *udata,
u64 offset, u32 count)
{
- int err, rsize, total;
+ int err, rsize;
struct p9_client *clnt;
struct p9_req_t *req;
@@ -1354,7 +1353,6 @@ p9_client_write(struct p9_fid *fid, char *data, const char __user *udata,
fid->fid, (long long unsigned) offset, count);
err = 0;
clnt = fid->clnt;
- total = 0;
rsize = fid->iounit;
if (!rsize || rsize > clnt->msize-P9_IOHDRSZ)
@@ -1745,7 +1743,7 @@ EXPORT_SYMBOL_GPL(p9_client_xattrcreate);
int p9_client_readdir(struct p9_fid *fid, char *data, u32 count, u64 offset)
{
- int err, rsize, total;
+ int err, rsize;
struct p9_client *clnt;
struct p9_req_t *req;
char *dataptr;
@@ -1755,7 +1753,6 @@ int p9_client_readdir(struct p9_fid *fid, char *data, u32 count, u64 offset)
err = 0;
clnt = fid->clnt;
- total = 0;
rsize = fid->iounit;
if (!rsize || rsize > clnt->msize-P9_READDIRHDRSZ)
diff --git a/net/9p/trans_fd.c b/net/9p/trans_fd.c
index aa5672b15ea..4a9084395d3 100644
--- a/net/9p/trans_fd.c
+++ b/net/9p/trans_fd.c
@@ -716,7 +716,6 @@ static int parse_opts(char *params, struct p9_fd_opts *opts)
substring_t args[MAX_OPT_ARGS];
int option;
char *options, *tmp_options;
- int ret;
opts->port = P9_PORT;
opts->rfd = ~0;
@@ -744,7 +743,6 @@ static int parse_opts(char *params, struct p9_fd_opts *opts)
if (r < 0) {
P9_DPRINTK(P9_DEBUG_ERROR,
"integer field, but no integer?\n");
- ret = r;
continue;
}
}
diff --git a/net/9p/trans_rdma.c b/net/9p/trans_rdma.c
index 150e0c4bbf4..844a7a5607e 100644
--- a/net/9p/trans_rdma.c
+++ b/net/9p/trans_rdma.c
@@ -167,7 +167,6 @@ static int parse_opts(char *params, struct p9_rdma_opts *opts)
substring_t args[MAX_OPT_ARGS];
int option;
char *options, *tmp_options;
- int ret;
opts->port = P9_PORT;
opts->sq_depth = P9_RDMA_SQ_DEPTH;
@@ -195,7 +194,6 @@ static int parse_opts(char *params, struct p9_rdma_opts *opts)
if (r < 0) {
P9_DPRINTK(P9_DEBUG_ERROR,
"integer field, but no integer?\n");
- ret = r;
continue;
}
switch (token) {
diff --git a/net/Kconfig b/net/Kconfig
index 79cabf1ee68..878151c772c 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -232,6 +232,20 @@ config XPS
depends on SMP && SYSFS && USE_GENERIC_SMP_HELPERS
default y
+config HAVE_BPF_JIT
+ bool
+
+config BPF_JIT
+ bool "enable BPF Just In Time compiler"
+ depends on HAVE_BPF_JIT
+ depends on MODULES
+ ---help---
+ Berkeley Packet Filter filtering capabilities are normally handled
+ by an interpreter. This option allows kernel to generate a native
+ code when filter is loaded in memory. This should speedup
+ packet sniffing (libpcap/tcpdump). Note : Admin should enable
+ this feature changing /proc/sys/net/core/bpf_jit_enable
+
menu "Network testing"
config NET_PKTGEN
diff --git a/net/atm/lec.c b/net/atm/lec.c
index 38754fdb88b..25073b6ef47 100644
--- a/net/atm/lec.c
+++ b/net/atm/lec.c
@@ -129,7 +129,6 @@ static struct net_device *dev_lec[MAX_LEC_ITF];
#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
static void lec_handle_bridge(struct sk_buff *skb, struct net_device *dev)
{
- struct ethhdr *eth;
char *buff;
struct lec_priv *priv;
@@ -138,7 +137,6 @@ static void lec_handle_bridge(struct sk_buff *skb, struct net_device *dev)
* LE_TOPOLOGY_REQUEST with the same value of Topology Change bit
* as the Config BPDU has
*/
- eth = (struct ethhdr *)skb->data;
buff = skb->data + skb->dev->hard_header_len;
if (*buff++ == 0x42 && *buff++ == 0x42 && *buff++ == 0x03) {
struct sock *sk;
@@ -1180,7 +1178,6 @@ static int __init lane_module_init(void)
static void __exit lane_module_cleanup(void)
{
int i;
- struct lec_priv *priv;
remove_proc_entry("lec", atm_proc_root);
@@ -1188,7 +1185,6 @@ static void __exit lane_module_cleanup(void)
for (i = 0; i < MAX_LEC_ITF; i++) {
if (dev_lec[i] != NULL) {
- priv = netdev_priv(dev_lec[i]);
unregister_netdev(dev_lec[i]);
free_netdev(dev_lec[i]);
dev_lec[i] = NULL;
diff --git a/net/ax25/af_ax25.c b/net/ax25/af_ax25.c
index 6da5daeebab..e7c69f4619e 100644
--- a/net/ax25/af_ax25.c
+++ b/net/ax25/af_ax25.c
@@ -1538,8 +1538,6 @@ static int ax25_sendmsg(struct kiocb *iocb, struct socket *sock,
}
/* Build a packet */
- SOCK_DEBUG(sk, "AX.25: sendto: Addresses built. Building packet.\n");
-
/* Assume the worst case */
size = len + ax25->ax25_dev->dev->hard_header_len;
@@ -1549,8 +1547,6 @@ static int ax25_sendmsg(struct kiocb *iocb, struct socket *sock,
skb_reserve(skb, size - len);
- SOCK_DEBUG(sk, "AX.25: Appending user data\n");
-
/* User data follows immediately after the AX.25 data */
if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) {
err = -EFAULT;
@@ -1564,8 +1560,6 @@ static int ax25_sendmsg(struct kiocb *iocb, struct socket *sock,
if (!ax25->pidincl)
*skb_push(skb, 1) = sk->sk_protocol;
- SOCK_DEBUG(sk, "AX.25: Transmitting buffer\n");
-
if (sk->sk_type == SOCK_SEQPACKET) {
/* Connected mode sockets go via the LAPB machine */
if (sk->sk_state != TCP_ESTABLISHED) {
@@ -1583,22 +1577,14 @@ static int ax25_sendmsg(struct kiocb *iocb, struct socket *sock,
skb_push(skb, 1 + ax25_addr_size(dp));
- SOCK_DEBUG(sk, "Building AX.25 Header (dp=%p).\n", dp);
-
- if (dp != NULL)
- SOCK_DEBUG(sk, "Num digipeaters=%d\n", dp->ndigi);
+ /* Building AX.25 Header */
/* Build an AX.25 header */
lv = ax25_addr_build(skb->data, &ax25->source_addr, &sax.sax25_call,
dp, AX25_COMMAND, AX25_MODULUS);
- SOCK_DEBUG(sk, "Built header (%d bytes)\n",lv);
-
skb_set_transport_header(skb, lv);
- SOCK_DEBUG(sk, "base=%p pos=%p\n",
- skb->data, skb_transport_header(skb));
-
*skb_transport_header(skb) = AX25_UI;
/* Datagram frames go straight out of the door as UI */
diff --git a/net/ax25/ax25_iface.c b/net/ax25/ax25_iface.c
index 5a0dda8df49..60b545e2822 100644
--- a/net/ax25/ax25_iface.c
+++ b/net/ax25/ax25_iface.c
@@ -58,7 +58,7 @@ EXPORT_SYMBOL_GPL(ax25_register_pid);
void ax25_protocol_release(unsigned int pid)
{
- struct ax25_protocol *s, *protocol;
+ struct ax25_protocol *protocol;
write_lock_bh(&protocol_list_lock);
protocol = protocol_list;
@@ -72,7 +72,6 @@ void ax25_protocol_release(unsigned int pid)
while (protocol != NULL && protocol->next != NULL) {
if (protocol->next->pid == pid) {
- s = protocol->next;
protocol->next = protocol->next->next;
goto out;
}
diff --git a/net/batman-adv/aggregation.c b/net/batman-adv/aggregation.c
index af45d6b2031..a8c32030527 100644
--- a/net/batman-adv/aggregation.c
+++ b/net/batman-adv/aggregation.c
@@ -23,11 +23,12 @@
#include "aggregation.h"
#include "send.h"
#include "routing.h"
+#include "hard-interface.h"
-/* calculate the size of the hna information for a given packet */
-static int hna_len(struct batman_packet *batman_packet)
+/* calculate the size of the tt information for a given packet */
+static int tt_len(struct batman_packet *batman_packet)
{
- return batman_packet->num_hna * ETH_ALEN;
+ return batman_packet->num_tt * ETH_ALEN;
}
/* return true if new_packet can be aggregated with forw_packet */
@@ -95,7 +96,6 @@ static bool can_aggregate_with(struct batman_packet *new_batman_packet,
return false;
}
-#define atomic_dec_not_zero(v) atomic_add_unless((v), -1, 0)
/* create a new aggregated packet and add this packet to it */
static void new_aggregated_packet(unsigned char *packet_buff, int packet_len,
unsigned long send_time, bool direct_link,
@@ -106,12 +106,15 @@ static void new_aggregated_packet(unsigned char *packet_buff, int packet_len,
struct forw_packet *forw_packet_aggr;
unsigned char *skb_buff;
+ if (!atomic_inc_not_zero(&if_incoming->refcount))
+ return;
+
/* own packet should always be scheduled */
if (!own_packet) {
if (!atomic_dec_not_zero(&bat_priv->batman_queue_left)) {
bat_dbg(DBG_BATMAN, bat_priv,
"batman packet queue full\n");
- return;
+ goto out;
}
}
@@ -119,7 +122,7 @@ static void new_aggregated_packet(unsigned char *packet_buff, int packet_len,
if (!forw_packet_aggr) {
if (!own_packet)
atomic_inc(&bat_priv->batman_queue_left);
- return;
+ goto out;
}
if ((atomic_read(&bat_priv->aggregated_ogms)) &&
@@ -134,7 +137,7 @@ static void new_aggregated_packet(unsigned char *packet_buff, int packet_len,
if (!own_packet)
atomic_inc(&bat_priv->batman_queue_left);
kfree(forw_packet_aggr);
- return;
+ goto out;
}
skb_reserve(forw_packet_aggr->skb, sizeof(struct ethhdr));
@@ -165,6 +168,10 @@ static void new_aggregated_packet(unsigned char *packet_buff, int packet_len,
queue_delayed_work(bat_event_workqueue,
&forw_packet_aggr->delayed_work,
send_time - jiffies);
+
+ return;
+out:
+ hardif_free_ref(if_incoming);
}
/* aggregate a new packet into the existing aggregation */
@@ -251,7 +258,7 @@ void receive_aggr_bat_packet(struct ethhdr *ethhdr, unsigned char *packet_buff,
{
struct batman_packet *batman_packet;
int buff_pos = 0;
- unsigned char *hna_buff;
+ unsigned char *tt_buff;
batman_packet = (struct batman_packet *)packet_buff;
@@ -260,14 +267,14 @@ void receive_aggr_bat_packet(struct ethhdr *ethhdr, unsigned char *packet_buff,
orig_interval. */
batman_packet->seqno = ntohl(batman_packet->seqno);
- hna_buff = packet_buff + buff_pos + BAT_PACKET_LEN;
+ tt_buff = packet_buff + buff_pos + BAT_PACKET_LEN;
receive_bat_packet(ethhdr, batman_packet,
- hna_buff, hna_len(batman_packet),
+ tt_buff, tt_len(batman_packet),
if_incoming);
- buff_pos += BAT_PACKET_LEN + hna_len(batman_packet);
+ buff_pos += BAT_PACKET_LEN + tt_len(batman_packet);
batman_packet = (struct batman_packet *)
(packet_buff + buff_pos);
} while (aggregated_packet(buff_pos, packet_len,
- batman_packet->num_hna));
+ batman_packet->num_tt));
}
diff --git a/net/batman-adv/aggregation.h b/net/batman-adv/aggregation.h
index 062204289d1..7e6d72fbf54 100644
--- a/net/batman-adv/aggregation.h
+++ b/net/batman-adv/aggregation.h
@@ -25,9 +25,9 @@
#include "main.h"
/* is there another aggregated packet here? */
-static inline int aggregated_packet(int buff_pos, int packet_len, int num_hna)
+static inline int aggregated_packet(int buff_pos, int packet_len, int num_tt)
{
- int next_buff_pos = buff_pos + BAT_PACKET_LEN + (num_hna * ETH_ALEN);
+ int next_buff_pos = buff_pos + BAT_PACKET_LEN + (num_tt * ETH_ALEN);
return (next_buff_pos <= packet_len) &&
(next_buff_pos <= MAX_AGGREGATION_BYTES);
diff --git a/net/batman-adv/bat_debugfs.c b/net/batman-adv/bat_debugfs.c
index 0e9d4350993..abaeec5f624 100644
--- a/net/batman-adv/bat_debugfs.c
+++ b/net/batman-adv/bat_debugfs.c
@@ -241,13 +241,13 @@ static int softif_neigh_open(struct inode *inode, struct file *file)
static int transtable_global_open(struct inode *inode, struct file *file)
{
struct net_device *net_dev = (struct net_device *)inode->i_private;
- return single_open(file, hna_global_seq_print_text, net_dev);
+ return single_open(file, tt_global_seq_print_text, net_dev);
}
static int transtable_local_open(struct inode *inode, struct file *file)
{
struct net_device *net_dev = (struct net_device *)inode->i_private;
- return single_open(file, hna_local_seq_print_text, net_dev);
+ return single_open(file, tt_local_seq_print_text, net_dev);
}
static int vis_data_open(struct inode *inode, struct file *file)
diff --git a/net/batman-adv/bat_sysfs.c b/net/batman-adv/bat_sysfs.c
index e449bf6353e..497a0700cc3 100644
--- a/net/batman-adv/bat_sysfs.c
+++ b/net/batman-adv/bat_sysfs.c
@@ -488,22 +488,24 @@ static ssize_t store_mesh_iface(struct kobject *kobj, struct attribute *attr,
(strncmp(hard_iface->soft_iface->name, buff, IFNAMSIZ) == 0))
goto out;
+ if (!rtnl_trylock()) {
+ ret = -ERESTARTSYS;
+ goto out;
+ }
+
if (status_tmp == IF_NOT_IN_USE) {
- rtnl_lock();
hardif_disable_interface(hard_iface);
- rtnl_unlock();
- goto out;
+ goto unlock;
}
/* if the interface already is in use */
- if (hard_iface->if_status != IF_NOT_IN_USE) {
- rtnl_lock();
+ if (hard_iface->if_status != IF_NOT_IN_USE)
hardif_disable_interface(hard_iface);
- rtnl_unlock();
- }
ret = hardif_enable_interface(hard_iface, buff);
+unlock:
+ rtnl_unlock();
out:
hardif_free_ref(hard_iface);
return ret;
diff --git a/net/batman-adv/gateway_client.c b/net/batman-adv/gateway_client.c
index 150b6ce23df..61605a0f3f3 100644
--- a/net/batman-adv/gateway_client.c
+++ b/net/batman-adv/gateway_client.c
@@ -23,6 +23,7 @@
#include "gateway_client.h"
#include "gateway_common.h"
#include "hard-interface.h"
+#include "originator.h"
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/udp.h>
@@ -34,61 +35,76 @@ static void gw_node_free_ref(struct gw_node *gw_node)
kfree_rcu(gw_node, rcu);
}
-void *gw_get_selected(struct bat_priv *bat_priv)
+static struct gw_node *gw_get_selected_gw_node(struct bat_priv *bat_priv)
{
- struct gw_node *curr_gateway_tmp;
- struct orig_node *orig_node = NULL;
+ struct gw_node *gw_node;
rcu_read_lock();
- curr_gateway_tmp = rcu_dereference(bat_priv->curr_gw);
- if (!curr_gateway_tmp)
- goto out;
-
- orig_node = curr_gateway_tmp->orig_node;
- if (!orig_node)
+ gw_node = rcu_dereference(bat_priv->curr_gw);
+ if (!gw_node)
goto out;
- if (!atomic_inc_not_zero(&orig_node->refcount))
- orig_node = NULL;
+ if (!atomic_inc_not_zero(&gw_node->refcount))
+ gw_node = NULL;
out:
rcu_read_unlock();
- return orig_node;
+ return gw_node;
}
-void gw_deselect(struct bat_priv *bat_priv)
+struct orig_node *gw_get_selected_orig(struct bat_priv *bat_priv)
{
struct gw_node *gw_node;
+ struct orig_node *orig_node = NULL;
- spin_lock_bh(&bat_priv->gw_list_lock);
- gw_node = rcu_dereference(bat_priv->curr_gw);
- rcu_assign_pointer(bat_priv->curr_gw, NULL);
- spin_unlock_bh(&bat_priv->gw_list_lock);
+ gw_node = gw_get_selected_gw_node(bat_priv);
+ if (!gw_node)
+ goto out;
+
+ rcu_read_lock();
+ orig_node = gw_node->orig_node;
+ if (!orig_node)
+ goto unlock;
+
+ if (!atomic_inc_not_zero(&orig_node->refcount))
+ orig_node = NULL;
+unlock:
+ rcu_read_unlock();
+out:
if (gw_node)
gw_node_free_ref(gw_node);
+ return orig_node;
}
static void gw_select(struct bat_priv *bat_priv, struct gw_node *new_gw_node)
{
struct gw_node *curr_gw_node;
+ spin_lock_bh(&bat_priv->gw_list_lock);
+
if (new_gw_node && !atomic_inc_not_zero(&new_gw_node->refcount))
new_gw_node = NULL;
- spin_lock_bh(&bat_priv->gw_list_lock);
- curr_gw_node = rcu_dereference(bat_priv->curr_gw);
+ curr_gw_node = bat_priv->curr_gw;
rcu_assign_pointer(bat_priv->curr_gw, new_gw_node);
- spin_unlock_bh(&bat_priv->gw_list_lock);
if (curr_gw_node)
gw_node_free_ref(curr_gw_node);
+
+ spin_unlock_bh(&bat_priv->gw_list_lock);
+}
+
+void gw_deselect(struct bat_priv *bat_priv)
+{
+ gw_select(bat_priv, NULL);
}
void gw_election(struct bat_priv *bat_priv)
{
struct hlist_node *node;
- struct gw_node *gw_node, *curr_gw, *curr_gw_tmp = NULL;
+ struct gw_node *gw_node, *curr_gw = NULL, *curr_gw_tmp = NULL;
+ struct neigh_node *router;
uint8_t max_tq = 0;
uint32_t max_gw_factor = 0, tmp_gw_factor = 0;
int down, up;
@@ -102,32 +118,25 @@ void gw_election(struct bat_priv *bat_priv)
if (atomic_read(&bat_priv->gw_mode) != GW_MODE_CLIENT)
return;
- rcu_read_lock();
- curr_gw = rcu_dereference(bat_priv->curr_gw);
- if (curr_gw) {
- rcu_read_unlock();
- return;
- }
+ curr_gw = gw_get_selected_gw_node(bat_priv);
+ if (curr_gw)
+ goto out;
+ rcu_read_lock();
if (hlist_empty(&bat_priv->gw_list)) {
-
- if (curr_gw) {
- rcu_read_unlock();
- bat_dbg(DBG_BATMAN, bat_priv,
- "Removing selected gateway - "
- "no gateway in range\n");
- gw_deselect(bat_priv);
- } else
- rcu_read_unlock();
-
- return;
+ bat_dbg(DBG_BATMAN, bat_priv,
+ "Removing selected gateway - "
+ "no gateway in range\n");
+ gw_deselect(bat_priv);
+ goto unlock;
}
hlist_for_each_entry_rcu(gw_node, node, &bat_priv->gw_list, list) {
- if (!gw_node->orig_node->router)
+ if (gw_node->deleted)
continue;
- if (gw_node->deleted)
+ router = orig_node_get_router(gw_node->orig_node);
+ if (!router)
continue;
switch (atomic_read(&bat_priv->gw_sel_class)) {
@@ -135,15 +144,14 @@ void gw_election(struct bat_priv *bat_priv)
gw_bandwidth_to_kbit(gw_node->orig_node->gw_flags,
&down, &up);
- tmp_gw_factor = (gw_node->orig_node->router->tq_avg *
- gw_node->orig_node->router->tq_avg *
+ tmp_gw_factor = (router->tq_avg * router->tq_avg *
down * 100 * 100) /
(TQ_LOCAL_WINDOW_SIZE *
TQ_LOCAL_WINDOW_SIZE * 64);
if ((tmp_gw_factor > max_gw_factor) ||
((tmp_gw_factor == max_gw_factor) &&
- (gw_node->orig_node->router->tq_avg > max_tq)))
+ (router->tq_avg > max_tq)))
curr_gw_tmp = gw_node;
break;
@@ -155,19 +163,25 @@ void gw_election(struct bat_priv *bat_priv)
* soon as a better gateway appears which has
* $routing_class more tq points)
**/
- if (gw_node->orig_node->router->tq_avg > max_tq)
+ if (router->tq_avg > max_tq)
curr_gw_tmp = gw_node;
break;
}
- if (gw_node->orig_node->router->tq_avg > max_tq)
- max_tq = gw_node->orig_node->router->tq_avg;
+ if (router->tq_avg > max_tq)
+ max_tq = router->tq_avg;
if (tmp_gw_factor > max_gw_factor)
max_gw_factor = tmp_gw_factor;
+
+ neigh_node_free_ref(router);
}
if (curr_gw != curr_gw_tmp) {
+ router = orig_node_get_router(curr_gw_tmp->orig_node);
+ if (!router)
+ goto unlock;
+
if ((curr_gw) && (!curr_gw_tmp))
bat_dbg(DBG_BATMAN, bat_priv,
"Removing selected gateway - "
@@ -178,48 +192,50 @@ void gw_election(struct bat_priv *bat_priv)
"(gw_flags: %i, tq: %i)\n",
curr_gw_tmp->orig_node->orig,
curr_gw_tmp->orig_node->gw_flags,
- curr_gw_tmp->orig_node->router->tq_avg);
+ router->tq_avg);
else
bat_dbg(DBG_BATMAN, bat_priv,
"Changing route to gateway %pM "
"(gw_flags: %i, tq: %i)\n",
curr_gw_tmp->orig_node->orig,
curr_gw_tmp->orig_node->gw_flags,
- curr_gw_tmp->orig_node->router->tq_avg);
+ router->tq_avg);
+ neigh_node_free_ref(router);
gw_select(bat_priv, curr_gw_tmp);
}
+unlock:
rcu_read_unlock();
+out:
+ if (curr_gw)
+ gw_node_free_ref(curr_gw);
}
void gw_check_election(struct bat_priv *bat_priv, struct orig_node *orig_node)
{
- struct gw_node *curr_gateway_tmp;
+ struct orig_node *curr_gw_orig;
+ struct neigh_node *router_gw = NULL, *router_orig = NULL;
uint8_t gw_tq_avg, orig_tq_avg;
- rcu_read_lock();
- curr_gateway_tmp = rcu_dereference(bat_priv->curr_gw);
- if (!curr_gateway_tmp)
- goto out_rcu;
-
- if (!curr_gateway_tmp->orig_node)
- goto deselect_rcu;
+ curr_gw_orig = gw_get_selected_orig(bat_priv);
+ if (!curr_gw_orig)
+ goto deselect;
- if (!curr_gateway_tmp->orig_node->router)
- goto deselect_rcu;
+ router_gw = orig_node_get_router(curr_gw_orig);
+ if (!router_gw)
+ goto deselect;
/* this node already is the gateway */
- if (curr_gateway_tmp->orig_node == orig_node)
- goto out_rcu;
-
- if (!orig_node->router)
- goto out_rcu;
+ if (curr_gw_orig == orig_node)
+ goto out;
- gw_tq_avg = curr_gateway_tmp->orig_node->router->tq_avg;
- rcu_read_unlock();
+ router_orig = orig_node_get_router(orig_node);
+ if (!router_orig)
+ goto out;
- orig_tq_avg = orig_node->router->tq_avg;
+ gw_tq_avg = router_gw->tq_avg;
+ orig_tq_avg = router_orig->tq_avg;
/* the TQ value has to be better */
if (orig_tq_avg < gw_tq_avg)
@@ -237,16 +253,17 @@ void gw_check_election(struct bat_priv *bat_priv, struct orig_node *orig_node)
"Restarting gateway selection: better gateway found (tq curr: "
"%i, tq new: %i)\n",
gw_tq_avg, orig_tq_avg);
- goto deselect;
-out_rcu:
- rcu_read_unlock();
- goto out;
-deselect_rcu:
- rcu_read_unlock();
deselect:
gw_deselect(bat_priv);
out:
+ if (curr_gw_orig)
+ orig_node_free_ref(curr_gw_orig);
+ if (router_gw)
+ neigh_node_free_ref(router_gw);
+ if (router_orig)
+ neigh_node_free_ref(router_orig);
+
return;
}
@@ -283,7 +300,15 @@ void gw_node_update(struct bat_priv *bat_priv,
struct orig_node *orig_node, uint8_t new_gwflags)
{
struct hlist_node *node;
- struct gw_node *gw_node;
+ struct gw_node *gw_node, *curr_gw;
+
+ /**
+ * Note: We don't need a NULL check here, since curr_gw never gets
+ * dereferenced. If curr_gw is NULL we also should not exit as we may
+ * have this gateway in our list (duplication check!) even though we
+ * have no currently selected gateway.
+ */
+ curr_gw = gw_get_selected_gw_node(bat_priv);
rcu_read_lock();
hlist_for_each_entry_rcu(gw_node, node, &bat_priv->gw_list, list) {
@@ -304,22 +329,26 @@ void gw_node_update(struct bat_priv *bat_priv,
"Gateway %pM removed from gateway list\n",
orig_node->orig);
- if (gw_node == rcu_dereference(bat_priv->curr_gw)) {
- rcu_read_unlock();
- gw_deselect(bat_priv);
- return;
- }
+ if (gw_node == curr_gw)
+ goto deselect;
}
- rcu_read_unlock();
- return;
+ goto unlock;
}
- rcu_read_unlock();
if (new_gwflags == 0)
- return;
+ goto unlock;
gw_node_add(bat_priv, orig_node, new_gwflags);
+ goto unlock;
+
+deselect:
+ gw_deselect(bat_priv);
+unlock:
+ rcu_read_unlock();
+
+ if (curr_gw)
+ gw_node_free_ref(curr_gw);
}
void gw_node_delete(struct bat_priv *bat_priv, struct orig_node *orig_node)
@@ -329,9 +358,12 @@ void gw_node_delete(struct bat_priv *bat_priv, struct orig_node *orig_node)
void gw_node_purge(struct bat_priv *bat_priv)
{
- struct gw_node *gw_node;
+ struct gw_node *gw_node, *curr_gw;
struct hlist_node *node, *node_tmp;
unsigned long timeout = 2 * PURGE_TIMEOUT * HZ;
+ char do_deselect = 0;
+
+ curr_gw = gw_get_selected_gw_node(bat_priv);
spin_lock_bh(&bat_priv->gw_list_lock);
@@ -342,41 +374,56 @@ void gw_node_purge(struct bat_priv *bat_priv)
atomic_read(&bat_priv->mesh_state) == MESH_ACTIVE)
continue;
- if (rcu_dereference(bat_priv->curr_gw) == gw_node)
- gw_deselect(bat_priv);
+ if (curr_gw == gw_node)
+ do_deselect = 1;
hlist_del_rcu(&gw_node->list);
gw_node_free_ref(gw_node);
}
-
spin_unlock_bh(&bat_priv->gw_list_lock);
+
+ /* gw_deselect() needs to acquire the gw_list_lock */
+ if (do_deselect)
+ gw_deselect(bat_priv);
+
+ if (curr_gw)
+ gw_node_free_ref(curr_gw);
}
+/**
+ * fails if orig_node has no router
+ */
static int _write_buffer_text(struct bat_priv *bat_priv,
struct seq_file *seq, struct gw_node *gw_node)
{
struct gw_node *curr_gw;
- int down, up, ret;
+ struct neigh_node *router;
+ int down, up, ret = -1;
gw_bandwidth_to_kbit(gw_node->orig_node->gw_flags, &down, &up);
- rcu_read_lock();
- curr_gw = rcu_dereference(bat_priv->curr_gw);
+ router = orig_node_get_router(gw_node->orig_node);
+ if (!router)
+ goto out;
- ret = seq_printf(seq, "%s %pM (%3i) %pM [%10s]: %3i - %i%s/%i%s\n",
- (curr_gw == gw_node ? "=>" : " "),
- gw_node->orig_node->orig,
- gw_node->orig_node->router->tq_avg,
- gw_node->orig_node->router->addr,
- gw_node->orig_node->router->if_incoming->net_dev->name,
- gw_node->orig_node->gw_flags,
- (down > 2048 ? down / 1024 : down),
- (down > 2048 ? "MBit" : "KBit"),
- (up > 2048 ? up / 1024 : up),
- (up > 2048 ? "MBit" : "KBit"));
+ curr_gw = gw_get_selected_gw_node(bat_priv);
- rcu_read_unlock();
+ ret = seq_printf(seq, "%s %pM (%3i) %pM [%10s]: %3i - %i%s/%i%s\n",
+ (curr_gw == gw_node ? "=>" : " "),
+ gw_node->orig_node->orig,
+ router->tq_avg, router->addr,
+ router->if_incoming->net_dev->name,
+ gw_node->orig_node->gw_flags,
+ (down > 2048 ? down / 1024 : down),
+ (down > 2048 ? "MBit" : "KBit"),
+ (up > 2048 ? up / 1024 : up),
+ (up > 2048 ? "MBit" : "KBit"));
+
+ neigh_node_free_ref(router);
+ if (curr_gw)
+ gw_node_free_ref(curr_gw);
+out:
return ret;
}
@@ -384,40 +431,42 @@ int gw_client_seq_print_text(struct seq_file *seq, void *offset)
{
struct net_device *net_dev = (struct net_device *)seq->private;
struct bat_priv *bat_priv = netdev_priv(net_dev);
+ struct hard_iface *primary_if;
struct gw_node *gw_node;
struct hlist_node *node;
- int gw_count = 0;
-
- if (!bat_priv->primary_if) {
+ int gw_count = 0, ret = 0;
- return seq_printf(seq, "BATMAN mesh %s disabled - please "
- "specify interfaces to enable it\n",
- net_dev->name);
+ primary_if = primary_if_get_selected(bat_priv);
+ if (!primary_if) {
+ ret = seq_printf(seq, "BATMAN mesh %s disabled - please "
+ "specify interfaces to enable it\n",
+ net_dev->name);
+ goto out;
}
- if (bat_priv->primary_if->if_status != IF_ACTIVE) {
-
- return seq_printf(seq, "BATMAN mesh %s disabled - "
- "primary interface not active\n",
- net_dev->name);
+ if (primary_if->if_status != IF_ACTIVE) {
+ ret = seq_printf(seq, "BATMAN mesh %s disabled - "
+ "primary interface not active\n",
+ net_dev->name);
+ goto out;
}
seq_printf(seq, " %-12s (%s/%i) %17s [%10s]: gw_class ... "
"[B.A.T.M.A.N. adv %s%s, MainIF/MAC: %s/%pM (%s)]\n",
"Gateway", "#", TQ_MAX_VALUE, "Nexthop",
"outgoingIF", SOURCE_VERSION, REVISION_VERSION_STR,
- bat_priv->primary_if->net_dev->name,
- bat_priv->primary_if->net_dev->dev_addr, net_dev->name);
+ primary_if->net_dev->name,
+ primary_if->net_dev->dev_addr, net_dev->name);
rcu_read_lock();
hlist_for_each_entry_rcu(gw_node, node, &bat_priv->gw_list, list) {
if (gw_node->deleted)
continue;
- if (!gw_node->orig_node->router)
+ /* fails if orig_node has no router */
+ if (_write_buffer_text(bat_priv, seq, gw_node) < 0)
continue;
- _write_buffer_text(bat_priv, seq, gw_node);
gw_count++;
}
rcu_read_unlock();
@@ -425,7 +474,10 @@ int gw_client_seq_print_text(struct seq_file *seq, void *offset)
if (gw_count == 0)
seq_printf(seq, "No gateways in range ...\n");
- return 0;
+out:
+ if (primary_if)
+ hardif_free_ref(primary_if);
+ return ret;
}
int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb)
@@ -434,6 +486,7 @@ int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb)
struct iphdr *iphdr;
struct ipv6hdr *ipv6hdr;
struct udphdr *udphdr;
+ struct gw_node *curr_gw;
unsigned int header_len = 0;
if (atomic_read(&bat_priv->gw_mode) == GW_MODE_OFF)
@@ -498,12 +551,11 @@ int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb)
if (atomic_read(&bat_priv->gw_mode) == GW_MODE_SERVER)
return -1;
- rcu_read_lock();
- if (!rcu_dereference(bat_priv->curr_gw)) {
- rcu_read_unlock();
+ curr_gw = gw_get_selected_gw_node(bat_priv);
+ if (!curr_gw)
return 0;
- }
- rcu_read_unlock();
+ if (curr_gw)
+ gw_node_free_ref(curr_gw);
return 1;
}
diff --git a/net/batman-adv/gateway_client.h b/net/batman-adv/gateway_client.h
index 2aa439124ee..1ce8c6066da 100644
--- a/net/batman-adv/gateway_client.h
+++ b/net/batman-adv/gateway_client.h
@@ -24,7 +24,7 @@
void gw_deselect(struct bat_priv *bat_priv);
void gw_election(struct bat_priv *bat_priv);
-void *gw_get_selected(struct bat_priv *bat_priv);
+struct orig_node *gw_get_selected_orig(struct bat_priv *bat_priv);
void gw_check_election(struct bat_priv *bat_priv, struct orig_node *orig_node);
void gw_node_update(struct bat_priv *bat_priv,
struct orig_node *orig_node, uint8_t new_gwflags);
diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c
index b3058e46ee6..dfbfccc9fe4 100644
--- a/net/batman-adv/hard-interface.c
+++ b/net/batman-adv/hard-interface.c
@@ -31,9 +31,6 @@
#include <linux/if_arp.h>
-/* protect update critical side of hardif_list - but not the content */
-static DEFINE_SPINLOCK(hardif_list_lock);
-
static int batman_skb_recv(struct sk_buff *skb,
struct net_device *dev,
@@ -110,47 +107,57 @@ out:
return hard_iface;
}
-static void update_primary_addr(struct bat_priv *bat_priv)
+static void primary_if_update_addr(struct bat_priv *bat_priv)
{
struct vis_packet *vis_packet;
+ struct hard_iface *primary_if;
+
+ primary_if = primary_if_get_selected(bat_priv);
+ if (!primary_if)
+ goto out;
vis_packet = (struct vis_packet *)
bat_priv->my_vis_info->skb_packet->data;
- memcpy(vis_packet->vis_orig,
- bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN);
+ memcpy(vis_packet->vis_orig, primary_if->net_dev->dev_addr, ETH_ALEN);
memcpy(vis_packet->sender_orig,
- bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN);
+ primary_if->net_dev->dev_addr, ETH_ALEN);
+
+out:
+ if (primary_if)
+ hardif_free_ref(primary_if);
}
-static void set_primary_if(struct bat_priv *bat_priv,
- struct hard_iface *hard_iface)
+static void primary_if_select(struct bat_priv *bat_priv,
+ struct hard_iface *new_hard_iface)
{
+ struct hard_iface *curr_hard_iface;
struct batman_packet *batman_packet;
- struct hard_iface *old_if;
- if (hard_iface && !atomic_inc_not_zero(&hard_iface->refcount))
- hard_iface = NULL;
+ ASSERT_RTNL();
+
+ if (new_hard_iface && !atomic_inc_not_zero(&new_hard_iface->refcount))
+ new_hard_iface = NULL;
- old_if = bat_priv->primary_if;
- bat_priv->primary_if = hard_iface;
+ curr_hard_iface = bat_priv->primary_if;
+ rcu_assign_pointer(bat_priv->primary_if, new_hard_iface);
- if (old_if)
- hardif_free_ref(old_if);
+ if (curr_hard_iface)
+ hardif_free_ref(curr_hard_iface);
- if (!bat_priv->primary_if)
+ if (!new_hard_iface)
return;
- batman_packet = (struct batman_packet *)(hard_iface->packet_buff);
+ batman_packet = (struct batman_packet *)(new_hard_iface->packet_buff);
batman_packet->flags = PRIMARIES_FIRST_HOP;
batman_packet->ttl = TTL;
- update_primary_addr(bat_priv);
+ primary_if_update_addr(bat_priv);
/***
- * hacky trick to make sure that we send the HNA information via
+ * hacky trick to make sure that we send the TT information via
* our new primary interface
*/
- atomic_set(&bat_priv->hna_local_changed, 1);
+ atomic_set(&bat_priv->tt_local_changed, 1);
}
static bool hardif_is_iface_up(struct hard_iface *hard_iface)
@@ -236,9 +243,10 @@ void update_min_mtu(struct net_device *soft_iface)
static void hardif_activate_interface(struct hard_iface *hard_iface)
{
struct bat_priv *bat_priv;
+ struct hard_iface *primary_if = NULL;
if (hard_iface->if_status != IF_INACTIVE)
- return;
+ goto out;
bat_priv = netdev_priv(hard_iface->soft_iface);
@@ -249,14 +257,18 @@ static void hardif_activate_interface(struct hard_iface *hard_iface)
* the first active interface becomes our primary interface or
* the next active interface after the old primay interface was removed
*/
- if (!bat_priv->primary_if)
- set_primary_if(bat_priv, hard_iface);
+ primary_if = primary_if_get_selected(bat_priv);
+ if (!primary_if)
+ primary_if_select(bat_priv, hard_iface);
bat_info(hard_iface->soft_iface, "Interface activated: %s\n",
hard_iface->net_dev->name);
update_min_mtu(hard_iface->soft_iface);
- return;
+
+out:
+ if (primary_if)
+ hardif_free_ref(primary_if);
}
static void hardif_deactivate_interface(struct hard_iface *hard_iface)
@@ -327,7 +339,7 @@ int hardif_enable_interface(struct hard_iface *hard_iface, char *iface_name)
batman_packet->flags = 0;
batman_packet->ttl = 2;
batman_packet->tq = TQ_MAX_VALUE;
- batman_packet->num_hna = 0;
+ batman_packet->num_tt = 0;
hard_iface->if_num = bat_priv->num_ifaces;
bat_priv->num_ifaces++;
@@ -386,12 +398,13 @@ err:
void hardif_disable_interface(struct hard_iface *hard_iface)
{
struct bat_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
+ struct hard_iface *primary_if = NULL;
if (hard_iface->if_status == IF_ACTIVE)
hardif_deactivate_interface(hard_iface);
if (hard_iface->if_status != IF_INACTIVE)
- return;
+ goto out;
bat_info(hard_iface->soft_iface, "Removing interface: %s\n",
hard_iface->net_dev->name);
@@ -400,11 +413,12 @@ void hardif_disable_interface(struct hard_iface *hard_iface)
bat_priv->num_ifaces--;
orig_hash_del_if(hard_iface, bat_priv->num_ifaces);
- if (hard_iface == bat_priv->primary_if) {
+ primary_if = primary_if_get_selected(bat_priv);
+ if (hard_iface == primary_if) {
struct hard_iface *new_if;
new_if = hardif_get_active(hard_iface->soft_iface);
- set_primary_if(bat_priv, new_if);
+ primary_if_select(bat_priv, new_if);
if (new_if)
hardif_free_ref(new_if);
@@ -425,6 +439,10 @@ void hardif_disable_interface(struct hard_iface *hard_iface)
hard_iface->soft_iface = NULL;
hardif_free_ref(hard_iface);
+
+out:
+ if (primary_if)
+ hardif_free_ref(primary_if);
}
static struct hard_iface *hardif_add_interface(struct net_device *net_dev)
@@ -432,6 +450,8 @@ static struct hard_iface *hardif_add_interface(struct net_device *net_dev)
struct hard_iface *hard_iface;
int ret;
+ ASSERT_RTNL();
+
ret = is_valid_iface(net_dev);
if (ret != 1)
goto out;
@@ -458,10 +478,7 @@ static struct hard_iface *hardif_add_interface(struct net_device *net_dev)
atomic_set(&hard_iface->refcount, 2);
check_known_mac_addr(hard_iface->net_dev);
-
- spin_lock(&hardif_list_lock);
list_add_tail_rcu(&hard_iface->list, &hardif_list);
- spin_unlock(&hardif_list_lock);
return hard_iface;
@@ -475,6 +492,8 @@ out:
static void hardif_remove_interface(struct hard_iface *hard_iface)
{
+ ASSERT_RTNL();
+
/* first deactivate interface */
if (hard_iface->if_status != IF_NOT_IN_USE)
hardif_disable_interface(hard_iface);
@@ -490,20 +509,11 @@ static void hardif_remove_interface(struct hard_iface *hard_iface)
void hardif_remove_interfaces(void)
{
struct hard_iface *hard_iface, *hard_iface_tmp;
- struct list_head if_queue;
-
- INIT_LIST_HEAD(&if_queue);
- spin_lock(&hardif_list_lock);
+ rtnl_lock();
list_for_each_entry_safe(hard_iface, hard_iface_tmp,
&hardif_list, list) {
list_del_rcu(&hard_iface->list);
- list_add_tail(&hard_iface->list, &if_queue);
- }
- spin_unlock(&hardif_list_lock);
-
- rtnl_lock();
- list_for_each_entry_safe(hard_iface, hard_iface_tmp, &if_queue, list) {
hardif_remove_interface(hard_iface);
}
rtnl_unlock();
@@ -514,6 +524,7 @@ static int hard_if_event(struct notifier_block *this,
{
struct net_device *net_dev = (struct net_device *)ptr;
struct hard_iface *hard_iface = hardif_get_by_netdev(net_dev);
+ struct hard_iface *primary_if = NULL;
struct bat_priv *bat_priv;
if (!hard_iface && event == NETDEV_REGISTER)
@@ -531,9 +542,7 @@ static int hard_if_event(struct notifier_block *this,
hardif_deactivate_interface(hard_iface);
break;
case NETDEV_UNREGISTER:
- spin_lock(&hardif_list_lock);
list_del_rcu(&hard_iface->list);
- spin_unlock(&hardif_list_lock);
hardif_remove_interface(hard_iface);
break;
@@ -549,8 +558,12 @@ static int hard_if_event(struct notifier_block *this,
update_mac_addresses(hard_iface);
bat_priv = netdev_priv(hard_iface->soft_iface);
- if (hard_iface == bat_priv->primary_if)
- update_primary_addr(bat_priv);
+ primary_if = primary_if_get_selected(bat_priv);
+ if (!primary_if)
+ goto hardif_put;
+
+ if (hard_iface == primary_if)
+ primary_if_update_addr(bat_priv);
break;
default:
break;
@@ -559,6 +572,8 @@ static int hard_if_event(struct notifier_block *this,
hardif_put:
hardif_free_ref(hard_iface);
out:
+ if (primary_if)
+ hardif_free_ref(primary_if);
return NOTIFY_DONE;
}
diff --git a/net/batman-adv/hard-interface.h b/net/batman-adv/hard-interface.h
index a9ddf36e51c..64265991460 100644
--- a/net/batman-adv/hard-interface.h
+++ b/net/batman-adv/hard-interface.h
@@ -45,4 +45,22 @@ static inline void hardif_free_ref(struct hard_iface *hard_iface)
call_rcu(&hard_iface->rcu, hardif_free_rcu);
}
+static inline struct hard_iface *primary_if_get_selected(
+ struct bat_priv *bat_priv)
+{
+ struct hard_iface *hard_iface;
+
+ rcu_read_lock();
+ hard_iface = rcu_dereference(bat_priv->primary_if);
+ if (!hard_iface)
+ goto out;
+
+ if (!atomic_inc_not_zero(&hard_iface->refcount))
+ hard_iface = NULL;
+
+out:
+ rcu_read_unlock();
+ return hard_iface;
+}
+
#endif /* _NET_BATMAN_ADV_HARD_INTERFACE_H_ */
diff --git a/net/batman-adv/icmp_socket.c b/net/batman-adv/icmp_socket.c
index 34ce56c358e..fa22ba2bb83 100644
--- a/net/batman-adv/icmp_socket.c
+++ b/net/batman-adv/icmp_socket.c
@@ -153,6 +153,7 @@ static ssize_t bat_socket_write(struct file *file, const char __user *buff,
{
struct socket_client *socket_client = file->private_data;
struct bat_priv *bat_priv = socket_client->bat_priv;
+ struct hard_iface *primary_if = NULL;
struct sk_buff *skb;
struct icmp_packet_rr *icmp_packet;
@@ -167,15 +168,21 @@ static ssize_t bat_socket_write(struct file *file, const char __user *buff,
return -EINVAL;
}
- if (!bat_priv->primary_if)
- return -EFAULT;
+ primary_if = primary_if_get_selected(bat_priv);
+
+ if (!primary_if) {
+ len = -EFAULT;
+ goto out;
+ }
if (len >= sizeof(struct icmp_packet_rr))
packet_len = sizeof(struct icmp_packet_rr);
skb = dev_alloc_skb(packet_len + sizeof(struct ethhdr));
- if (!skb)
- return -ENOMEM;
+ if (!skb) {
+ len = -ENOMEM;
+ goto out;
+ }
skb_reserve(skb, sizeof(struct ethhdr));
icmp_packet = (struct icmp_packet_rr *)skb_put(skb, packet_len);
@@ -218,23 +225,13 @@ static ssize_t bat_socket_write(struct file *file, const char __user *buff,
if (atomic_read(&bat_priv->mesh_state) != MESH_ACTIVE)
goto dst_unreach;
- rcu_read_lock();
orig_node = orig_hash_find(bat_priv, icmp_packet->dst);
-
if (!orig_node)
- goto unlock;
-
- neigh_node = orig_node->router;
+ goto dst_unreach;
+ neigh_node = orig_node_get_router(orig_node);
if (!neigh_node)
- goto unlock;
-
- if (!atomic_inc_not_zero(&neigh_node->refcount)) {
- neigh_node = NULL;
- goto unlock;
- }
-
- rcu_read_unlock();
+ goto dst_unreach;
if (!neigh_node->if_incoming)
goto dst_unreach;
@@ -243,7 +240,7 @@ static ssize_t bat_socket_write(struct file *file, const char __user *buff,
goto dst_unreach;
memcpy(icmp_packet->orig,
- bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN);
+ primary_if->net_dev->dev_addr, ETH_ALEN);
if (packet_len == sizeof(struct icmp_packet_rr))
memcpy(icmp_packet->rr,
@@ -252,14 +249,14 @@ static ssize_t bat_socket_write(struct file *file, const char __user *buff,
send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr);
goto out;
-unlock:
- rcu_read_unlock();
dst_unreach:
icmp_packet->msg_type = DESTINATION_UNREACHABLE;
bat_socket_add_packet(socket_client, icmp_packet, packet_len);
free_skb:
kfree_skb(skb);
out:
+ if (primary_if)
+ hardif_free_ref(primary_if);
if (neigh_node)
neigh_node_free_ref(neigh_node);
if (orig_node)
diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c
index 709b33bbdf4..0a7cee0076f 100644
--- a/net/batman-adv/main.c
+++ b/net/batman-adv/main.c
@@ -33,6 +33,9 @@
#include "vis.h"
#include "hash.h"
+
+/* List manipulations on hardif_list have to be rtnl_lock()'ed,
+ * list traversals just rcu-locked */
struct list_head hardif_list;
unsigned char broadcast_addr[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
@@ -81,28 +84,29 @@ int mesh_init(struct net_device *soft_iface)
spin_lock_init(&bat_priv->forw_bat_list_lock);
spin_lock_init(&bat_priv->forw_bcast_list_lock);
- spin_lock_init(&bat_priv->hna_lhash_lock);
- spin_lock_init(&bat_priv->hna_ghash_lock);
+ spin_lock_init(&bat_priv->tt_lhash_lock);
+ spin_lock_init(&bat_priv->tt_ghash_lock);
spin_lock_init(&bat_priv->gw_list_lock);
spin_lock_init(&bat_priv->vis_hash_lock);
spin_lock_init(&bat_priv->vis_list_lock);
spin_lock_init(&bat_priv->softif_neigh_lock);
+ spin_lock_init(&bat_priv->softif_neigh_vid_lock);
INIT_HLIST_HEAD(&bat_priv->forw_bat_list);
INIT_HLIST_HEAD(&bat_priv->forw_bcast_list);
INIT_HLIST_HEAD(&bat_priv->gw_list);
- INIT_HLIST_HEAD(&bat_priv->softif_neigh_list);
+ INIT_HLIST_HEAD(&bat_priv->softif_neigh_vids);
if (originator_init(bat_priv) < 1)
goto err;
- if (hna_local_init(bat_priv) < 1)
+ if (tt_local_init(bat_priv) < 1)
goto err;
- if (hna_global_init(bat_priv) < 1)
+ if (tt_global_init(bat_priv) < 1)
goto err;
- hna_local_add(soft_iface, soft_iface->dev_addr);
+ tt_local_add(soft_iface, soft_iface->dev_addr);
if (vis_init(bat_priv) < 1)
goto err;
@@ -133,8 +137,8 @@ void mesh_free(struct net_device *soft_iface)
gw_node_purge(bat_priv);
originator_free(bat_priv);
- hna_local_free(bat_priv);
- hna_global_free(bat_priv);
+ tt_local_free(bat_priv);
+ tt_global_free(bat_priv);
softif_neigh_purge(bat_priv);
diff --git a/net/batman-adv/main.h b/net/batman-adv/main.h
index dc248697de7..148b49e0264 100644
--- a/net/batman-adv/main.h
+++ b/net/batman-adv/main.h
@@ -34,16 +34,18 @@
#define TQ_MAX_VALUE 255
#define JITTER 20
-#define TTL 50 /* Time To Live of broadcast messages */
-#define PURGE_TIMEOUT 200 /* purge originators after time in seconds if no
- * valid packet comes in -> TODO: check
- * influence on TQ_LOCAL_WINDOW_SIZE */
-#define LOCAL_HNA_TIMEOUT 3600 /* in seconds */
+ /* Time To Live of broadcast messages */
+#define TTL 50
-#define TQ_LOCAL_WINDOW_SIZE 64 /* sliding packet range of received originator
- * messages in squence numbers (should be a
- * multiple of our word size) */
+/* purge originators after time in seconds if no valid packet comes in
+ * -> TODO: check influence on TQ_LOCAL_WINDOW_SIZE */
+#define PURGE_TIMEOUT 200
+#define TT_LOCAL_TIMEOUT 3600 /* in seconds */
+
+/* sliding packet range of received originator messages in squence numbers
+ * (should be a multiple of our word size) */
+#define TQ_LOCAL_WINDOW_SIZE 64
#define TQ_GLOBAL_WINDOW_SIZE 5
#define TQ_LOCAL_BIDRECT_SEND_MINIMUM 1
#define TQ_LOCAL_BIDRECT_RECV_MINIMUM 1
@@ -55,21 +57,20 @@
#define VIS_INTERVAL 5000 /* 5 seconds */
-/* how much worse secondary interfaces may be to
- * to be considered as bonding candidates */
-
+/* how much worse secondary interfaces may be to be considered as bonding
+ * candidates */
#define BONDING_TQ_THRESHOLD 50
-#define MAX_AGGREGATION_BYTES 512 /* should not be bigger than 512 bytes or
- * change the size of
- * forw_packet->direct_link_flags */
+/* should not be bigger than 512 bytes or change the size of
+ * forw_packet->direct_link_flags */
+#define MAX_AGGREGATION_BYTES 512
#define MAX_AGGREGATION_MS 100
#define SOFTIF_NEIGH_TIMEOUT 180000 /* 3 minutes */
+/* don't reset again within 30 seconds */
#define RESET_PROTECTION_MS 30000
#define EXPECTED_SEQNO_RANGE 65536
-/* don't reset again within 30 seconds */
#define MESH_INACTIVE 0
#define MESH_ACTIVE 1
@@ -84,12 +85,13 @@
#ifdef pr_fmt
#undef pr_fmt
#endif
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt /* Append 'batman-adv: ' before
- * kernel messages */
+/* Append 'batman-adv: ' before kernel messages */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#define DBG_BATMAN 1 /* all messages related to routing / flooding /
- * broadcasting / etc */
-#define DBG_ROUTES 2 /* route or hna added / changed / deleted */
+/* all messages related to routing / flooding / broadcasting / etc */
+#define DBG_BATMAN 1
+/* route or tt entry added / changed / deleted */
+#define DBG_ROUTES 2
#define DBG_ALL 3
@@ -175,4 +177,6 @@ static inline int compare_eth(void *data1, void *data2)
return (memcmp(data1, data2, ETH_ALEN) == 0 ? 1 : 0);
}
+#define atomic_dec_not_zero(v) atomic_add_unless((v), -1, 0)
+
#endif /* _NET_BATMAN_ADV_MAIN_H_ */
diff --git a/net/batman-adv/originator.c b/net/batman-adv/originator.c
index ed23a5895d6..40a30bbcd14 100644
--- a/net/batman-adv/originator.c
+++ b/net/batman-adv/originator.c
@@ -19,8 +19,6 @@
*
*/
-/* increase the reference counter for this originator */
-
#include "main.h"
#include "originator.h"
#include "hash.h"
@@ -62,6 +60,21 @@ void neigh_node_free_ref(struct neigh_node *neigh_node)
kfree_rcu(neigh_node, rcu);
}
+/* increases the refcounter of a found router */
+struct neigh_node *orig_node_get_router(struct orig_node *orig_node)
+{
+ struct neigh_node *router;
+
+ rcu_read_lock();
+ router = rcu_dereference(orig_node->router);
+
+ if (router && !atomic_inc_not_zero(&router->refcount))
+ router = NULL;
+
+ rcu_read_unlock();
+ return router;
+}
+
struct neigh_node *create_neighbor(struct orig_node *orig_node,
struct orig_node *orig_neigh_node,
uint8_t *neigh,
@@ -79,6 +92,7 @@ struct neigh_node *create_neighbor(struct orig_node *orig_node,
INIT_HLIST_NODE(&neigh_node->list);
INIT_LIST_HEAD(&neigh_node->bonding_list);
+ spin_lock_init(&neigh_node->tq_lock);
memcpy(neigh_node->addr, neigh, ETH_ALEN);
neigh_node->orig_node = orig_neigh_node;
@@ -120,7 +134,7 @@ static void orig_node_free_rcu(struct rcu_head *rcu)
spin_unlock_bh(&orig_node->neigh_list_lock);
frag_list_free(&orig_node->frag_list);
- hna_global_del_orig(orig_node->bat_priv, orig_node,
+ tt_global_del_orig(orig_node->bat_priv, orig_node,
"originator timed out");
kfree(orig_node->bcast_own);
@@ -198,7 +212,7 @@ struct orig_node *get_orig_node(struct bat_priv *bat_priv, uint8_t *addr)
orig_node->bat_priv = bat_priv;
memcpy(orig_node->orig, addr, ETH_ALEN);
orig_node->router = NULL;
- orig_node->hna_buff = NULL;
+ orig_node->tt_buff = NULL;
orig_node->bcast_seqno_reset = jiffies - 1
- msecs_to_jiffies(RESET_PROTECTION_MS);
orig_node->batman_seqno_reset = jiffies - 1
@@ -309,8 +323,8 @@ static bool purge_orig_node(struct bat_priv *bat_priv,
&best_neigh_node)) {
update_routes(bat_priv, orig_node,
best_neigh_node,
- orig_node->hna_buff,
- orig_node->hna_buff_len);
+ orig_node->tt_buff,
+ orig_node->tt_buff_len);
}
}
@@ -381,29 +395,34 @@ int orig_seq_print_text(struct seq_file *seq, void *offset)
struct hashtable_t *hash = bat_priv->orig_hash;
struct hlist_node *node, *node_tmp;
struct hlist_head *head;
+ struct hard_iface *primary_if;
struct orig_node *orig_node;
- struct neigh_node *neigh_node;
+ struct neigh_node *neigh_node, *neigh_node_tmp;
int batman_count = 0;
int last_seen_secs;
int last_seen_msecs;
- int i;
+ int i, ret = 0;
- if ((!bat_priv->primary_if) ||
- (bat_priv->primary_if->if_status != IF_ACTIVE)) {
- if (!bat_priv->primary_if)
- return seq_printf(seq, "BATMAN mesh %s disabled - "
- "please specify interfaces to enable it\n",
- net_dev->name);
+ primary_if = primary_if_get_selected(bat_priv);
+
+ if (!primary_if) {
+ ret = seq_printf(seq, "BATMAN mesh %s disabled - "
+ "please specify interfaces to enable it\n",
+ net_dev->name);
+ goto out;
+ }
- return seq_printf(seq, "BATMAN mesh %s "
- "disabled - primary interface not active\n",
- net_dev->name);
+ if (primary_if->if_status != IF_ACTIVE) {
+ ret = seq_printf(seq, "BATMAN mesh %s "
+ "disabled - primary interface not active\n",
+ net_dev->name);
+ goto out;
}
seq_printf(seq, "[B.A.T.M.A.N. adv %s%s, MainIF/MAC: %s/%pM (%s)]\n",
SOURCE_VERSION, REVISION_VERSION_STR,
- bat_priv->primary_if->net_dev->name,
- bat_priv->primary_if->net_dev->dev_addr, net_dev->name);
+ primary_if->net_dev->name,
+ primary_if->net_dev->dev_addr, net_dev->name);
seq_printf(seq, " %-15s %s (%s/%i) %17s [%10s]: %20s ...\n",
"Originator", "last-seen", "#", TQ_MAX_VALUE, "Nexthop",
"outgoingIF", "Potential nexthops");
@@ -413,40 +432,47 @@ int orig_seq_print_text(struct seq_file *seq, void *offset)
rcu_read_lock();
hlist_for_each_entry_rcu(orig_node, node, head, hash_entry) {
- if (!orig_node->router)
+ neigh_node = orig_node_get_router(orig_node);
+ if (!neigh_node)
continue;
- if (orig_node->router->tq_avg == 0)
- continue;
+ if (neigh_node->tq_avg == 0)
+ goto next;
last_seen_secs = jiffies_to_msecs(jiffies -
orig_node->last_valid) / 1000;
last_seen_msecs = jiffies_to_msecs(jiffies -
orig_node->last_valid) % 1000;
- neigh_node = orig_node->router;
seq_printf(seq, "%pM %4i.%03is (%3i) %pM [%10s]:",
orig_node->orig, last_seen_secs,
last_seen_msecs, neigh_node->tq_avg,
neigh_node->addr,
neigh_node->if_incoming->net_dev->name);
- hlist_for_each_entry_rcu(neigh_node, node_tmp,
+ hlist_for_each_entry_rcu(neigh_node_tmp, node_tmp,
&orig_node->neigh_list, list) {
- seq_printf(seq, " %pM (%3i)", neigh_node->addr,
- neigh_node->tq_avg);
+ seq_printf(seq, " %pM (%3i)",
+ neigh_node_tmp->addr,
+ neigh_node_tmp->tq_avg);
}
seq_printf(seq, "\n");
batman_count++;
+
+next:
+ neigh_node_free_ref(neigh_node);
}
rcu_read_unlock();
}
- if ((batman_count == 0))
+ if (batman_count == 0)
seq_printf(seq, "No batman nodes in range ...\n");
- return 0;
+out:
+ if (primary_if)
+ hardif_free_ref(primary_if);
+ return ret;
}
static int orig_node_add_if(struct orig_node *orig_node, int max_if_num)
diff --git a/net/batman-adv/originator.h b/net/batman-adv/originator.h
index 5cc011057da..e1d641f27aa 100644
--- a/net/batman-adv/originator.h
+++ b/net/batman-adv/originator.h
@@ -34,6 +34,7 @@ struct neigh_node *create_neighbor(struct orig_node *orig_node,
uint8_t *neigh,
struct hard_iface *if_incoming);
void neigh_node_free_ref(struct neigh_node *neigh_node);
+struct neigh_node *orig_node_get_router(struct orig_node *orig_node);
int orig_seq_print_text(struct seq_file *seq, void *offset);
int orig_hash_add_if(struct hard_iface *hard_iface, int max_if_num);
int orig_hash_del_if(struct hard_iface *hard_iface, int max_if_num);
diff --git a/net/batman-adv/packet.h b/net/batman-adv/packet.h
index e7571879af3..eda99650e9f 100644
--- a/net/batman-adv/packet.h
+++ b/net/batman-adv/packet.h
@@ -61,7 +61,7 @@ struct batman_packet {
uint8_t orig[6];
uint8_t prev_sender[6];
uint8_t ttl;
- uint8_t num_hna;
+ uint8_t num_tt;
uint8_t gw_flags; /* flags related to gateway class */
uint8_t align;
} __packed;
@@ -128,8 +128,7 @@ struct vis_packet {
uint8_t entries; /* number of entries behind this struct */
uint32_t seqno; /* sequence number */
uint8_t ttl; /* TTL */
- uint8_t vis_orig[6]; /* originator that informs about its
- * neighbors */
+ uint8_t vis_orig[6]; /* originator that announces its neighbors */
uint8_t target_orig[6]; /* who should receive this packet */
uint8_t sender_orig[6]; /* who sent or rebroadcasted this packet */
} __packed;
diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c
index c172f5d0e05..bb1c3ec7e3f 100644
--- a/net/batman-adv/routing.c
+++ b/net/batman-adv/routing.c
@@ -64,80 +64,97 @@ void slide_own_bcast_window(struct hard_iface *hard_iface)
}
}
-static void update_HNA(struct bat_priv *bat_priv, struct orig_node *orig_node,
- unsigned char *hna_buff, int hna_buff_len)
+static void update_TT(struct bat_priv *bat_priv, struct orig_node *orig_node,
+ unsigned char *tt_buff, int tt_buff_len)
{
- if ((hna_buff_len != orig_node->hna_buff_len) ||
- ((hna_buff_len > 0) &&
- (orig_node->hna_buff_len > 0) &&
- (memcmp(orig_node->hna_buff, hna_buff, hna_buff_len) != 0))) {
-
- if (orig_node->hna_buff_len > 0)
- hna_global_del_orig(bat_priv, orig_node,
- "originator changed hna");
-
- if ((hna_buff_len > 0) && (hna_buff))
- hna_global_add_orig(bat_priv, orig_node,
- hna_buff, hna_buff_len);
+ if ((tt_buff_len != orig_node->tt_buff_len) ||
+ ((tt_buff_len > 0) &&
+ (orig_node->tt_buff_len > 0) &&
+ (memcmp(orig_node->tt_buff, tt_buff, tt_buff_len) != 0))) {
+
+ if (orig_node->tt_buff_len > 0)
+ tt_global_del_orig(bat_priv, orig_node,
+ "originator changed tt");
+
+ if ((tt_buff_len > 0) && (tt_buff))
+ tt_global_add_orig(bat_priv, orig_node,
+ tt_buff, tt_buff_len);
}
}
static void update_route(struct bat_priv *bat_priv,
struct orig_node *orig_node,
struct neigh_node *neigh_node,
- unsigned char *hna_buff, int hna_buff_len)
+ unsigned char *tt_buff, int tt_buff_len)
{
- struct neigh_node *neigh_node_tmp;
+ struct neigh_node *curr_router;
+
+ curr_router = orig_node_get_router(orig_node);
/* route deleted */
- if ((orig_node->router) && (!neigh_node)) {
+ if ((curr_router) && (!neigh_node)) {
bat_dbg(DBG_ROUTES, bat_priv, "Deleting route towards: %pM\n",
orig_node->orig);
- hna_global_del_orig(bat_priv, orig_node,
+ tt_global_del_orig(bat_priv, orig_node,
"originator timed out");
- /* route added */
- } else if ((!orig_node->router) && (neigh_node)) {
+ /* route added */
+ } else if ((!curr_router) && (neigh_node)) {
bat_dbg(DBG_ROUTES, bat_priv,
"Adding route towards: %pM (via %pM)\n",
orig_node->orig, neigh_node->addr);
- hna_global_add_orig(bat_priv, orig_node,
- hna_buff, hna_buff_len);
+ tt_global_add_orig(bat_priv, orig_node,
+ tt_buff, tt_buff_len);
- /* route changed */
+ /* route changed */
} else {
bat_dbg(DBG_ROUTES, bat_priv,
"Changing route towards: %pM "
"(now via %pM - was via %pM)\n",
orig_node->orig, neigh_node->addr,
- orig_node->router->addr);
+ curr_router->addr);
}
+ if (curr_router)
+ neigh_node_free_ref(curr_router);
+
+ /* increase refcount of new best neighbor */
if (neigh_node && !atomic_inc_not_zero(&neigh_node->refcount))
neigh_node = NULL;
- neigh_node_tmp = orig_node->router;
- orig_node->router = neigh_node;
- if (neigh_node_tmp)
- neigh_node_free_ref(neigh_node_tmp);
+
+ spin_lock_bh(&orig_node->neigh_list_lock);
+ rcu_assign_pointer(orig_node->router, neigh_node);
+ spin_unlock_bh(&orig_node->neigh_list_lock);
+
+ /* decrease refcount of previous best neighbor */
+ if (curr_router)
+ neigh_node_free_ref(curr_router);
}
void update_routes(struct bat_priv *bat_priv, struct orig_node *orig_node,
- struct neigh_node *neigh_node, unsigned char *hna_buff,
- int hna_buff_len)
+ struct neigh_node *neigh_node, unsigned char *tt_buff,
+ int tt_buff_len)
{
+ struct neigh_node *router = NULL;
if (!orig_node)
- return;
+ goto out;
+
+ router = orig_node_get_router(orig_node);
- if (orig_node->router != neigh_node)
+ if (router != neigh_node)
update_route(bat_priv, orig_node, neigh_node,
- hna_buff, hna_buff_len);
- /* may be just HNA changed */
+ tt_buff, tt_buff_len);
+ /* may be just TT changed */
else
- update_HNA(bat_priv, orig_node, hna_buff, hna_buff_len);
+ update_TT(bat_priv, orig_node, tt_buff, tt_buff_len);
+
+out:
+ if (router)
+ neigh_node_free_ref(router);
}
static int is_bidirectional_neigh(struct orig_node *orig_node,
@@ -152,65 +169,41 @@ static int is_bidirectional_neigh(struct orig_node *orig_node,
uint8_t orig_eq_count, neigh_rq_count, tq_own;
int tq_asym_penalty, ret = 0;
- if (orig_node == orig_neigh_node) {
- rcu_read_lock();
- hlist_for_each_entry_rcu(tmp_neigh_node, node,
- &orig_node->neigh_list, list) {
-
- if (!compare_eth(tmp_neigh_node->addr,
- orig_neigh_node->orig))
- continue;
-
- if (tmp_neigh_node->if_incoming != if_incoming)
- continue;
-
- if (!atomic_inc_not_zero(&tmp_neigh_node->refcount))
- continue;
-
- neigh_node = tmp_neigh_node;
- }
- rcu_read_unlock();
+ /* find corresponding one hop neighbor */
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(tmp_neigh_node, node,
+ &orig_neigh_node->neigh_list, list) {
- if (!neigh_node)
- neigh_node = create_neighbor(orig_node,
- orig_neigh_node,
- orig_neigh_node->orig,
- if_incoming);
- if (!neigh_node)
- goto out;
+ if (!compare_eth(tmp_neigh_node->addr, orig_neigh_node->orig))
+ continue;
- neigh_node->last_valid = jiffies;
- } else {
- /* find packet count of corresponding one hop neighbor */
- rcu_read_lock();
- hlist_for_each_entry_rcu(tmp_neigh_node, node,
- &orig_neigh_node->neigh_list, list) {
+ if (tmp_neigh_node->if_incoming != if_incoming)
+ continue;
- if (!compare_eth(tmp_neigh_node->addr,
- orig_neigh_node->orig))
- continue;
+ if (!atomic_inc_not_zero(&tmp_neigh_node->refcount))
+ continue;
- if (tmp_neigh_node->if_incoming != if_incoming)
- continue;
+ neigh_node = tmp_neigh_node;
+ break;
+ }
+ rcu_read_unlock();
- if (!atomic_inc_not_zero(&tmp_neigh_node->refcount))
- continue;
+ if (!neigh_node)
+ neigh_node = create_neighbor(orig_neigh_node,
+ orig_neigh_node,
+ orig_neigh_node->orig,
+ if_incoming);
- neigh_node = tmp_neigh_node;
- }
- rcu_read_unlock();
+ if (!neigh_node)
+ goto out;
- if (!neigh_node)
- neigh_node = create_neighbor(orig_neigh_node,
- orig_neigh_node,
- orig_neigh_node->orig,
- if_incoming);
- if (!neigh_node)
- goto out;
- }
+ /* if orig_node is direct neighbour update neigh_node last_valid */
+ if (orig_node == orig_neigh_node)
+ neigh_node->last_valid = jiffies;
orig_node->last_valid = jiffies;
+ /* find packet count of corresponding one hop neighbor */
spin_lock_bh(&orig_node->ogm_cnt_lock);
orig_eq_count = orig_neigh_node->bcast_own_sum[if_incoming->if_num];
neigh_rq_count = neigh_node->real_packet_count;
@@ -288,8 +281,8 @@ static void bonding_candidate_add(struct orig_node *orig_node,
struct neigh_node *neigh_node)
{
struct hlist_node *node;
- struct neigh_node *tmp_neigh_node;
- uint8_t best_tq, interference_candidate = 0;
+ struct neigh_node *tmp_neigh_node, *router = NULL;
+ uint8_t interference_candidate = 0;
spin_lock_bh(&orig_node->neigh_list_lock);
@@ -298,13 +291,12 @@ static void bonding_candidate_add(struct orig_node *orig_node,
neigh_node->orig_node->primary_addr))
goto candidate_del;
- if (!orig_node->router)
+ router = orig_node_get_router(orig_node);
+ if (!router)
goto candidate_del;
- best_tq = orig_node->router->tq_avg;
-
/* ... and is good enough to be considered */
- if (neigh_node->tq_avg < best_tq - BONDING_TQ_THRESHOLD)
+ if (neigh_node->tq_avg < router->tq_avg - BONDING_TQ_THRESHOLD)
goto candidate_del;
/**
@@ -350,7 +342,9 @@ candidate_del:
out:
spin_unlock_bh(&orig_node->neigh_list_lock);
- return;
+
+ if (router)
+ neigh_node_free_ref(router);
}
/* copy primary address for bonding */
@@ -369,13 +363,14 @@ static void update_orig(struct bat_priv *bat_priv,
struct ethhdr *ethhdr,
struct batman_packet *batman_packet,
struct hard_iface *if_incoming,
- unsigned char *hna_buff, int hna_buff_len,
+ unsigned char *tt_buff, int tt_buff_len,
char is_duplicate)
{
struct neigh_node *neigh_node = NULL, *tmp_neigh_node = NULL;
+ struct neigh_node *router = NULL;
struct orig_node *orig_node_tmp;
struct hlist_node *node;
- int tmp_hna_buff_len;
+ int tmp_tt_buff_len;
uint8_t bcast_own_sum_orig, bcast_own_sum_neigh;
bat_dbg(DBG_BATMAN, bat_priv, "update_originator(): "
@@ -396,10 +391,12 @@ static void update_orig(struct bat_priv *bat_priv,
if (is_duplicate)
continue;
+ spin_lock_bh(&tmp_neigh_node->tq_lock);
ring_buffer_set(tmp_neigh_node->tq_recv,
&tmp_neigh_node->tq_index, 0);
tmp_neigh_node->tq_avg =
ring_buffer_avg(tmp_neigh_node->tq_recv);
+ spin_unlock_bh(&tmp_neigh_node->tq_lock);
}
if (!neigh_node) {
@@ -424,10 +421,12 @@ static void update_orig(struct bat_priv *bat_priv,
orig_node->flags = batman_packet->flags;
neigh_node->last_valid = jiffies;
+ spin_lock_bh(&neigh_node->tq_lock);
ring_buffer_set(neigh_node->tq_recv,
&neigh_node->tq_index,
batman_packet->tq);
neigh_node->tq_avg = ring_buffer_avg(neigh_node->tq_recv);
+ spin_unlock_bh(&neigh_node->tq_lock);
if (!is_duplicate) {
orig_node->last_ttl = batman_packet->ttl;
@@ -436,24 +435,23 @@ static void update_orig(struct bat_priv *bat_priv,
bonding_candidate_add(orig_node, neigh_node);
- tmp_hna_buff_len = (hna_buff_len > batman_packet->num_hna * ETH_ALEN ?
- batman_packet->num_hna * ETH_ALEN : hna_buff_len);
+ tmp_tt_buff_len = (tt_buff_len > batman_packet->num_tt * ETH_ALEN ?
+ batman_packet->num_tt * ETH_ALEN : tt_buff_len);
/* if this neighbor already is our next hop there is nothing
* to change */
- if (orig_node->router == neigh_node)
- goto update_hna;
+ router = orig_node_get_router(orig_node);
+ if (router == neigh_node)
+ goto update_tt;
/* if this neighbor does not offer a better TQ we won't consider it */
- if ((orig_node->router) &&
- (orig_node->router->tq_avg > neigh_node->tq_avg))
- goto update_hna;
+ if (router && (router->tq_avg > neigh_node->tq_avg))
+ goto update_tt;
/* if the TQ is the same and the link not more symetric we
* won't consider it either */
- if ((orig_node->router) &&
- (neigh_node->tq_avg == orig_node->router->tq_avg)) {
- orig_node_tmp = orig_node->router->orig_node;
+ if (router && (neigh_node->tq_avg == router->tq_avg)) {
+ orig_node_tmp = router->orig_node;
spin_lock_bh(&orig_node_tmp->ogm_cnt_lock);
bcast_own_sum_orig =
orig_node_tmp->bcast_own_sum[if_incoming->if_num];
@@ -466,16 +464,16 @@ static void update_orig(struct bat_priv *bat_priv,
spin_unlock_bh(&orig_node_tmp->ogm_cnt_lock);
if (bcast_own_sum_orig >= bcast_own_sum_neigh)
- goto update_hna;
+ goto update_tt;
}
update_routes(bat_priv, orig_node, neigh_node,
- hna_buff, tmp_hna_buff_len);
+ tt_buff, tmp_tt_buff_len);
goto update_gw;
-update_hna:
- update_routes(bat_priv, orig_node, orig_node->router,
- hna_buff, tmp_hna_buff_len);
+update_tt:
+ update_routes(bat_priv, orig_node, router,
+ tt_buff, tmp_tt_buff_len);
update_gw:
if (orig_node->gw_flags != batman_packet->gw_flags)
@@ -496,6 +494,8 @@ unlock:
out:
if (neigh_node)
neigh_node_free_ref(neigh_node);
+ if (router)
+ neigh_node_free_ref(router);
}
/* checks whether the host restarted and is in the protection time.
@@ -597,12 +597,14 @@ out:
void receive_bat_packet(struct ethhdr *ethhdr,
struct batman_packet *batman_packet,
- unsigned char *hna_buff, int hna_buff_len,
+ unsigned char *tt_buff, int tt_buff_len,
struct hard_iface *if_incoming)
{
struct bat_priv *bat_priv = netdev_priv(if_incoming->soft_iface);
struct hard_iface *hard_iface;
struct orig_node *orig_neigh_node, *orig_node;
+ struct neigh_node *router = NULL, *router_router = NULL;
+ struct neigh_node *orig_neigh_router = NULL;
char has_directlink_flag;
char is_my_addr = 0, is_my_orig = 0, is_my_oldorig = 0;
char is_broadcast = 0, is_bidirectional, is_single_hop_neigh;
@@ -747,14 +749,15 @@ void receive_bat_packet(struct ethhdr *ethhdr,
goto out;
}
+ router = orig_node_get_router(orig_node);
+ if (router)
+ router_router = orig_node_get_router(router->orig_node);
+
/* avoid temporary routing loops */
- if ((orig_node->router) &&
- (orig_node->router->orig_node->router) &&
- (compare_eth(orig_node->router->addr,
- batman_packet->prev_sender)) &&
+ if (router && router_router &&
+ (compare_eth(router->addr, batman_packet->prev_sender)) &&
!(compare_eth(batman_packet->orig, batman_packet->prev_sender)) &&
- (compare_eth(orig_node->router->addr,
- orig_node->router->orig_node->router->addr))) {
+ (compare_eth(router->addr, router_router->addr))) {
bat_dbg(DBG_BATMAN, bat_priv,
"Drop packet: ignoring all rebroadcast packets that "
"may make me loop (sender: %pM)\n", ethhdr->h_source);
@@ -769,9 +772,11 @@ void receive_bat_packet(struct ethhdr *ethhdr,
if (!orig_neigh_node)
goto out;
+ orig_neigh_router = orig_node_get_router(orig_neigh_node);
+
/* drop packet if sender is not a direct neighbor and if we
* don't route towards it */
- if (!is_single_hop_neigh && (!orig_neigh_node->router)) {
+ if (!is_single_hop_neigh && (!orig_neigh_router)) {
bat_dbg(DBG_BATMAN, bat_priv,
"Drop packet: OGM via unknown neighbor!\n");
goto out_neigh;
@@ -789,14 +794,14 @@ void receive_bat_packet(struct ethhdr *ethhdr,
((orig_node->last_real_seqno == batman_packet->seqno) &&
(orig_node->last_ttl - 3 <= batman_packet->ttl))))
update_orig(bat_priv, orig_node, ethhdr, batman_packet,
- if_incoming, hna_buff, hna_buff_len, is_duplicate);
+ if_incoming, tt_buff, tt_buff_len, is_duplicate);
/* is single hop (direct) neighbor */
if (is_single_hop_neigh) {
/* mark direct link on incoming interface */
schedule_forward_packet(orig_node, ethhdr, batman_packet,
- 1, hna_buff_len, if_incoming);
+ 1, tt_buff_len, if_incoming);
bat_dbg(DBG_BATMAN, bat_priv, "Forwarding packet: "
"rebroadcast neighbor packet with direct link flag\n");
@@ -819,12 +824,19 @@ void receive_bat_packet(struct ethhdr *ethhdr,
bat_dbg(DBG_BATMAN, bat_priv,
"Forwarding packet: rebroadcast originator packet\n");
schedule_forward_packet(orig_node, ethhdr, batman_packet,
- 0, hna_buff_len, if_incoming);
+ 0, tt_buff_len, if_incoming);
out_neigh:
if ((orig_neigh_node) && (!is_single_hop_neigh))
orig_node_free_ref(orig_neigh_node);
out:
+ if (router)
+ neigh_node_free_ref(router);
+ if (router_router)
+ neigh_node_free_ref(router_router);
+ if (orig_neigh_router)
+ neigh_node_free_ref(orig_neigh_router);
+
orig_node_free_ref(orig_node);
}
@@ -868,8 +880,9 @@ int recv_bat_packet(struct sk_buff *skb, struct hard_iface *hard_iface)
static int recv_my_icmp_packet(struct bat_priv *bat_priv,
struct sk_buff *skb, size_t icmp_len)
{
+ struct hard_iface *primary_if = NULL;
struct orig_node *orig_node = NULL;
- struct neigh_node *neigh_node = NULL;
+ struct neigh_node *router = NULL;
struct icmp_packet_rr *icmp_packet;
int ret = NET_RX_DROP;
@@ -881,28 +894,19 @@ static int recv_my_icmp_packet(struct bat_priv *bat_priv,
goto out;
}
- if (!bat_priv->primary_if)
+ primary_if = primary_if_get_selected(bat_priv);
+ if (!primary_if)
goto out;
/* answer echo request (ping) */
/* get routing information */
- rcu_read_lock();
orig_node = orig_hash_find(bat_priv, icmp_packet->orig);
-
if (!orig_node)
- goto unlock;
-
- neigh_node = orig_node->router;
-
- if (!neigh_node)
- goto unlock;
-
- if (!atomic_inc_not_zero(&neigh_node->refcount)) {
- neigh_node = NULL;
- goto unlock;
- }
+ goto out;
- rcu_read_unlock();
+ router = orig_node_get_router(orig_node);
+ if (!router)
+ goto out;
/* create a copy of the skb, if needed, to modify it. */
if (skb_cow(skb, sizeof(struct ethhdr)) < 0)
@@ -911,20 +915,18 @@ static int recv_my_icmp_packet(struct bat_priv *bat_priv,
icmp_packet = (struct icmp_packet_rr *)skb->data;
memcpy(icmp_packet->dst, icmp_packet->orig, ETH_ALEN);
- memcpy(icmp_packet->orig,
- bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN);
+ memcpy(icmp_packet->orig, primary_if->net_dev->dev_addr, ETH_ALEN);
icmp_packet->msg_type = ECHO_REPLY;
icmp_packet->ttl = TTL;
- send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr);
+ send_skb_packet(skb, router->if_incoming, router->addr);
ret = NET_RX_SUCCESS;
- goto out;
-unlock:
- rcu_read_unlock();
out:
- if (neigh_node)
- neigh_node_free_ref(neigh_node);
+ if (primary_if)
+ hardif_free_ref(primary_if);
+ if (router)
+ neigh_node_free_ref(router);
if (orig_node)
orig_node_free_ref(orig_node);
return ret;
@@ -933,8 +935,9 @@ out:
static int recv_icmp_ttl_exceeded(struct bat_priv *bat_priv,
struct sk_buff *skb)
{
+ struct hard_iface *primary_if = NULL;
struct orig_node *orig_node = NULL;
- struct neigh_node *neigh_node = NULL;
+ struct neigh_node *router = NULL;
struct icmp_packet *icmp_packet;
int ret = NET_RX_DROP;
@@ -948,27 +951,18 @@ static int recv_icmp_ttl_exceeded(struct bat_priv *bat_priv,
goto out;
}
- if (!bat_priv->primary_if)
+ primary_if = primary_if_get_selected(bat_priv);
+ if (!primary_if)
goto out;
/* get routing information */
- rcu_read_lock();
orig_node = orig_hash_find(bat_priv, icmp_packet->orig);
-
if (!orig_node)
- goto unlock;
-
- neigh_node = orig_node->router;
-
- if (!neigh_node)
- goto unlock;
-
- if (!atomic_inc_not_zero(&neigh_node->refcount)) {
- neigh_node = NULL;
- goto unlock;
- }
+ goto out;
- rcu_read_unlock();
+ router = orig_node_get_router(orig_node);
+ if (!router)
+ goto out;
/* create a copy of the skb, if needed, to modify it. */
if (skb_cow(skb, sizeof(struct ethhdr)) < 0)
@@ -977,20 +971,18 @@ static int recv_icmp_ttl_exceeded(struct bat_priv *bat_priv,
icmp_packet = (struct icmp_packet *)skb->data;
memcpy(icmp_packet->dst, icmp_packet->orig, ETH_ALEN);
- memcpy(icmp_packet->orig,
- bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN);
+ memcpy(icmp_packet->orig, primary_if->net_dev->dev_addr, ETH_ALEN);
icmp_packet->msg_type = TTL_EXCEEDED;
icmp_packet->ttl = TTL;
- send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr);
+ send_skb_packet(skb, router->if_incoming, router->addr);
ret = NET_RX_SUCCESS;
- goto out;
-unlock:
- rcu_read_unlock();
out:
- if (neigh_node)
- neigh_node_free_ref(neigh_node);
+ if (primary_if)
+ hardif_free_ref(primary_if);
+ if (router)
+ neigh_node_free_ref(router);
if (orig_node)
orig_node_free_ref(orig_node);
return ret;
@@ -1003,7 +995,7 @@ int recv_icmp_packet(struct sk_buff *skb, struct hard_iface *recv_if)
struct icmp_packet_rr *icmp_packet;
struct ethhdr *ethhdr;
struct orig_node *orig_node = NULL;
- struct neigh_node *neigh_node = NULL;
+ struct neigh_node *router = NULL;
int hdr_size = sizeof(struct icmp_packet);
int ret = NET_RX_DROP;
@@ -1050,23 +1042,13 @@ int recv_icmp_packet(struct sk_buff *skb, struct hard_iface *recv_if)
return recv_icmp_ttl_exceeded(bat_priv, skb);
/* get routing information */
- rcu_read_lock();
orig_node = orig_hash_find(bat_priv, icmp_packet->dst);
-
if (!orig_node)
- goto unlock;
-
- neigh_node = orig_node->router;
-
- if (!neigh_node)
- goto unlock;
-
- if (!atomic_inc_not_zero(&neigh_node->refcount)) {
- neigh_node = NULL;
- goto unlock;
- }
+ goto out;
- rcu_read_unlock();
+ router = orig_node_get_router(orig_node);
+ if (!router)
+ goto out;
/* create a copy of the skb, if needed, to modify it. */
if (skb_cow(skb, sizeof(struct ethhdr)) < 0)
@@ -1078,20 +1060,117 @@ int recv_icmp_packet(struct sk_buff *skb, struct hard_iface *recv_if)
icmp_packet->ttl--;
/* route it */
- send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr);
+ send_skb_packet(skb, router->if_incoming, router->addr);
ret = NET_RX_SUCCESS;
- goto out;
-unlock:
- rcu_read_unlock();
out:
- if (neigh_node)
- neigh_node_free_ref(neigh_node);
+ if (router)
+ neigh_node_free_ref(router);
if (orig_node)
orig_node_free_ref(orig_node);
return ret;
}
+/* In the bonding case, send the packets in a round
+ * robin fashion over the remaining interfaces.
+ *
+ * This method rotates the bonding list and increases the
+ * returned router's refcount. */
+static struct neigh_node *find_bond_router(struct orig_node *primary_orig,
+ struct hard_iface *recv_if)
+{
+ struct neigh_node *tmp_neigh_node;
+ struct neigh_node *router = NULL, *first_candidate = NULL;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(tmp_neigh_node, &primary_orig->bond_list,
+ bonding_list) {
+ if (!first_candidate)
+ first_candidate = tmp_neigh_node;
+
+ /* recv_if == NULL on the first node. */
+ if (tmp_neigh_node->if_incoming == recv_if)
+ continue;
+
+ if (!atomic_inc_not_zero(&tmp_neigh_node->refcount))
+ continue;
+
+ router = tmp_neigh_node;
+ break;
+ }
+
+ /* use the first candidate if nothing was found. */
+ if (!router && first_candidate &&
+ atomic_inc_not_zero(&first_candidate->refcount))
+ router = first_candidate;
+
+ if (!router)
+ goto out;
+
+ /* selected should point to the next element
+ * after the current router */
+ spin_lock_bh(&primary_orig->neigh_list_lock);
+ /* this is a list_move(), which unfortunately
+ * does not exist as rcu version */
+ list_del_rcu(&primary_orig->bond_list);
+ list_add_rcu(&primary_orig->bond_list,
+ &router->bonding_list);
+ spin_unlock_bh(&primary_orig->neigh_list_lock);
+
+out:
+ rcu_read_unlock();
+ return router;
+}
+
+/* Interface Alternating: Use the best of the
+ * remaining candidates which are not using
+ * this interface.
+ *
+ * Increases the returned router's refcount */
+static struct neigh_node *find_ifalter_router(struct orig_node *primary_orig,
+ struct hard_iface *recv_if)
+{
+ struct neigh_node *tmp_neigh_node;
+ struct neigh_node *router = NULL, *first_candidate = NULL;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(tmp_neigh_node, &primary_orig->bond_list,
+ bonding_list) {
+ if (!first_candidate)
+ first_candidate = tmp_neigh_node;
+
+ /* recv_if == NULL on the first node. */
+ if (tmp_neigh_node->if_incoming == recv_if)
+ continue;
+
+ if (!atomic_inc_not_zero(&tmp_neigh_node->refcount))
+ continue;
+
+ /* if we don't have a router yet
+ * or this one is better, choose it. */
+ if ((!router) ||
+ (tmp_neigh_node->tq_avg > router->tq_avg)) {
+ /* decrement refcount of
+ * previously selected router */
+ if (router)
+ neigh_node_free_ref(router);
+
+ router = tmp_neigh_node;
+ atomic_inc_not_zero(&router->refcount);
+ }
+
+ neigh_node_free_ref(tmp_neigh_node);
+ }
+
+ /* use the first candidate if nothing was found. */
+ if (!router && first_candidate &&
+ atomic_inc_not_zero(&first_candidate->refcount))
+ router = first_candidate;
+
+ rcu_read_unlock();
+ return router;
+}
+
/* find a suitable router for this originator, and use
* bonding if possible. increases the found neighbors
* refcount.*/
@@ -1101,15 +1180,16 @@ struct neigh_node *find_router(struct bat_priv *bat_priv,
{
struct orig_node *primary_orig_node;
struct orig_node *router_orig;
- struct neigh_node *router, *first_candidate, *tmp_neigh_node;
+ struct neigh_node *router;
static uint8_t zero_mac[ETH_ALEN] = {0, 0, 0, 0, 0, 0};
int bonding_enabled;
if (!orig_node)
return NULL;
- if (!orig_node->router)
- return NULL;
+ router = orig_node_get_router(orig_node);
+ if (!router)
+ goto err;
/* without bonding, the first node should
* always choose the default router. */
@@ -1117,12 +1197,9 @@ struct neigh_node *find_router(struct bat_priv *bat_priv,
rcu_read_lock();
/* select default router to output */
- router = orig_node->router;
- router_orig = orig_node->router->orig_node;
- if (!router_orig || !atomic_inc_not_zero(&router->refcount)) {
- rcu_read_unlock();
- return NULL;
- }
+ router_orig = router->orig_node;
+ if (!router_orig)
+ goto err_unlock;
if ((!recv_if) && (!bonding_enabled))
goto return_router;
@@ -1151,91 +1228,26 @@ struct neigh_node *find_router(struct bat_priv *bat_priv,
if (atomic_read(&primary_orig_node->bond_candidates) < 2)
goto return_router;
-
/* all nodes between should choose a candidate which
* is is not on the interface where the packet came
* in. */
neigh_node_free_ref(router);
- first_candidate = NULL;
- router = NULL;
-
- if (bonding_enabled) {
- /* in the bonding case, send the packets in a round
- * robin fashion over the remaining interfaces. */
-
- list_for_each_entry_rcu(tmp_neigh_node,
- &primary_orig_node->bond_list, bonding_list) {
- if (!first_candidate)
- first_candidate = tmp_neigh_node;
- /* recv_if == NULL on the first node. */
- if (tmp_neigh_node->if_incoming != recv_if &&
- atomic_inc_not_zero(&tmp_neigh_node->refcount)) {
- router = tmp_neigh_node;
- break;
- }
- }
-
- /* use the first candidate if nothing was found. */
- if (!router && first_candidate &&
- atomic_inc_not_zero(&first_candidate->refcount))
- router = first_candidate;
- if (!router) {
- rcu_read_unlock();
- return NULL;
- }
-
- /* selected should point to the next element
- * after the current router */
- spin_lock_bh(&primary_orig_node->neigh_list_lock);
- /* this is a list_move(), which unfortunately
- * does not exist as rcu version */
- list_del_rcu(&primary_orig_node->bond_list);
- list_add_rcu(&primary_orig_node->bond_list,
- &router->bonding_list);
- spin_unlock_bh(&primary_orig_node->neigh_list_lock);
-
- } else {
- /* if bonding is disabled, use the best of the
- * remaining candidates which are not using
- * this interface. */
- list_for_each_entry_rcu(tmp_neigh_node,
- &primary_orig_node->bond_list, bonding_list) {
- if (!first_candidate)
- first_candidate = tmp_neigh_node;
-
- /* recv_if == NULL on the first node. */
- if (tmp_neigh_node->if_incoming == recv_if)
- continue;
-
- if (!atomic_inc_not_zero(&tmp_neigh_node->refcount))
- continue;
-
- /* if we don't have a router yet
- * or this one is better, choose it. */
- if ((!router) ||
- (tmp_neigh_node->tq_avg > router->tq_avg)) {
- /* decrement refcount of
- * previously selected router */
- if (router)
- neigh_node_free_ref(router);
-
- router = tmp_neigh_node;
- atomic_inc_not_zero(&router->refcount);
- }
-
- neigh_node_free_ref(tmp_neigh_node);
- }
+ if (bonding_enabled)
+ router = find_bond_router(primary_orig_node, recv_if);
+ else
+ router = find_ifalter_router(primary_orig_node, recv_if);
- /* use the first candidate if nothing was found. */
- if (!router && first_candidate &&
- atomic_inc_not_zero(&first_candidate->refcount))
- router = first_candidate;
- }
return_router:
rcu_read_unlock();
return router;
+err_unlock:
+ rcu_read_unlock();
+err:
+ if (router)
+ neigh_node_free_ref(router);
+ return NULL;
}
static int check_unicast_packet(struct sk_buff *skb, int hdr_size)
@@ -1284,13 +1296,10 @@ int route_unicast_packet(struct sk_buff *skb, struct hard_iface *recv_if)
}
/* get routing information */
- rcu_read_lock();
orig_node = orig_hash_find(bat_priv, unicast_packet->dest);
if (!orig_node)
- goto unlock;
-
- rcu_read_unlock();
+ goto out;
/* find_router() increases neigh_nodes refcount if found. */
neigh_node = find_router(bat_priv, orig_node, recv_if);
@@ -1336,10 +1345,7 @@ int route_unicast_packet(struct sk_buff *skb, struct hard_iface *recv_if)
/* route it */
send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr);
ret = NET_RX_SUCCESS;
- goto out;
-unlock:
- rcu_read_unlock();
out:
if (neigh_node)
neigh_node_free_ref(neigh_node);
@@ -1438,13 +1444,10 @@ int recv_bcast_packet(struct sk_buff *skb, struct hard_iface *recv_if)
if (bcast_packet->ttl < 2)
goto out;
- rcu_read_lock();
orig_node = orig_hash_find(bat_priv, bcast_packet->orig);
if (!orig_node)
- goto rcu_unlock;
-
- rcu_read_unlock();
+ goto out;
spin_lock_bh(&orig_node->bcast_seqno_lock);
@@ -1475,9 +1478,6 @@ int recv_bcast_packet(struct sk_buff *skb, struct hard_iface *recv_if)
ret = NET_RX_SUCCESS;
goto out;
-rcu_unlock:
- rcu_read_unlock();
- goto out;
spin_unlock:
spin_unlock_bh(&orig_node->bcast_seqno_lock);
out:
diff --git a/net/batman-adv/routing.h b/net/batman-adv/routing.h
index b5a064c88a4..870f29842b2 100644
--- a/net/batman-adv/routing.h
+++ b/net/batman-adv/routing.h
@@ -25,11 +25,11 @@
void slide_own_bcast_window(struct hard_iface *hard_iface);
void receive_bat_packet(struct ethhdr *ethhdr,
struct batman_packet *batman_packet,
- unsigned char *hna_buff, int hna_buff_len,
+ unsigned char *tt_buff, int tt_buff_len,
struct hard_iface *if_incoming);
void update_routes(struct bat_priv *bat_priv, struct orig_node *orig_node,
- struct neigh_node *neigh_node, unsigned char *hna_buff,
- int hna_buff_len);
+ struct neigh_node *neigh_node, unsigned char *tt_buff,
+ int tt_buff_len);
int route_unicast_packet(struct sk_buff *skb, struct hard_iface *recv_if);
int recv_icmp_packet(struct sk_buff *skb, struct hard_iface *recv_if);
int recv_unicast_packet(struct sk_buff *skb, struct hard_iface *recv_if);
diff --git a/net/batman-adv/send.c b/net/batman-adv/send.c
index d49e54d932a..33779278f1b 100644
--- a/net/batman-adv/send.c
+++ b/net/batman-adv/send.c
@@ -121,7 +121,7 @@ static void send_packet_to_if(struct forw_packet *forw_packet,
/* adjust all flags and log packets */
while (aggregated_packet(buff_pos,
forw_packet->packet_len,
- batman_packet->num_hna)) {
+ batman_packet->num_tt)) {
/* we might have aggregated direct link packets with an
* ordinary base packet */
@@ -146,7 +146,7 @@ static void send_packet_to_if(struct forw_packet *forw_packet,
hard_iface->net_dev->dev_addr);
buff_pos += sizeof(struct batman_packet) +
- (batman_packet->num_hna * ETH_ALEN);
+ (batman_packet->num_tt * ETH_ALEN);
packet_num++;
batman_packet = (struct batman_packet *)
(forw_packet->skb->data + buff_pos);
@@ -222,7 +222,7 @@ static void rebuild_batman_packet(struct bat_priv *bat_priv,
struct batman_packet *batman_packet;
new_len = sizeof(struct batman_packet) +
- (bat_priv->num_local_hna * ETH_ALEN);
+ (bat_priv->num_local_tt * ETH_ALEN);
new_buff = kmalloc(new_len, GFP_ATOMIC);
/* keep old buffer if kmalloc should fail */
@@ -231,7 +231,7 @@ static void rebuild_batman_packet(struct bat_priv *bat_priv,
sizeof(struct batman_packet));
batman_packet = (struct batman_packet *)new_buff;
- batman_packet->num_hna = hna_local_fill_buffer(bat_priv,
+ batman_packet->num_tt = tt_local_fill_buffer(bat_priv,
new_buff + sizeof(struct batman_packet),
new_len - sizeof(struct batman_packet));
@@ -244,6 +244,7 @@ static void rebuild_batman_packet(struct bat_priv *bat_priv,
void schedule_own_packet(struct hard_iface *hard_iface)
{
struct bat_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
+ struct hard_iface *primary_if;
unsigned long send_time;
struct batman_packet *batman_packet;
int vis_server;
@@ -253,6 +254,7 @@ void schedule_own_packet(struct hard_iface *hard_iface)
return;
vis_server = atomic_read(&bat_priv->vis_mode);
+ primary_if = primary_if_get_selected(bat_priv);
/**
* the interface gets activated here to avoid race conditions between
@@ -264,9 +266,9 @@ void schedule_own_packet(struct hard_iface *hard_iface)
if (hard_iface->if_status == IF_TO_BE_ACTIVATED)
hard_iface->if_status = IF_ACTIVE;
- /* if local hna has changed and interface is a primary interface */
- if ((atomic_read(&bat_priv->hna_local_changed)) &&
- (hard_iface == bat_priv->primary_if))
+ /* if local tt has changed and interface is a primary interface */
+ if ((atomic_read(&bat_priv->tt_local_changed)) &&
+ (hard_iface == primary_if))
rebuild_batman_packet(bat_priv, hard_iface);
/**
@@ -284,7 +286,7 @@ void schedule_own_packet(struct hard_iface *hard_iface)
else
batman_packet->flags &= ~VIS_SERVER;
- if ((hard_iface == bat_priv->primary_if) &&
+ if ((hard_iface == primary_if) &&
(atomic_read(&bat_priv->gw_mode) == GW_MODE_SERVER))
batman_packet->gw_flags =
(uint8_t)atomic_read(&bat_priv->gw_bandwidth);
@@ -299,15 +301,19 @@ void schedule_own_packet(struct hard_iface *hard_iface)
hard_iface->packet_buff,
hard_iface->packet_len,
hard_iface, 1, send_time);
+
+ if (primary_if)
+ hardif_free_ref(primary_if);
}
void schedule_forward_packet(struct orig_node *orig_node,
struct ethhdr *ethhdr,
struct batman_packet *batman_packet,
- uint8_t directlink, int hna_buff_len,
+ uint8_t directlink, int tt_buff_len,
struct hard_iface *if_incoming)
{
struct bat_priv *bat_priv = netdev_priv(if_incoming->soft_iface);
+ struct neigh_node *router;
unsigned char in_tq, in_ttl, tq_avg = 0;
unsigned long send_time;
@@ -316,6 +322,8 @@ void schedule_forward_packet(struct orig_node *orig_node,
return;
}
+ router = orig_node_get_router(orig_node);
+
in_tq = batman_packet->tq;
in_ttl = batman_packet->ttl;
@@ -324,20 +332,22 @@ void schedule_forward_packet(struct orig_node *orig_node,
/* rebroadcast tq of our best ranking neighbor to ensure the rebroadcast
* of our best tq value */
- if ((orig_node->router) && (orig_node->router->tq_avg != 0)) {
+ if (router && router->tq_avg != 0) {
/* rebroadcast ogm of best ranking neighbor as is */
- if (!compare_eth(orig_node->router->addr, ethhdr->h_source)) {
- batman_packet->tq = orig_node->router->tq_avg;
+ if (!compare_eth(router->addr, ethhdr->h_source)) {
+ batman_packet->tq = router->tq_avg;
- if (orig_node->router->last_ttl)
- batman_packet->ttl = orig_node->router->last_ttl
- - 1;
+ if (router->last_ttl)
+ batman_packet->ttl = router->last_ttl - 1;
}
- tq_avg = orig_node->router->tq_avg;
+ tq_avg = router->tq_avg;
}
+ if (router)
+ neigh_node_free_ref(router);
+
/* apply hop penalty */
batman_packet->tq = hop_penalty(batman_packet->tq, bat_priv);
@@ -359,7 +369,7 @@ void schedule_forward_packet(struct orig_node *orig_node,
send_time = forward_send_time();
add_bat_packet_to_list(bat_priv,
(unsigned char *)batman_packet,
- sizeof(struct batman_packet) + hna_buff_len,
+ sizeof(struct batman_packet) + tt_buff_len,
if_incoming, 0, send_time);
}
@@ -367,6 +377,8 @@ static void forw_packet_free(struct forw_packet *forw_packet)
{
if (forw_packet->skb)
kfree_skb(forw_packet->skb);
+ if (forw_packet->if_incoming)
+ hardif_free_ref(forw_packet->if_incoming);
kfree(forw_packet);
}
@@ -388,7 +400,6 @@ static void _add_bcast_packet_to_list(struct bat_priv *bat_priv,
send_time);
}
-#define atomic_dec_not_zero(v) atomic_add_unless((v), -1, 0)
/* add a broadcast packet to the queue and setup timers. broadcast packets
* are sent multiple times to increase probability for beeing received.
*
@@ -399,6 +410,7 @@ static void _add_bcast_packet_to_list(struct bat_priv *bat_priv,
* skb is freed. */
int add_bcast_packet_to_list(struct bat_priv *bat_priv, struct sk_buff *skb)
{
+ struct hard_iface *primary_if = NULL;
struct forw_packet *forw_packet;
struct bcast_packet *bcast_packet;
@@ -407,8 +419,9 @@ int add_bcast_packet_to_list(struct bat_priv *bat_priv, struct sk_buff *skb)
goto out;
}
- if (!bat_priv->primary_if)
- goto out;
+ primary_if = primary_if_get_selected(bat_priv);
+ if (!primary_if)
+ goto out_and_inc;
forw_packet = kmalloc(sizeof(struct forw_packet), GFP_ATOMIC);
@@ -426,7 +439,7 @@ int add_bcast_packet_to_list(struct bat_priv *bat_priv, struct sk_buff *skb)
skb_reset_mac_header(skb);
forw_packet->skb = skb;
- forw_packet->if_incoming = bat_priv->primary_if;
+ forw_packet->if_incoming = primary_if;
/* how often did we send the bcast packet ? */
forw_packet->num_packets = 0;
@@ -439,6 +452,8 @@ packet_free:
out_and_inc:
atomic_inc(&bat_priv->bcast_queue_left);
out:
+ if (primary_if)
+ hardif_free_ref(primary_if);
return NETDEV_TX_BUSY;
}
@@ -526,6 +541,7 @@ void purge_outstanding_packets(struct bat_priv *bat_priv,
{
struct forw_packet *forw_packet;
struct hlist_node *tmp_node, *safe_tmp_node;
+ bool pending;
if (hard_iface)
bat_dbg(DBG_BATMAN, bat_priv,
@@ -554,8 +570,13 @@ void purge_outstanding_packets(struct bat_priv *bat_priv,
* send_outstanding_bcast_packet() will lock the list to
* delete the item from the list
*/
- cancel_delayed_work_sync(&forw_packet->delayed_work);
+ pending = cancel_delayed_work_sync(&forw_packet->delayed_work);
spin_lock_bh(&bat_priv->forw_bcast_list_lock);
+
+ if (pending) {
+ hlist_del(&forw_packet->list);
+ forw_packet_free(forw_packet);
+ }
}
spin_unlock_bh(&bat_priv->forw_bcast_list_lock);
@@ -578,8 +599,13 @@ void purge_outstanding_packets(struct bat_priv *bat_priv,
* send_outstanding_bat_packet() will lock the list to
* delete the item from the list
*/
- cancel_delayed_work_sync(&forw_packet->delayed_work);
+ pending = cancel_delayed_work_sync(&forw_packet->delayed_work);
spin_lock_bh(&bat_priv->forw_bat_list_lock);
+
+ if (pending) {
+ hlist_del(&forw_packet->list);
+ forw_packet_free(forw_packet);
+ }
}
spin_unlock_bh(&bat_priv->forw_bat_list_lock);
}
diff --git a/net/batman-adv/send.h b/net/batman-adv/send.h
index 7b2ff19c05e..247172d71e4 100644
--- a/net/batman-adv/send.h
+++ b/net/batman-adv/send.h
@@ -29,7 +29,7 @@ void schedule_own_packet(struct hard_iface *hard_iface);
void schedule_forward_packet(struct orig_node *orig_node,
struct ethhdr *ethhdr,
struct batman_packet *batman_packet,
- uint8_t directlink, int hna_buff_len,
+ uint8_t directlink, int tt_buff_len,
struct hard_iface *if_outgoing);
int add_bcast_packet_to_list(struct bat_priv *bat_priv, struct sk_buff *skb);
void send_outstanding_bat_packet(struct work_struct *work);
diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c
index 04efe022c13..d5aa60999e8 100644
--- a/net/batman-adv/soft-interface.c
+++ b/net/batman-adv/soft-interface.c
@@ -43,8 +43,6 @@ static void bat_get_drvinfo(struct net_device *dev,
static u32 bat_get_msglevel(struct net_device *dev);
static void bat_set_msglevel(struct net_device *dev, u32 value);
static u32 bat_get_link(struct net_device *dev);
-static u32 bat_get_rx_csum(struct net_device *dev);
-static int bat_set_rx_csum(struct net_device *dev, u32 data);
static const struct ethtool_ops bat_ethtool_ops = {
.get_settings = bat_get_settings,
@@ -52,8 +50,6 @@ static const struct ethtool_ops bat_ethtool_ops = {
.get_msglevel = bat_get_msglevel,
.set_msglevel = bat_set_msglevel,
.get_link = bat_get_link,
- .get_rx_csum = bat_get_rx_csum,
- .set_rx_csum = bat_set_rx_csum
};
int my_skb_head_push(struct sk_buff *skb, unsigned int len)
@@ -82,106 +78,365 @@ static void softif_neigh_free_ref(struct softif_neigh *softif_neigh)
kfree_rcu(softif_neigh, rcu);
}
-void softif_neigh_purge(struct bat_priv *bat_priv)
+static void softif_neigh_vid_free_rcu(struct rcu_head *rcu)
{
- struct softif_neigh *softif_neigh, *softif_neigh_tmp;
+ struct softif_neigh_vid *softif_neigh_vid;
+ struct softif_neigh *softif_neigh;
struct hlist_node *node, *node_tmp;
+ struct bat_priv *bat_priv;
- spin_lock_bh(&bat_priv->softif_neigh_lock);
+ softif_neigh_vid = container_of(rcu, struct softif_neigh_vid, rcu);
+ bat_priv = softif_neigh_vid->bat_priv;
+ spin_lock_bh(&bat_priv->softif_neigh_lock);
hlist_for_each_entry_safe(softif_neigh, node, node_tmp,
- &bat_priv->softif_neigh_list, list) {
+ &softif_neigh_vid->softif_neigh_list, list) {
+ hlist_del_rcu(&softif_neigh->list);
+ softif_neigh_free_ref(softif_neigh);
+ }
+ spin_unlock_bh(&bat_priv->softif_neigh_lock);
- if ((!time_after(jiffies, softif_neigh->last_seen +
- msecs_to_jiffies(SOFTIF_NEIGH_TIMEOUT))) &&
- (atomic_read(&bat_priv->mesh_state) == MESH_ACTIVE))
- continue;
+ kfree(softif_neigh_vid);
+}
- hlist_del_rcu(&softif_neigh->list);
+static void softif_neigh_vid_free_ref(struct softif_neigh_vid *softif_neigh_vid)
+{
+ if (atomic_dec_and_test(&softif_neigh_vid->refcount))
+ call_rcu(&softif_neigh_vid->rcu, softif_neigh_vid_free_rcu);
+}
- if (bat_priv->softif_neigh == softif_neigh) {
- bat_dbg(DBG_ROUTES, bat_priv,
- "Current mesh exit point '%pM' vanished "
- "(vid: %d).\n",
- softif_neigh->addr, softif_neigh->vid);
- softif_neigh_tmp = bat_priv->softif_neigh;
- bat_priv->softif_neigh = NULL;
- softif_neigh_free_ref(softif_neigh_tmp);
- }
+static struct softif_neigh_vid *softif_neigh_vid_get(struct bat_priv *bat_priv,
+ short vid)
+{
+ struct softif_neigh_vid *softif_neigh_vid;
+ struct hlist_node *node;
- softif_neigh_free_ref(softif_neigh);
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(softif_neigh_vid, node,
+ &bat_priv->softif_neigh_vids, list) {
+ if (softif_neigh_vid->vid != vid)
+ continue;
+
+ if (!atomic_inc_not_zero(&softif_neigh_vid->refcount))
+ continue;
+
+ goto out;
}
- spin_unlock_bh(&bat_priv->softif_neigh_lock);
+ softif_neigh_vid = kzalloc(sizeof(struct softif_neigh_vid),
+ GFP_ATOMIC);
+ if (!softif_neigh_vid)
+ goto out;
+
+ softif_neigh_vid->vid = vid;
+ softif_neigh_vid->bat_priv = bat_priv;
+
+ /* initialize with 2 - caller decrements counter by one */
+ atomic_set(&softif_neigh_vid->refcount, 2);
+ INIT_HLIST_HEAD(&softif_neigh_vid->softif_neigh_list);
+ INIT_HLIST_NODE(&softif_neigh_vid->list);
+ spin_lock_bh(&bat_priv->softif_neigh_vid_lock);
+ hlist_add_head_rcu(&softif_neigh_vid->list,
+ &bat_priv->softif_neigh_vids);
+ spin_unlock_bh(&bat_priv->softif_neigh_vid_lock);
+
+out:
+ rcu_read_unlock();
+ return softif_neigh_vid;
}
static struct softif_neigh *softif_neigh_get(struct bat_priv *bat_priv,
uint8_t *addr, short vid)
{
- struct softif_neigh *softif_neigh;
+ struct softif_neigh_vid *softif_neigh_vid;
+ struct softif_neigh *softif_neigh = NULL;
struct hlist_node *node;
+ softif_neigh_vid = softif_neigh_vid_get(bat_priv, vid);
+ if (!softif_neigh_vid)
+ goto out;
+
rcu_read_lock();
hlist_for_each_entry_rcu(softif_neigh, node,
- &bat_priv->softif_neigh_list, list) {
+ &softif_neigh_vid->softif_neigh_list,
+ list) {
if (!compare_eth(softif_neigh->addr, addr))
continue;
- if (softif_neigh->vid != vid)
- continue;
-
if (!atomic_inc_not_zero(&softif_neigh->refcount))
continue;
softif_neigh->last_seen = jiffies;
- goto out;
+ goto unlock;
}
softif_neigh = kzalloc(sizeof(struct softif_neigh), GFP_ATOMIC);
if (!softif_neigh)
- goto out;
+ goto unlock;
memcpy(softif_neigh->addr, addr, ETH_ALEN);
- softif_neigh->vid = vid;
softif_neigh->last_seen = jiffies;
/* initialize with 2 - caller decrements counter by one */
atomic_set(&softif_neigh->refcount, 2);
INIT_HLIST_NODE(&softif_neigh->list);
spin_lock_bh(&bat_priv->softif_neigh_lock);
- hlist_add_head_rcu(&softif_neigh->list, &bat_priv->softif_neigh_list);
+ hlist_add_head_rcu(&softif_neigh->list,
+ &softif_neigh_vid->softif_neigh_list);
spin_unlock_bh(&bat_priv->softif_neigh_lock);
+unlock:
+ rcu_read_unlock();
out:
+ if (softif_neigh_vid)
+ softif_neigh_vid_free_ref(softif_neigh_vid);
+ return softif_neigh;
+}
+
+static struct softif_neigh *softif_neigh_get_selected(
+ struct softif_neigh_vid *softif_neigh_vid)
+{
+ struct softif_neigh *softif_neigh;
+
+ rcu_read_lock();
+ softif_neigh = rcu_dereference(softif_neigh_vid->softif_neigh);
+
+ if (softif_neigh && !atomic_inc_not_zero(&softif_neigh->refcount))
+ softif_neigh = NULL;
+
rcu_read_unlock();
return softif_neigh;
}
+static struct softif_neigh *softif_neigh_vid_get_selected(
+ struct bat_priv *bat_priv,
+ short vid)
+{
+ struct softif_neigh_vid *softif_neigh_vid;
+ struct softif_neigh *softif_neigh = NULL;
+
+ softif_neigh_vid = softif_neigh_vid_get(bat_priv, vid);
+ if (!softif_neigh_vid)
+ goto out;
+
+ softif_neigh = softif_neigh_get_selected(softif_neigh_vid);
+out:
+ if (softif_neigh_vid)
+ softif_neigh_vid_free_ref(softif_neigh_vid);
+ return softif_neigh;
+}
+
+static void softif_neigh_vid_select(struct bat_priv *bat_priv,
+ struct softif_neigh *new_neigh,
+ short vid)
+{
+ struct softif_neigh_vid *softif_neigh_vid;
+ struct softif_neigh *curr_neigh;
+
+ softif_neigh_vid = softif_neigh_vid_get(bat_priv, vid);
+ if (!softif_neigh_vid)
+ goto out;
+
+ spin_lock_bh(&bat_priv->softif_neigh_lock);
+
+ if (new_neigh && !atomic_inc_not_zero(&new_neigh->refcount))
+ new_neigh = NULL;
+
+ curr_neigh = softif_neigh_vid->softif_neigh;
+ rcu_assign_pointer(softif_neigh_vid->softif_neigh, new_neigh);
+
+ if ((curr_neigh) && (!new_neigh))
+ bat_dbg(DBG_ROUTES, bat_priv,
+ "Removing mesh exit point on vid: %d (prev: %pM).\n",
+ vid, curr_neigh->addr);
+ else if ((curr_neigh) && (new_neigh))
+ bat_dbg(DBG_ROUTES, bat_priv,
+ "Changing mesh exit point on vid: %d from %pM "
+ "to %pM.\n", vid, curr_neigh->addr, new_neigh->addr);
+ else if ((!curr_neigh) && (new_neigh))
+ bat_dbg(DBG_ROUTES, bat_priv,
+ "Setting mesh exit point on vid: %d to %pM.\n",
+ vid, new_neigh->addr);
+
+ if (curr_neigh)
+ softif_neigh_free_ref(curr_neigh);
+
+ spin_unlock_bh(&bat_priv->softif_neigh_lock);
+
+out:
+ if (softif_neigh_vid)
+ softif_neigh_vid_free_ref(softif_neigh_vid);
+}
+
+static void softif_neigh_vid_deselect(struct bat_priv *bat_priv,
+ struct softif_neigh_vid *softif_neigh_vid)
+{
+ struct softif_neigh *curr_neigh;
+ struct softif_neigh *softif_neigh = NULL, *softif_neigh_tmp;
+ struct hard_iface *primary_if = NULL;
+ struct hlist_node *node;
+
+ primary_if = primary_if_get_selected(bat_priv);
+ if (!primary_if)
+ goto out;
+
+ /* find new softif_neigh immediately to avoid temporary loops */
+ rcu_read_lock();
+ curr_neigh = rcu_dereference(softif_neigh_vid->softif_neigh);
+
+ hlist_for_each_entry_rcu(softif_neigh_tmp, node,
+ &softif_neigh_vid->softif_neigh_list,
+ list) {
+ if (softif_neigh_tmp == curr_neigh)
+ continue;
+
+ /* we got a neighbor but its mac is 'bigger' than ours */
+ if (memcmp(primary_if->net_dev->dev_addr,
+ softif_neigh_tmp->addr, ETH_ALEN) < 0)
+ continue;
+
+ if (!atomic_inc_not_zero(&softif_neigh_tmp->refcount))
+ continue;
+
+ softif_neigh = softif_neigh_tmp;
+ goto unlock;
+ }
+
+unlock:
+ rcu_read_unlock();
+out:
+ softif_neigh_vid_select(bat_priv, softif_neigh, softif_neigh_vid->vid);
+
+ if (primary_if)
+ hardif_free_ref(primary_if);
+ if (softif_neigh)
+ softif_neigh_free_ref(softif_neigh);
+}
+
int softif_neigh_seq_print_text(struct seq_file *seq, void *offset)
{
struct net_device *net_dev = (struct net_device *)seq->private;
struct bat_priv *bat_priv = netdev_priv(net_dev);
+ struct softif_neigh_vid *softif_neigh_vid;
struct softif_neigh *softif_neigh;
- struct hlist_node *node;
+ struct hard_iface *primary_if;
+ struct hlist_node *node, *node_tmp;
+ struct softif_neigh *curr_softif_neigh;
+ int ret = 0, last_seen_secs, last_seen_msecs;
+
+ primary_if = primary_if_get_selected(bat_priv);
+ if (!primary_if) {
+ ret = seq_printf(seq, "BATMAN mesh %s disabled - "
+ "please specify interfaces to enable it\n",
+ net_dev->name);
+ goto out;
+ }
- if (!bat_priv->primary_if) {
- return seq_printf(seq, "BATMAN mesh %s disabled - "
- "please specify interfaces to enable it\n",
- net_dev->name);
+ if (primary_if->if_status != IF_ACTIVE) {
+ ret = seq_printf(seq, "BATMAN mesh %s "
+ "disabled - primary interface not active\n",
+ net_dev->name);
+ goto out;
}
seq_printf(seq, "Softif neighbor list (%s)\n", net_dev->name);
rcu_read_lock();
- hlist_for_each_entry_rcu(softif_neigh, node,
- &bat_priv->softif_neigh_list, list)
- seq_printf(seq, "%s %pM (vid: %d)\n",
- bat_priv->softif_neigh == softif_neigh
- ? "=>" : " ", softif_neigh->addr,
- softif_neigh->vid);
+ hlist_for_each_entry_rcu(softif_neigh_vid, node,
+ &bat_priv->softif_neigh_vids, list) {
+ seq_printf(seq, " %-15s %s on vid: %d\n",
+ "Originator", "last-seen", softif_neigh_vid->vid);
+
+ curr_softif_neigh = softif_neigh_get_selected(softif_neigh_vid);
+
+ hlist_for_each_entry_rcu(softif_neigh, node_tmp,
+ &softif_neigh_vid->softif_neigh_list,
+ list) {
+ last_seen_secs = jiffies_to_msecs(jiffies -
+ softif_neigh->last_seen) / 1000;
+ last_seen_msecs = jiffies_to_msecs(jiffies -
+ softif_neigh->last_seen) % 1000;
+ seq_printf(seq, "%s %pM %3i.%03is\n",
+ curr_softif_neigh == softif_neigh
+ ? "=>" : " ", softif_neigh->addr,
+ last_seen_secs, last_seen_msecs);
+ }
+
+ if (curr_softif_neigh)
+ softif_neigh_free_ref(curr_softif_neigh);
+
+ seq_printf(seq, "\n");
+ }
rcu_read_unlock();
- return 0;
+out:
+ if (primary_if)
+ hardif_free_ref(primary_if);
+ return ret;
+}
+
+void softif_neigh_purge(struct bat_priv *bat_priv)
+{
+ struct softif_neigh *softif_neigh, *curr_softif_neigh;
+ struct softif_neigh_vid *softif_neigh_vid;
+ struct hlist_node *node, *node_tmp, *node_tmp2;
+ char do_deselect;
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(softif_neigh_vid, node,
+ &bat_priv->softif_neigh_vids, list) {
+ if (!atomic_inc_not_zero(&softif_neigh_vid->refcount))
+ continue;
+
+ curr_softif_neigh = softif_neigh_get_selected(softif_neigh_vid);
+ do_deselect = 0;
+
+ spin_lock_bh(&bat_priv->softif_neigh_lock);
+ hlist_for_each_entry_safe(softif_neigh, node_tmp, node_tmp2,
+ &softif_neigh_vid->softif_neigh_list,
+ list) {
+ if ((!time_after(jiffies, softif_neigh->last_seen +
+ msecs_to_jiffies(SOFTIF_NEIGH_TIMEOUT))) &&
+ (atomic_read(&bat_priv->mesh_state) == MESH_ACTIVE))
+ continue;
+
+ if (curr_softif_neigh == softif_neigh) {
+ bat_dbg(DBG_ROUTES, bat_priv,
+ "Current mesh exit point on vid: %d "
+ "'%pM' vanished.\n",
+ softif_neigh_vid->vid,
+ softif_neigh->addr);
+ do_deselect = 1;
+ }
+
+ hlist_del_rcu(&softif_neigh->list);
+ softif_neigh_free_ref(softif_neigh);
+ }
+ spin_unlock_bh(&bat_priv->softif_neigh_lock);
+
+ /* soft_neigh_vid_deselect() needs to acquire the
+ * softif_neigh_lock */
+ if (do_deselect)
+ softif_neigh_vid_deselect(bat_priv, softif_neigh_vid);
+
+ if (curr_softif_neigh)
+ softif_neigh_free_ref(curr_softif_neigh);
+
+ softif_neigh_vid_free_ref(softif_neigh_vid);
+ }
+ rcu_read_unlock();
+
+ spin_lock_bh(&bat_priv->softif_neigh_vid_lock);
+ hlist_for_each_entry_safe(softif_neigh_vid, node, node_tmp,
+ &bat_priv->softif_neigh_vids, list) {
+ if (!hlist_empty(&softif_neigh_vid->softif_neigh_list))
+ continue;
+
+ hlist_del_rcu(&softif_neigh_vid->list);
+ softif_neigh_vid_free_ref(softif_neigh_vid);
+ }
+ spin_unlock_bh(&bat_priv->softif_neigh_vid_lock);
+
}
static void softif_batman_recv(struct sk_buff *skb, struct net_device *dev,
@@ -190,7 +445,9 @@ static void softif_batman_recv(struct sk_buff *skb, struct net_device *dev,
struct bat_priv *bat_priv = netdev_priv(dev);
struct ethhdr *ethhdr = (struct ethhdr *)skb->data;
struct batman_packet *batman_packet;
- struct softif_neigh *softif_neigh, *softif_neigh_tmp;
+ struct softif_neigh *softif_neigh = NULL;
+ struct hard_iface *primary_if = NULL;
+ struct softif_neigh *curr_softif_neigh = NULL;
if (ntohs(ethhdr->h_proto) == ETH_P_8021Q)
batman_packet = (struct batman_packet *)
@@ -199,63 +456,52 @@ static void softif_batman_recv(struct sk_buff *skb, struct net_device *dev,
batman_packet = (struct batman_packet *)(skb->data + ETH_HLEN);
if (batman_packet->version != COMPAT_VERSION)
- goto err;
+ goto out;
if (batman_packet->packet_type != BAT_PACKET)
- goto err;
+ goto out;
if (!(batman_packet->flags & PRIMARIES_FIRST_HOP))
- goto err;
+ goto out;
if (is_my_mac(batman_packet->orig))
- goto err;
+ goto out;
softif_neigh = softif_neigh_get(bat_priv, batman_packet->orig, vid);
-
if (!softif_neigh)
- goto err;
+ goto out;
- if (bat_priv->softif_neigh == softif_neigh)
+ curr_softif_neigh = softif_neigh_vid_get_selected(bat_priv, vid);
+ if (curr_softif_neigh == softif_neigh)
+ goto out;
+
+ primary_if = primary_if_get_selected(bat_priv);
+ if (!primary_if)
goto out;
/* we got a neighbor but its mac is 'bigger' than ours */
- if (memcmp(bat_priv->primary_if->net_dev->dev_addr,
+ if (memcmp(primary_if->net_dev->dev_addr,
softif_neigh->addr, ETH_ALEN) < 0)
goto out;
- /* switch to new 'smallest neighbor' */
- if ((bat_priv->softif_neigh) &&
- (memcmp(softif_neigh->addr, bat_priv->softif_neigh->addr,
- ETH_ALEN) < 0)) {
- bat_dbg(DBG_ROUTES, bat_priv,
- "Changing mesh exit point from %pM (vid: %d) "
- "to %pM (vid: %d).\n",
- bat_priv->softif_neigh->addr,
- bat_priv->softif_neigh->vid,
- softif_neigh->addr, softif_neigh->vid);
- softif_neigh_tmp = bat_priv->softif_neigh;
- bat_priv->softif_neigh = softif_neigh;
- softif_neigh_free_ref(softif_neigh_tmp);
- /* we need to hold the additional reference */
- goto err;
- }
-
/* close own batX device and use softif_neigh as exit node */
- if ((!bat_priv->softif_neigh) &&
- (memcmp(softif_neigh->addr,
- bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN) < 0)) {
- bat_dbg(DBG_ROUTES, bat_priv,
- "Setting mesh exit point to %pM (vid: %d).\n",
- softif_neigh->addr, softif_neigh->vid);
- bat_priv->softif_neigh = softif_neigh;
- /* we need to hold the additional reference */
- goto err;
+ if (!curr_softif_neigh) {
+ softif_neigh_vid_select(bat_priv, softif_neigh, vid);
+ goto out;
}
+ /* switch to new 'smallest neighbor' */
+ if (memcmp(softif_neigh->addr, curr_softif_neigh->addr, ETH_ALEN) < 0)
+ softif_neigh_vid_select(bat_priv, softif_neigh, vid);
+
out:
- softif_neigh_free_ref(softif_neigh);
-err:
kfree_skb(skb);
+ if (softif_neigh)
+ softif_neigh_free_ref(softif_neigh);
+ if (curr_softif_neigh)
+ softif_neigh_free_ref(curr_softif_neigh);
+ if (primary_if)
+ hardif_free_ref(primary_if);
return;
}
@@ -285,11 +531,11 @@ static int interface_set_mac_addr(struct net_device *dev, void *p)
if (!is_valid_ether_addr(addr->sa_data))
return -EADDRNOTAVAIL;
- /* only modify hna-table if it has been initialised before */
+ /* only modify transtable if it has been initialised before */
if (atomic_read(&bat_priv->mesh_state) == MESH_ACTIVE) {
- hna_local_remove(bat_priv, dev->dev_addr,
+ tt_local_remove(bat_priv, dev->dev_addr,
"mac address changed");
- hna_local_add(dev, addr->sa_data);
+ tt_local_add(dev, addr->sa_data);
}
memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN);
@@ -311,8 +557,10 @@ int interface_tx(struct sk_buff *skb, struct net_device *soft_iface)
{
struct ethhdr *ethhdr = (struct ethhdr *)skb->data;
struct bat_priv *bat_priv = netdev_priv(soft_iface);
+ struct hard_iface *primary_if = NULL;
struct bcast_packet *bcast_packet;
struct vlan_ethhdr *vhdr;
+ struct softif_neigh *curr_softif_neigh = NULL;
int data_len = skb->len, ret;
short vid = -1;
bool do_bcast = false;
@@ -340,11 +588,12 @@ int interface_tx(struct sk_buff *skb, struct net_device *soft_iface)
* if we have a another chosen mesh exit node in range
* it will transport the packets to the mesh
*/
- if ((bat_priv->softif_neigh) && (bat_priv->softif_neigh->vid == vid))
+ curr_softif_neigh = softif_neigh_vid_get_selected(bat_priv, vid);
+ if (curr_softif_neigh)
goto dropped;
/* TODO: check this for locks */
- hna_local_add(soft_iface, ethhdr->h_source);
+ tt_local_add(soft_iface, ethhdr->h_source);
if (is_multicast_ether_addr(ethhdr->h_dest)) {
ret = gw_is_target(bat_priv, skb);
@@ -358,7 +607,8 @@ int interface_tx(struct sk_buff *skb, struct net_device *soft_iface)
/* ethernet packet should be broadcasted */
if (do_bcast) {
- if (!bat_priv->primary_if)
+ primary_if = primary_if_get_selected(bat_priv);
+ if (!primary_if)
goto dropped;
if (my_skb_head_push(skb, sizeof(struct bcast_packet)) < 0)
@@ -374,7 +624,7 @@ int interface_tx(struct sk_buff *skb, struct net_device *soft_iface)
/* hw address of first interface is the orig mac because only
* this mac is known throughout the mesh */
memcpy(bcast_packet->orig,
- bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN);
+ primary_if->net_dev->dev_addr, ETH_ALEN);
/* set broadcast sequence number */
bcast_packet->seqno =
@@ -402,6 +652,10 @@ dropped:
dropped_freed:
bat_priv->stats.tx_dropped++;
end:
+ if (curr_softif_neigh)
+ softif_neigh_free_ref(curr_softif_neigh);
+ if (primary_if)
+ hardif_free_ref(primary_if);
return NETDEV_TX_OK;
}
@@ -413,6 +667,7 @@ void interface_rx(struct net_device *soft_iface,
struct unicast_packet *unicast_packet;
struct ethhdr *ethhdr;
struct vlan_ethhdr *vhdr;
+ struct softif_neigh *curr_softif_neigh = NULL;
short vid = -1;
int ret;
@@ -442,7 +697,8 @@ void interface_rx(struct net_device *soft_iface,
* if we have a another chosen mesh exit node in range
* it will transport the packets to the non-mesh network
*/
- if ((bat_priv->softif_neigh) && (bat_priv->softif_neigh->vid == vid)) {
+ curr_softif_neigh = softif_neigh_vid_get_selected(bat_priv, vid);
+ if (curr_softif_neigh) {
skb_push(skb, hdr_size);
unicast_packet = (struct unicast_packet *)skb->data;
@@ -453,7 +709,7 @@ void interface_rx(struct net_device *soft_iface,
skb_reset_mac_header(skb);
memcpy(unicast_packet->dest,
- bat_priv->softif_neigh->addr, ETH_ALEN);
+ curr_softif_neigh->addr, ETH_ALEN);
ret = route_unicast_packet(skb, recv_if);
if (ret == NET_RX_DROP)
goto dropped;
@@ -478,11 +734,13 @@ void interface_rx(struct net_device *soft_iface,
soft_iface->last_rx = jiffies;
netif_rx(skb);
- return;
+ goto out;
dropped:
kfree_skb(skb);
out:
+ if (curr_softif_neigh)
+ softif_neigh_free_ref(curr_softif_neigh);
return;
}
@@ -516,14 +774,15 @@ static void interface_setup(struct net_device *dev)
dev->hard_start_xmit = interface_tx;
#endif
dev->destructor = free_netdev;
+ dev->tx_queue_len = 0;
/**
* can't call min_mtu, because the needed variables
* have not been initialized yet
*/
dev->mtu = ETH_DATA_LEN;
- dev->hard_header_len = BAT_HEADER_LEN; /* reserve more space in the
- * skbuff for our header */
+ /* reserve more space in the skbuff for our header */
+ dev->hard_header_len = BAT_HEADER_LEN;
/* generate random address */
random_ether_addr(dev_addr);
@@ -548,7 +807,7 @@ struct net_device *softif_create(char *name)
goto out;
}
- ret = register_netdev(soft_iface);
+ ret = register_netdevice(soft_iface);
if (ret < 0) {
pr_err("Unable to register the batman interface '%s': %i\n",
name, ret);
@@ -572,11 +831,10 @@ struct net_device *softif_create(char *name)
atomic_set(&bat_priv->mesh_state, MESH_INACTIVE);
atomic_set(&bat_priv->bcast_seqno, 1);
- atomic_set(&bat_priv->hna_local_changed, 0);
+ atomic_set(&bat_priv->tt_local_changed, 0);
bat_priv->primary_if = NULL;
bat_priv->num_ifaces = 0;
- bat_priv->softif_neigh = NULL;
ret = sysfs_add_meshif(soft_iface);
if (ret < 0)
@@ -632,7 +890,7 @@ static int bat_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
cmd->supported = 0;
cmd->advertising = 0;
- cmd->speed = SPEED_10;
+ ethtool_cmd_speed_set(cmd, SPEED_10);
cmd->duplex = DUPLEX_FULL;
cmd->port = PORT_TP;
cmd->phy_address = 0;
@@ -667,12 +925,3 @@ static u32 bat_get_link(struct net_device *dev)
return 1;
}
-static u32 bat_get_rx_csum(struct net_device *dev)
-{
- return 0;
-}
-
-static int bat_set_rx_csum(struct net_device *dev, u32 data)
-{
- return -EOPNOTSUPP;
-}
diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c
index 8d15b48d169..7b729660cbf 100644
--- a/net/batman-adv/translation-table.c
+++ b/net/batman-adv/translation-table.c
@@ -22,43 +22,44 @@
#include "main.h"
#include "translation-table.h"
#include "soft-interface.h"
+#include "hard-interface.h"
#include "hash.h"
#include "originator.h"
-static void hna_local_purge(struct work_struct *work);
-static void _hna_global_del_orig(struct bat_priv *bat_priv,
- struct hna_global_entry *hna_global_entry,
+static void tt_local_purge(struct work_struct *work);
+static void _tt_global_del_orig(struct bat_priv *bat_priv,
+ struct tt_global_entry *tt_global_entry,
char *message);
/* returns 1 if they are the same mac addr */
-static int compare_lhna(struct hlist_node *node, void *data2)
+static int compare_ltt(struct hlist_node *node, void *data2)
{
- void *data1 = container_of(node, struct hna_local_entry, hash_entry);
+ void *data1 = container_of(node, struct tt_local_entry, hash_entry);
return (memcmp(data1, data2, ETH_ALEN) == 0 ? 1 : 0);
}
/* returns 1 if they are the same mac addr */
-static int compare_ghna(struct hlist_node *node, void *data2)
+static int compare_gtt(struct hlist_node *node, void *data2)
{
- void *data1 = container_of(node, struct hna_global_entry, hash_entry);
+ void *data1 = container_of(node, struct tt_global_entry, hash_entry);
return (memcmp(data1, data2, ETH_ALEN) == 0 ? 1 : 0);
}
-static void hna_local_start_timer(struct bat_priv *bat_priv)
+static void tt_local_start_timer(struct bat_priv *bat_priv)
{
- INIT_DELAYED_WORK(&bat_priv->hna_work, hna_local_purge);
- queue_delayed_work(bat_event_workqueue, &bat_priv->hna_work, 10 * HZ);
+ INIT_DELAYED_WORK(&bat_priv->tt_work, tt_local_purge);
+ queue_delayed_work(bat_event_workqueue, &bat_priv->tt_work, 10 * HZ);
}
-static struct hna_local_entry *hna_local_hash_find(struct bat_priv *bat_priv,
+static struct tt_local_entry *tt_local_hash_find(struct bat_priv *bat_priv,
void *data)
{
- struct hashtable_t *hash = bat_priv->hna_local_hash;
+ struct hashtable_t *hash = bat_priv->tt_local_hash;
struct hlist_head *head;
struct hlist_node *node;
- struct hna_local_entry *hna_local_entry, *hna_local_entry_tmp = NULL;
+ struct tt_local_entry *tt_local_entry, *tt_local_entry_tmp = NULL;
int index;
if (!hash)
@@ -68,26 +69,26 @@ static struct hna_local_entry *hna_local_hash_find(struct bat_priv *bat_priv,
head = &hash->table[index];
rcu_read_lock();
- hlist_for_each_entry_rcu(hna_local_entry, node, head, hash_entry) {
- if (!compare_eth(hna_local_entry, data))
+ hlist_for_each_entry_rcu(tt_local_entry, node, head, hash_entry) {
+ if (!compare_eth(tt_local_entry, data))
continue;
- hna_local_entry_tmp = hna_local_entry;
+ tt_local_entry_tmp = tt_local_entry;
break;
}
rcu_read_unlock();
- return hna_local_entry_tmp;
+ return tt_local_entry_tmp;
}
-static struct hna_global_entry *hna_global_hash_find(struct bat_priv *bat_priv,
+static struct tt_global_entry *tt_global_hash_find(struct bat_priv *bat_priv,
void *data)
{
- struct hashtable_t *hash = bat_priv->hna_global_hash;
+ struct hashtable_t *hash = bat_priv->tt_global_hash;
struct hlist_head *head;
struct hlist_node *node;
- struct hna_global_entry *hna_global_entry;
- struct hna_global_entry *hna_global_entry_tmp = NULL;
+ struct tt_global_entry *tt_global_entry;
+ struct tt_global_entry *tt_global_entry_tmp = NULL;
int index;
if (!hash)
@@ -97,125 +98,125 @@ static struct hna_global_entry *hna_global_hash_find(struct bat_priv *bat_priv,
head = &hash->table[index];
rcu_read_lock();
- hlist_for_each_entry_rcu(hna_global_entry, node, head, hash_entry) {
- if (!compare_eth(hna_global_entry, data))
+ hlist_for_each_entry_rcu(tt_global_entry, node, head, hash_entry) {
+ if (!compare_eth(tt_global_entry, data))
continue;
- hna_global_entry_tmp = hna_global_entry;
+ tt_global_entry_tmp = tt_global_entry;
break;
}
rcu_read_unlock();
- return hna_global_entry_tmp;
+ return tt_global_entry_tmp;
}
-int hna_local_init(struct bat_priv *bat_priv)
+int tt_local_init(struct bat_priv *bat_priv)
{
- if (bat_priv->hna_local_hash)
+ if (bat_priv->tt_local_hash)
return 1;
- bat_priv->hna_local_hash = hash_new(1024);
+ bat_priv->tt_local_hash = hash_new(1024);
- if (!bat_priv->hna_local_hash)
+ if (!bat_priv->tt_local_hash)
return 0;
- atomic_set(&bat_priv->hna_local_changed, 0);
- hna_local_start_timer(bat_priv);
+ atomic_set(&bat_priv->tt_local_changed, 0);
+ tt_local_start_timer(bat_priv);
return 1;
}
-void hna_local_add(struct net_device *soft_iface, uint8_t *addr)
+void tt_local_add(struct net_device *soft_iface, uint8_t *addr)
{
struct bat_priv *bat_priv = netdev_priv(soft_iface);
- struct hna_local_entry *hna_local_entry;
- struct hna_global_entry *hna_global_entry;
+ struct tt_local_entry *tt_local_entry;
+ struct tt_global_entry *tt_global_entry;
int required_bytes;
- spin_lock_bh(&bat_priv->hna_lhash_lock);
- hna_local_entry = hna_local_hash_find(bat_priv, addr);
- spin_unlock_bh(&bat_priv->hna_lhash_lock);
+ spin_lock_bh(&bat_priv->tt_lhash_lock);
+ tt_local_entry = tt_local_hash_find(bat_priv, addr);
+ spin_unlock_bh(&bat_priv->tt_lhash_lock);
- if (hna_local_entry) {
- hna_local_entry->last_seen = jiffies;
+ if (tt_local_entry) {
+ tt_local_entry->last_seen = jiffies;
return;
}
/* only announce as many hosts as possible in the batman-packet and
- space in batman_packet->num_hna That also should give a limit to
+ space in batman_packet->num_tt That also should give a limit to
MAC-flooding. */
- required_bytes = (bat_priv->num_local_hna + 1) * ETH_ALEN;
+ required_bytes = (bat_priv->num_local_tt + 1) * ETH_ALEN;
required_bytes += BAT_PACKET_LEN;
if ((required_bytes > ETH_DATA_LEN) ||
(atomic_read(&bat_priv->aggregated_ogms) &&
required_bytes > MAX_AGGREGATION_BYTES) ||
- (bat_priv->num_local_hna + 1 > 255)) {
+ (bat_priv->num_local_tt + 1 > 255)) {
bat_dbg(DBG_ROUTES, bat_priv,
- "Can't add new local hna entry (%pM): "
- "number of local hna entries exceeds packet size\n",
+ "Can't add new local tt entry (%pM): "
+ "number of local tt entries exceeds packet size\n",
addr);
return;
}
bat_dbg(DBG_ROUTES, bat_priv,
- "Creating new local hna entry: %pM\n", addr);
+ "Creating new local tt entry: %pM\n", addr);
- hna_local_entry = kmalloc(sizeof(struct hna_local_entry), GFP_ATOMIC);
- if (!hna_local_entry)
+ tt_local_entry = kmalloc(sizeof(struct tt_local_entry), GFP_ATOMIC);
+ if (!tt_local_entry)
return;
- memcpy(hna_local_entry->addr, addr, ETH_ALEN);
- hna_local_entry->last_seen = jiffies;
+ memcpy(tt_local_entry->addr, addr, ETH_ALEN);
+ tt_local_entry->last_seen = jiffies;
/* the batman interface mac address should never be purged */
if (compare_eth(addr, soft_iface->dev_addr))
- hna_local_entry->never_purge = 1;
+ tt_local_entry->never_purge = 1;
else
- hna_local_entry->never_purge = 0;
+ tt_local_entry->never_purge = 0;
- spin_lock_bh(&bat_priv->hna_lhash_lock);
+ spin_lock_bh(&bat_priv->tt_lhash_lock);
- hash_add(bat_priv->hna_local_hash, compare_lhna, choose_orig,
- hna_local_entry, &hna_local_entry->hash_entry);
- bat_priv->num_local_hna++;
- atomic_set(&bat_priv->hna_local_changed, 1);
+ hash_add(bat_priv->tt_local_hash, compare_ltt, choose_orig,
+ tt_local_entry, &tt_local_entry->hash_entry);
+ bat_priv->num_local_tt++;
+ atomic_set(&bat_priv->tt_local_changed, 1);
- spin_unlock_bh(&bat_priv->hna_lhash_lock);
+ spin_unlock_bh(&bat_priv->tt_lhash_lock);
/* remove address from global hash if present */
- spin_lock_bh(&bat_priv->hna_ghash_lock);
+ spin_lock_bh(&bat_priv->tt_ghash_lock);
- hna_global_entry = hna_global_hash_find(bat_priv, addr);
+ tt_global_entry = tt_global_hash_find(bat_priv, addr);
- if (hna_global_entry)
- _hna_global_del_orig(bat_priv, hna_global_entry,
- "local hna received");
+ if (tt_global_entry)
+ _tt_global_del_orig(bat_priv, tt_global_entry,
+ "local tt received");
- spin_unlock_bh(&bat_priv->hna_ghash_lock);
+ spin_unlock_bh(&bat_priv->tt_ghash_lock);
}
-int hna_local_fill_buffer(struct bat_priv *bat_priv,
+int tt_local_fill_buffer(struct bat_priv *bat_priv,
unsigned char *buff, int buff_len)
{
- struct hashtable_t *hash = bat_priv->hna_local_hash;
- struct hna_local_entry *hna_local_entry;
+ struct hashtable_t *hash = bat_priv->tt_local_hash;
+ struct tt_local_entry *tt_local_entry;
struct hlist_node *node;
struct hlist_head *head;
int i, count = 0;
- spin_lock_bh(&bat_priv->hna_lhash_lock);
+ spin_lock_bh(&bat_priv->tt_lhash_lock);
for (i = 0; i < hash->size; i++) {
head = &hash->table[i];
rcu_read_lock();
- hlist_for_each_entry_rcu(hna_local_entry, node,
+ hlist_for_each_entry_rcu(tt_local_entry, node,
head, hash_entry) {
if (buff_len < (count + 1) * ETH_ALEN)
break;
- memcpy(buff + (count * ETH_ALEN), hna_local_entry->addr,
+ memcpy(buff + (count * ETH_ALEN), tt_local_entry->addr,
ETH_ALEN);
count++;
@@ -223,37 +224,47 @@ int hna_local_fill_buffer(struct bat_priv *bat_priv,
rcu_read_unlock();
}
- /* if we did not get all new local hnas see you next time ;-) */
- if (count == bat_priv->num_local_hna)
- atomic_set(&bat_priv->hna_local_changed, 0);
+ /* if we did not get all new local tts see you next time ;-) */
+ if (count == bat_priv->num_local_tt)
+ atomic_set(&bat_priv->tt_local_changed, 0);
- spin_unlock_bh(&bat_priv->hna_lhash_lock);
+ spin_unlock_bh(&bat_priv->tt_lhash_lock);
return count;
}
-int hna_local_seq_print_text(struct seq_file *seq, void *offset)
+int tt_local_seq_print_text(struct seq_file *seq, void *offset)
{
struct net_device *net_dev = (struct net_device *)seq->private;
struct bat_priv *bat_priv = netdev_priv(net_dev);
- struct hashtable_t *hash = bat_priv->hna_local_hash;
- struct hna_local_entry *hna_local_entry;
+ struct hashtable_t *hash = bat_priv->tt_local_hash;
+ struct tt_local_entry *tt_local_entry;
+ struct hard_iface *primary_if;
struct hlist_node *node;
struct hlist_head *head;
size_t buf_size, pos;
char *buff;
- int i;
+ int i, ret = 0;
- if (!bat_priv->primary_if) {
- return seq_printf(seq, "BATMAN mesh %s disabled - "
- "please specify interfaces to enable it\n",
- net_dev->name);
+ primary_if = primary_if_get_selected(bat_priv);
+ if (!primary_if) {
+ ret = seq_printf(seq, "BATMAN mesh %s disabled - "
+ "please specify interfaces to enable it\n",
+ net_dev->name);
+ goto out;
+ }
+
+ if (primary_if->if_status != IF_ACTIVE) {
+ ret = seq_printf(seq, "BATMAN mesh %s disabled - "
+ "primary interface not active\n",
+ net_dev->name);
+ goto out;
}
seq_printf(seq, "Locally retrieved addresses (from %s) "
- "announced via HNA:\n",
+ "announced via TT:\n",
net_dev->name);
- spin_lock_bh(&bat_priv->hna_lhash_lock);
+ spin_lock_bh(&bat_priv->tt_lhash_lock);
buf_size = 1;
/* Estimate length for: " * xx:xx:xx:xx:xx:xx\n" */
@@ -268,8 +279,9 @@ int hna_local_seq_print_text(struct seq_file *seq, void *offset)
buff = kmalloc(buf_size, GFP_ATOMIC);
if (!buff) {
- spin_unlock_bh(&bat_priv->hna_lhash_lock);
- return -ENOMEM;
+ spin_unlock_bh(&bat_priv->tt_lhash_lock);
+ ret = -ENOMEM;
+ goto out;
}
buff[0] = '\0';
@@ -279,211 +291,225 @@ int hna_local_seq_print_text(struct seq_file *seq, void *offset)
head = &hash->table[i];
rcu_read_lock();
- hlist_for_each_entry_rcu(hna_local_entry, node,
+ hlist_for_each_entry_rcu(tt_local_entry, node,
head, hash_entry) {
pos += snprintf(buff + pos, 22, " * %pM\n",
- hna_local_entry->addr);
+ tt_local_entry->addr);
}
rcu_read_unlock();
}
- spin_unlock_bh(&bat_priv->hna_lhash_lock);
+ spin_unlock_bh(&bat_priv->tt_lhash_lock);
seq_printf(seq, "%s", buff);
kfree(buff);
- return 0;
+out:
+ if (primary_if)
+ hardif_free_ref(primary_if);
+ return ret;
}
-static void _hna_local_del(struct hlist_node *node, void *arg)
+static void _tt_local_del(struct hlist_node *node, void *arg)
{
struct bat_priv *bat_priv = (struct bat_priv *)arg;
- void *data = container_of(node, struct hna_local_entry, hash_entry);
+ void *data = container_of(node, struct tt_local_entry, hash_entry);
kfree(data);
- bat_priv->num_local_hna--;
- atomic_set(&bat_priv->hna_local_changed, 1);
+ bat_priv->num_local_tt--;
+ atomic_set(&bat_priv->tt_local_changed, 1);
}
-static void hna_local_del(struct bat_priv *bat_priv,
- struct hna_local_entry *hna_local_entry,
+static void tt_local_del(struct bat_priv *bat_priv,
+ struct tt_local_entry *tt_local_entry,
char *message)
{
- bat_dbg(DBG_ROUTES, bat_priv, "Deleting local hna entry (%pM): %s\n",
- hna_local_entry->addr, message);
+ bat_dbg(DBG_ROUTES, bat_priv, "Deleting local tt entry (%pM): %s\n",
+ tt_local_entry->addr, message);
- hash_remove(bat_priv->hna_local_hash, compare_lhna, choose_orig,
- hna_local_entry->addr);
- _hna_local_del(&hna_local_entry->hash_entry, bat_priv);
+ hash_remove(bat_priv->tt_local_hash, compare_ltt, choose_orig,
+ tt_local_entry->addr);
+ _tt_local_del(&tt_local_entry->hash_entry, bat_priv);
}
-void hna_local_remove(struct bat_priv *bat_priv,
+void tt_local_remove(struct bat_priv *bat_priv,
uint8_t *addr, char *message)
{
- struct hna_local_entry *hna_local_entry;
+ struct tt_local_entry *tt_local_entry;
- spin_lock_bh(&bat_priv->hna_lhash_lock);
+ spin_lock_bh(&bat_priv->tt_lhash_lock);
- hna_local_entry = hna_local_hash_find(bat_priv, addr);
+ tt_local_entry = tt_local_hash_find(bat_priv, addr);
- if (hna_local_entry)
- hna_local_del(bat_priv, hna_local_entry, message);
+ if (tt_local_entry)
+ tt_local_del(bat_priv, tt_local_entry, message);
- spin_unlock_bh(&bat_priv->hna_lhash_lock);
+ spin_unlock_bh(&bat_priv->tt_lhash_lock);
}
-static void hna_local_purge(struct work_struct *work)
+static void tt_local_purge(struct work_struct *work)
{
struct delayed_work *delayed_work =
container_of(work, struct delayed_work, work);
struct bat_priv *bat_priv =
- container_of(delayed_work, struct bat_priv, hna_work);
- struct hashtable_t *hash = bat_priv->hna_local_hash;
- struct hna_local_entry *hna_local_entry;
+ container_of(delayed_work, struct bat_priv, tt_work);
+ struct hashtable_t *hash = bat_priv->tt_local_hash;
+ struct tt_local_entry *tt_local_entry;
struct hlist_node *node, *node_tmp;
struct hlist_head *head;
unsigned long timeout;
int i;
- spin_lock_bh(&bat_priv->hna_lhash_lock);
+ spin_lock_bh(&bat_priv->tt_lhash_lock);
for (i = 0; i < hash->size; i++) {
head = &hash->table[i];
- hlist_for_each_entry_safe(hna_local_entry, node, node_tmp,
+ hlist_for_each_entry_safe(tt_local_entry, node, node_tmp,
head, hash_entry) {
- if (hna_local_entry->never_purge)
+ if (tt_local_entry->never_purge)
continue;
- timeout = hna_local_entry->last_seen;
- timeout += LOCAL_HNA_TIMEOUT * HZ;
+ timeout = tt_local_entry->last_seen;
+ timeout += TT_LOCAL_TIMEOUT * HZ;
if (time_before(jiffies, timeout))
continue;
- hna_local_del(bat_priv, hna_local_entry,
+ tt_local_del(bat_priv, tt_local_entry,
"address timed out");
}
}
- spin_unlock_bh(&bat_priv->hna_lhash_lock);
- hna_local_start_timer(bat_priv);
+ spin_unlock_bh(&bat_priv->tt_lhash_lock);
+ tt_local_start_timer(bat_priv);
}
-void hna_local_free(struct bat_priv *bat_priv)
+void tt_local_free(struct bat_priv *bat_priv)
{
- if (!bat_priv->hna_local_hash)
+ if (!bat_priv->tt_local_hash)
return;
- cancel_delayed_work_sync(&bat_priv->hna_work);
- hash_delete(bat_priv->hna_local_hash, _hna_local_del, bat_priv);
- bat_priv->hna_local_hash = NULL;
+ cancel_delayed_work_sync(&bat_priv->tt_work);
+ hash_delete(bat_priv->tt_local_hash, _tt_local_del, bat_priv);
+ bat_priv->tt_local_hash = NULL;
}
-int hna_global_init(struct bat_priv *bat_priv)
+int tt_global_init(struct bat_priv *bat_priv)
{
- if (bat_priv->hna_global_hash)
+ if (bat_priv->tt_global_hash)
return 1;
- bat_priv->hna_global_hash = hash_new(1024);
+ bat_priv->tt_global_hash = hash_new(1024);
- if (!bat_priv->hna_global_hash)
+ if (!bat_priv->tt_global_hash)
return 0;
return 1;
}
-void hna_global_add_orig(struct bat_priv *bat_priv,
+void tt_global_add_orig(struct bat_priv *bat_priv,
struct orig_node *orig_node,
- unsigned char *hna_buff, int hna_buff_len)
+ unsigned char *tt_buff, int tt_buff_len)
{
- struct hna_global_entry *hna_global_entry;
- struct hna_local_entry *hna_local_entry;
- int hna_buff_count = 0;
- unsigned char *hna_ptr;
+ struct tt_global_entry *tt_global_entry;
+ struct tt_local_entry *tt_local_entry;
+ int tt_buff_count = 0;
+ unsigned char *tt_ptr;
- while ((hna_buff_count + 1) * ETH_ALEN <= hna_buff_len) {
- spin_lock_bh(&bat_priv->hna_ghash_lock);
+ while ((tt_buff_count + 1) * ETH_ALEN <= tt_buff_len) {
+ spin_lock_bh(&bat_priv->tt_ghash_lock);
- hna_ptr = hna_buff + (hna_buff_count * ETH_ALEN);
- hna_global_entry = hna_global_hash_find(bat_priv, hna_ptr);
+ tt_ptr = tt_buff + (tt_buff_count * ETH_ALEN);
+ tt_global_entry = tt_global_hash_find(bat_priv, tt_ptr);
- if (!hna_global_entry) {
- spin_unlock_bh(&bat_priv->hna_ghash_lock);
+ if (!tt_global_entry) {
+ spin_unlock_bh(&bat_priv->tt_ghash_lock);
- hna_global_entry =
- kmalloc(sizeof(struct hna_global_entry),
+ tt_global_entry =
+ kmalloc(sizeof(struct tt_global_entry),
GFP_ATOMIC);
- if (!hna_global_entry)
+ if (!tt_global_entry)
break;
- memcpy(hna_global_entry->addr, hna_ptr, ETH_ALEN);
+ memcpy(tt_global_entry->addr, tt_ptr, ETH_ALEN);
bat_dbg(DBG_ROUTES, bat_priv,
- "Creating new global hna entry: "
+ "Creating new global tt entry: "
"%pM (via %pM)\n",
- hna_global_entry->addr, orig_node->orig);
+ tt_global_entry->addr, orig_node->orig);
- spin_lock_bh(&bat_priv->hna_ghash_lock);
- hash_add(bat_priv->hna_global_hash, compare_ghna,
- choose_orig, hna_global_entry,
- &hna_global_entry->hash_entry);
+ spin_lock_bh(&bat_priv->tt_ghash_lock);
+ hash_add(bat_priv->tt_global_hash, compare_gtt,
+ choose_orig, tt_global_entry,
+ &tt_global_entry->hash_entry);
}
- hna_global_entry->orig_node = orig_node;
- spin_unlock_bh(&bat_priv->hna_ghash_lock);
+ tt_global_entry->orig_node = orig_node;
+ spin_unlock_bh(&bat_priv->tt_ghash_lock);
/* remove address from local hash if present */
- spin_lock_bh(&bat_priv->hna_lhash_lock);
+ spin_lock_bh(&bat_priv->tt_lhash_lock);
- hna_ptr = hna_buff + (hna_buff_count * ETH_ALEN);
- hna_local_entry = hna_local_hash_find(bat_priv, hna_ptr);
+ tt_ptr = tt_buff + (tt_buff_count * ETH_ALEN);
+ tt_local_entry = tt_local_hash_find(bat_priv, tt_ptr);
- if (hna_local_entry)
- hna_local_del(bat_priv, hna_local_entry,
- "global hna received");
+ if (tt_local_entry)
+ tt_local_del(bat_priv, tt_local_entry,
+ "global tt received");
- spin_unlock_bh(&bat_priv->hna_lhash_lock);
+ spin_unlock_bh(&bat_priv->tt_lhash_lock);
- hna_buff_count++;
+ tt_buff_count++;
}
/* initialize, and overwrite if malloc succeeds */
- orig_node->hna_buff = NULL;
- orig_node->hna_buff_len = 0;
-
- if (hna_buff_len > 0) {
- orig_node->hna_buff = kmalloc(hna_buff_len, GFP_ATOMIC);
- if (orig_node->hna_buff) {
- memcpy(orig_node->hna_buff, hna_buff, hna_buff_len);
- orig_node->hna_buff_len = hna_buff_len;
+ orig_node->tt_buff = NULL;
+ orig_node->tt_buff_len = 0;
+
+ if (tt_buff_len > 0) {
+ orig_node->tt_buff = kmalloc(tt_buff_len, GFP_ATOMIC);
+ if (orig_node->tt_buff) {
+ memcpy(orig_node->tt_buff, tt_buff, tt_buff_len);
+ orig_node->tt_buff_len = tt_buff_len;
}
}
}
-int hna_global_seq_print_text(struct seq_file *seq, void *offset)
+int tt_global_seq_print_text(struct seq_file *seq, void *offset)
{
struct net_device *net_dev = (struct net_device *)seq->private;
struct bat_priv *bat_priv = netdev_priv(net_dev);
- struct hashtable_t *hash = bat_priv->hna_global_hash;
- struct hna_global_entry *hna_global_entry;
+ struct hashtable_t *hash = bat_priv->tt_global_hash;
+ struct tt_global_entry *tt_global_entry;
+ struct hard_iface *primary_if;
struct hlist_node *node;
struct hlist_head *head;
size_t buf_size, pos;
char *buff;
- int i;
+ int i, ret = 0;
- if (!bat_priv->primary_if) {
- return seq_printf(seq, "BATMAN mesh %s disabled - "
- "please specify interfaces to enable it\n",
- net_dev->name);
+ primary_if = primary_if_get_selected(bat_priv);
+ if (!primary_if) {
+ ret = seq_printf(seq, "BATMAN mesh %s disabled - please "
+ "specify interfaces to enable it\n",
+ net_dev->name);
+ goto out;
}
- seq_printf(seq, "Globally announced HNAs received via the mesh %s\n",
+ if (primary_if->if_status != IF_ACTIVE) {
+ ret = seq_printf(seq, "BATMAN mesh %s disabled - "
+ "primary interface not active\n",
+ net_dev->name);
+ goto out;
+ }
+
+ seq_printf(seq,
+ "Globally announced TT entries received via the mesh %s\n",
net_dev->name);
- spin_lock_bh(&bat_priv->hna_ghash_lock);
+ spin_lock_bh(&bat_priv->tt_ghash_lock);
buf_size = 1;
/* Estimate length for: " * xx:xx:xx:xx:xx:xx via xx:xx:xx:xx:xx:xx\n"*/
@@ -498,8 +524,9 @@ int hna_global_seq_print_text(struct seq_file *seq, void *offset)
buff = kmalloc(buf_size, GFP_ATOMIC);
if (!buff) {
- spin_unlock_bh(&bat_priv->hna_ghash_lock);
- return -ENOMEM;
+ spin_unlock_bh(&bat_priv->tt_ghash_lock);
+ ret = -ENOMEM;
+ goto out;
}
buff[0] = '\0';
pos = 0;
@@ -508,101 +535,104 @@ int hna_global_seq_print_text(struct seq_file *seq, void *offset)
head = &hash->table[i];
rcu_read_lock();
- hlist_for_each_entry_rcu(hna_global_entry, node,
+ hlist_for_each_entry_rcu(tt_global_entry, node,
head, hash_entry) {
pos += snprintf(buff + pos, 44,
" * %pM via %pM\n",
- hna_global_entry->addr,
- hna_global_entry->orig_node->orig);
+ tt_global_entry->addr,
+ tt_global_entry->orig_node->orig);
}
rcu_read_unlock();
}
- spin_unlock_bh(&bat_priv->hna_ghash_lock);
+ spin_unlock_bh(&bat_priv->tt_ghash_lock);
seq_printf(seq, "%s", buff);
kfree(buff);
- return 0;
+out:
+ if (primary_if)
+ hardif_free_ref(primary_if);
+ return ret;
}
-static void _hna_global_del_orig(struct bat_priv *bat_priv,
- struct hna_global_entry *hna_global_entry,
+static void _tt_global_del_orig(struct bat_priv *bat_priv,
+ struct tt_global_entry *tt_global_entry,
char *message)
{
bat_dbg(DBG_ROUTES, bat_priv,
- "Deleting global hna entry %pM (via %pM): %s\n",
- hna_global_entry->addr, hna_global_entry->orig_node->orig,
+ "Deleting global tt entry %pM (via %pM): %s\n",
+ tt_global_entry->addr, tt_global_entry->orig_node->orig,
message);
- hash_remove(bat_priv->hna_global_hash, compare_ghna, choose_orig,
- hna_global_entry->addr);
- kfree(hna_global_entry);
+ hash_remove(bat_priv->tt_global_hash, compare_gtt, choose_orig,
+ tt_global_entry->addr);
+ kfree(tt_global_entry);
}
-void hna_global_del_orig(struct bat_priv *bat_priv,
+void tt_global_del_orig(struct bat_priv *bat_priv,
struct orig_node *orig_node, char *message)
{
- struct hna_global_entry *hna_global_entry;
- int hna_buff_count = 0;
- unsigned char *hna_ptr;
+ struct tt_global_entry *tt_global_entry;
+ int tt_buff_count = 0;
+ unsigned char *tt_ptr;
- if (orig_node->hna_buff_len == 0)
+ if (orig_node->tt_buff_len == 0)
return;
- spin_lock_bh(&bat_priv->hna_ghash_lock);
+ spin_lock_bh(&bat_priv->tt_ghash_lock);
- while ((hna_buff_count + 1) * ETH_ALEN <= orig_node->hna_buff_len) {
- hna_ptr = orig_node->hna_buff + (hna_buff_count * ETH_ALEN);
- hna_global_entry = hna_global_hash_find(bat_priv, hna_ptr);
+ while ((tt_buff_count + 1) * ETH_ALEN <= orig_node->tt_buff_len) {
+ tt_ptr = orig_node->tt_buff + (tt_buff_count * ETH_ALEN);
+ tt_global_entry = tt_global_hash_find(bat_priv, tt_ptr);
- if ((hna_global_entry) &&
- (hna_global_entry->orig_node == orig_node))
- _hna_global_del_orig(bat_priv, hna_global_entry,
+ if ((tt_global_entry) &&
+ (tt_global_entry->orig_node == orig_node))
+ _tt_global_del_orig(bat_priv, tt_global_entry,
message);
- hna_buff_count++;
+ tt_buff_count++;
}
- spin_unlock_bh(&bat_priv->hna_ghash_lock);
+ spin_unlock_bh(&bat_priv->tt_ghash_lock);
- orig_node->hna_buff_len = 0;
- kfree(orig_node->hna_buff);
- orig_node->hna_buff = NULL;
+ orig_node->tt_buff_len = 0;
+ kfree(orig_node->tt_buff);
+ orig_node->tt_buff = NULL;
}
-static void hna_global_del(struct hlist_node *node, void *arg)
+static void tt_global_del(struct hlist_node *node, void *arg)
{
- void *data = container_of(node, struct hna_global_entry, hash_entry);
+ void *data = container_of(node, struct tt_global_entry, hash_entry);
kfree(data);
}
-void hna_global_free(struct bat_priv *bat_priv)
+void tt_global_free(struct bat_priv *bat_priv)
{
- if (!bat_priv->hna_global_hash)
+ if (!bat_priv->tt_global_hash)
return;
- hash_delete(bat_priv->hna_global_hash, hna_global_del, NULL);
- bat_priv->hna_global_hash = NULL;
+ hash_delete(bat_priv->tt_global_hash, tt_global_del, NULL);
+ bat_priv->tt_global_hash = NULL;
}
struct orig_node *transtable_search(struct bat_priv *bat_priv, uint8_t *addr)
{
- struct hna_global_entry *hna_global_entry;
+ struct tt_global_entry *tt_global_entry;
struct orig_node *orig_node = NULL;
- spin_lock_bh(&bat_priv->hna_ghash_lock);
- hna_global_entry = hna_global_hash_find(bat_priv, addr);
+ spin_lock_bh(&bat_priv->tt_ghash_lock);
+ tt_global_entry = tt_global_hash_find(bat_priv, addr);
- if (!hna_global_entry)
+ if (!tt_global_entry)
goto out;
- if (!atomic_inc_not_zero(&hna_global_entry->orig_node->refcount))
+ if (!atomic_inc_not_zero(&tt_global_entry->orig_node->refcount))
goto out;
- orig_node = hna_global_entry->orig_node;
+ orig_node = tt_global_entry->orig_node;
out:
- spin_unlock_bh(&bat_priv->hna_ghash_lock);
+ spin_unlock_bh(&bat_priv->tt_ghash_lock);
return orig_node;
}
diff --git a/net/batman-adv/translation-table.h b/net/batman-adv/translation-table.h
index f19931ca145..46152c38cc9 100644
--- a/net/batman-adv/translation-table.h
+++ b/net/batman-adv/translation-table.h
@@ -22,22 +22,22 @@
#ifndef _NET_BATMAN_ADV_TRANSLATION_TABLE_H_
#define _NET_BATMAN_ADV_TRANSLATION_TABLE_H_
-int hna_local_init(struct bat_priv *bat_priv);
-void hna_local_add(struct net_device *soft_iface, uint8_t *addr);
-void hna_local_remove(struct bat_priv *bat_priv,
+int tt_local_init(struct bat_priv *bat_priv);
+void tt_local_add(struct net_device *soft_iface, uint8_t *addr);
+void tt_local_remove(struct bat_priv *bat_priv,
uint8_t *addr, char *message);
-int hna_local_fill_buffer(struct bat_priv *bat_priv,
+int tt_local_fill_buffer(struct bat_priv *bat_priv,
unsigned char *buff, int buff_len);
-int hna_local_seq_print_text(struct seq_file *seq, void *offset);
-void hna_local_free(struct bat_priv *bat_priv);
-int hna_global_init(struct bat_priv *bat_priv);
-void hna_global_add_orig(struct bat_priv *bat_priv,
+int tt_local_seq_print_text(struct seq_file *seq, void *offset);
+void tt_local_free(struct bat_priv *bat_priv);
+int tt_global_init(struct bat_priv *bat_priv);
+void tt_global_add_orig(struct bat_priv *bat_priv,
struct orig_node *orig_node,
- unsigned char *hna_buff, int hna_buff_len);
-int hna_global_seq_print_text(struct seq_file *seq, void *offset);
-void hna_global_del_orig(struct bat_priv *bat_priv,
+ unsigned char *tt_buff, int tt_buff_len);
+int tt_global_seq_print_text(struct seq_file *seq, void *offset);
+void tt_global_del_orig(struct bat_priv *bat_priv,
struct orig_node *orig_node, char *message);
-void hna_global_free(struct bat_priv *bat_priv);
+void tt_global_free(struct bat_priv *bat_priv);
struct orig_node *transtable_search(struct bat_priv *bat_priv, uint8_t *addr);
#endif /* _NET_BATMAN_ADV_TRANSLATION_TABLE_H_ */
diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
index 83445cf0cc9..fab70e8b16e 100644
--- a/net/batman-adv/types.h
+++ b/net/batman-adv/types.h
@@ -67,7 +67,7 @@ struct hard_iface {
struct orig_node {
uint8_t orig[ETH_ALEN];
uint8_t primary_addr[ETH_ALEN];
- struct neigh_node *router;
+ struct neigh_node __rcu *router; /* rcu protected pointer */
unsigned long *bcast_own;
uint8_t *bcast_own_sum;
unsigned long last_valid;
@@ -75,25 +75,25 @@ struct orig_node {
unsigned long batman_seqno_reset;
uint8_t gw_flags;
uint8_t flags;
- unsigned char *hna_buff;
- int16_t hna_buff_len;
+ unsigned char *tt_buff;
+ int16_t tt_buff_len;
uint32_t last_real_seqno;
uint8_t last_ttl;
unsigned long bcast_bits[NUM_WORDS];
uint32_t last_bcast_seqno;
struct hlist_head neigh_list;
struct list_head frag_list;
- spinlock_t neigh_list_lock; /* protects neighbor list */
+ spinlock_t neigh_list_lock; /* protects neigh_list and router */
atomic_t refcount;
struct rcu_head rcu;
struct hlist_node hash_entry;
struct bat_priv *bat_priv;
unsigned long last_frag_packet;
- spinlock_t ogm_cnt_lock; /* protects: bcast_own, bcast_own_sum,
- * neigh_node->real_bits,
- * neigh_node->real_packet_count */
- spinlock_t bcast_seqno_lock; /* protects bcast_bits,
- * last_bcast_seqno */
+ /* ogm_cnt_lock protects: bcast_own, bcast_own_sum,
+ * neigh_node->real_bits, neigh_node->real_packet_count */
+ spinlock_t ogm_cnt_lock;
+ /* bcast_seqno_lock protects bcast_bits, last_bcast_seqno */
+ spinlock_t bcast_seqno_lock;
atomic_t bond_candidates;
struct list_head bond_list;
};
@@ -125,6 +125,7 @@ struct neigh_node {
struct rcu_head rcu;
struct orig_node *orig_node;
struct hard_iface *if_incoming;
+ spinlock_t tq_lock; /* protects: tq_recv, tq_index */
};
@@ -145,34 +146,34 @@ struct bat_priv {
atomic_t bcast_queue_left;
atomic_t batman_queue_left;
char num_ifaces;
- struct hlist_head softif_neigh_list;
- struct softif_neigh *softif_neigh;
struct debug_log *debug_log;
- struct hard_iface *primary_if;
struct kobject *mesh_obj;
struct dentry *debug_dir;
struct hlist_head forw_bat_list;
struct hlist_head forw_bcast_list;
struct hlist_head gw_list;
+ struct hlist_head softif_neigh_vids;
struct list_head vis_send_list;
struct hashtable_t *orig_hash;
- struct hashtable_t *hna_local_hash;
- struct hashtable_t *hna_global_hash;
+ struct hashtable_t *tt_local_hash;
+ struct hashtable_t *tt_global_hash;
struct hashtable_t *vis_hash;
spinlock_t forw_bat_list_lock; /* protects forw_bat_list */
spinlock_t forw_bcast_list_lock; /* protects */
- spinlock_t hna_lhash_lock; /* protects hna_local_hash */
- spinlock_t hna_ghash_lock; /* protects hna_global_hash */
+ spinlock_t tt_lhash_lock; /* protects tt_local_hash */
+ spinlock_t tt_ghash_lock; /* protects tt_global_hash */
spinlock_t gw_list_lock; /* protects gw_list and curr_gw */
spinlock_t vis_hash_lock; /* protects vis_hash */
spinlock_t vis_list_lock; /* protects vis_info::recv_list */
spinlock_t softif_neigh_lock; /* protects soft-interface neigh list */
- int16_t num_local_hna;
- atomic_t hna_local_changed;
- struct delayed_work hna_work;
+ spinlock_t softif_neigh_vid_lock; /* protects soft-interface vid list */
+ int16_t num_local_tt;
+ atomic_t tt_local_changed;
+ struct delayed_work tt_work;
struct delayed_work orig_work;
struct delayed_work vis_work;
struct gw_node __rcu *curr_gw; /* rcu protected pointer */
+ struct hard_iface __rcu *primary_if; /* rcu protected pointer */
struct vis_info *my_vis_info;
};
@@ -191,14 +192,14 @@ struct socket_packet {
struct icmp_packet_rr icmp_packet;
};
-struct hna_local_entry {
+struct tt_local_entry {
uint8_t addr[ETH_ALEN];
unsigned long last_seen;
char never_purge;
struct hlist_node hash_entry;
};
-struct hna_global_entry {
+struct tt_global_entry {
uint8_t addr[ETH_ALEN];
struct orig_node *orig_node;
struct hlist_node hash_entry;
@@ -261,7 +262,7 @@ struct vis_info {
struct vis_info_entry {
uint8_t src[ETH_ALEN];
uint8_t dest[ETH_ALEN];
- uint8_t quality; /* quality = 0 means HNA */
+ uint8_t quality; /* quality = 0 client */
} __packed;
struct recvlist_node {
@@ -269,11 +270,20 @@ struct recvlist_node {
uint8_t mac[ETH_ALEN];
};
+struct softif_neigh_vid {
+ struct hlist_node list;
+ struct bat_priv *bat_priv;
+ short vid;
+ atomic_t refcount;
+ struct softif_neigh __rcu *softif_neigh;
+ struct rcu_head rcu;
+ struct hlist_head softif_neigh_list;
+};
+
struct softif_neigh {
struct hlist_node list;
uint8_t addr[ETH_ALEN];
unsigned long last_seen;
- short vid;
atomic_t refcount;
struct rcu_head rcu;
};
diff --git a/net/batman-adv/unicast.c b/net/batman-adv/unicast.c
index 19f84bd443a..19c3daf34ac 100644
--- a/net/batman-adv/unicast.c
+++ b/net/batman-adv/unicast.c
@@ -221,15 +221,17 @@ int frag_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv,
struct hard_iface *hard_iface, uint8_t dstaddr[])
{
struct unicast_packet tmp_uc, *unicast_packet;
+ struct hard_iface *primary_if;
struct sk_buff *frag_skb;
struct unicast_frag_packet *frag1, *frag2;
int uc_hdr_len = sizeof(struct unicast_packet);
int ucf_hdr_len = sizeof(struct unicast_frag_packet);
int data_len = skb->len - uc_hdr_len;
- int large_tail = 0;
+ int large_tail = 0, ret = NET_RX_DROP;
uint16_t seqno;
- if (!bat_priv->primary_if)
+ primary_if = primary_if_get_selected(bat_priv);
+ if (!primary_if)
goto dropped;
frag_skb = dev_alloc_skb(data_len - (data_len / 2) + ucf_hdr_len);
@@ -254,7 +256,7 @@ int frag_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv,
frag1->version = COMPAT_VERSION;
frag1->packet_type = BAT_UNICAST_FRAG;
- memcpy(frag1->orig, bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN);
+ memcpy(frag1->orig, primary_if->net_dev->dev_addr, ETH_ALEN);
memcpy(frag2, frag1, sizeof(struct unicast_frag_packet));
if (data_len & 1)
@@ -269,13 +271,17 @@ int frag_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv,
send_skb_packet(skb, hard_iface, dstaddr);
send_skb_packet(frag_skb, hard_iface, dstaddr);
- return NET_RX_SUCCESS;
+ ret = NET_RX_SUCCESS;
+ goto out;
drop_frag:
kfree_skb(frag_skb);
dropped:
kfree_skb(skb);
- return NET_RX_DROP;
+out:
+ if (primary_if)
+ hardif_free_ref(primary_if);
+ return ret;
}
int unicast_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv)
@@ -289,12 +295,12 @@ int unicast_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv)
/* get routing information */
if (is_multicast_ether_addr(ethhdr->h_dest)) {
- orig_node = (struct orig_node *)gw_get_selected(bat_priv);
+ orig_node = (struct orig_node *)gw_get_selected_orig(bat_priv);
if (orig_node)
goto find_router;
}
- /* check for hna host - increases orig_node refcount */
+ /* check for tt host - increases orig_node refcount */
orig_node = transtable_search(bat_priv, ethhdr->h_dest);
find_router:
diff --git a/net/batman-adv/vis.c b/net/batman-adv/vis.c
index f90212f4208..c39f20cc1ba 100644
--- a/net/batman-adv/vis.c
+++ b/net/batman-adv/vis.c
@@ -194,7 +194,7 @@ static ssize_t vis_data_read_entry(char *buff, struct vis_info_entry *entry,
{
/* maximal length: max(4+17+2, 3+17+1+3+2) == 26 */
if (primary && entry->quality == 0)
- return sprintf(buff, "HNA %pM, ", entry->dest);
+ return sprintf(buff, "TT %pM, ", entry->dest);
else if (compare_eth(entry->src, src))
return sprintf(buff, "TQ %pM %d, ", entry->dest,
entry->quality);
@@ -204,6 +204,7 @@ static ssize_t vis_data_read_entry(char *buff, struct vis_info_entry *entry,
int vis_seq_print_text(struct seq_file *seq, void *offset)
{
+ struct hard_iface *primary_if;
struct hlist_node *node;
struct hlist_head *head;
struct vis_info *info;
@@ -215,15 +216,18 @@ int vis_seq_print_text(struct seq_file *seq, void *offset)
HLIST_HEAD(vis_if_list);
struct if_list_entry *entry;
struct hlist_node *pos, *n;
- int i, j;
+ int i, j, ret = 0;
int vis_server = atomic_read(&bat_priv->vis_mode);
size_t buff_pos, buf_size;
char *buff;
int compare;
- if ((!bat_priv->primary_if) ||
- (vis_server == VIS_TYPE_CLIENT_UPDATE))
- return 0;
+ primary_if = primary_if_get_selected(bat_priv);
+ if (!primary_if)
+ goto out;
+
+ if (vis_server == VIS_TYPE_CLIENT_UPDATE)
+ goto out;
buf_size = 1;
/* Estimate length */
@@ -270,7 +274,8 @@ int vis_seq_print_text(struct seq_file *seq, void *offset)
buff = kmalloc(buf_size, GFP_ATOMIC);
if (!buff) {
spin_unlock_bh(&bat_priv->vis_hash_lock);
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto out;
}
buff[0] = '\0';
buff_pos = 0;
@@ -328,7 +333,10 @@ int vis_seq_print_text(struct seq_file *seq, void *offset)
seq_printf(seq, "%s", buff);
kfree(buff);
- return 0;
+out:
+ if (primary_if)
+ hardif_free_ref(primary_if);
+ return ret;
}
/* add the info packet to the send list, if it was not
@@ -558,6 +566,7 @@ static int find_best_vis_server(struct bat_priv *bat_priv,
struct vis_info *info)
{
struct hashtable_t *hash = bat_priv->orig_hash;
+ struct neigh_node *router;
struct hlist_node *node;
struct hlist_head *head;
struct orig_node *orig_node;
@@ -571,13 +580,17 @@ static int find_best_vis_server(struct bat_priv *bat_priv,
rcu_read_lock();
hlist_for_each_entry_rcu(orig_node, node, head, hash_entry) {
- if ((orig_node) && (orig_node->router) &&
- (orig_node->flags & VIS_SERVER) &&
- (orig_node->router->tq_avg > best_tq)) {
- best_tq = orig_node->router->tq_avg;
+ router = orig_node_get_router(orig_node);
+ if (!router)
+ continue;
+
+ if ((orig_node->flags & VIS_SERVER) &&
+ (router->tq_avg > best_tq)) {
+ best_tq = router->tq_avg;
memcpy(packet->target_orig, orig_node->orig,
ETH_ALEN);
}
+ neigh_node_free_ref(router);
}
rcu_read_unlock();
}
@@ -605,11 +618,11 @@ static int generate_vis_packet(struct bat_priv *bat_priv)
struct hlist_node *node;
struct hlist_head *head;
struct orig_node *orig_node;
- struct neigh_node *neigh_node;
+ struct neigh_node *router;
struct vis_info *info = (struct vis_info *)bat_priv->my_vis_info;
struct vis_packet *packet = (struct vis_packet *)info->skb_packet->data;
struct vis_info_entry *entry;
- struct hna_local_entry *hna_local_entry;
+ struct tt_local_entry *tt_local_entry;
int best_tq = -1, i;
info->first_seen = jiffies;
@@ -633,59 +646,61 @@ static int generate_vis_packet(struct bat_priv *bat_priv)
rcu_read_lock();
hlist_for_each_entry_rcu(orig_node, node, head, hash_entry) {
- neigh_node = orig_node->router;
-
- if (!neigh_node)
+ router = orig_node_get_router(orig_node);
+ if (!router)
continue;
- if (!compare_eth(neigh_node->addr, orig_node->orig))
- continue;
+ if (!compare_eth(router->addr, orig_node->orig))
+ goto next;
- if (neigh_node->if_incoming->if_status != IF_ACTIVE)
- continue;
+ if (router->if_incoming->if_status != IF_ACTIVE)
+ goto next;
- if (neigh_node->tq_avg < 1)
- continue;
+ if (router->tq_avg < 1)
+ goto next;
/* fill one entry into buffer. */
entry = (struct vis_info_entry *)
skb_put(info->skb_packet, sizeof(*entry));
memcpy(entry->src,
- neigh_node->if_incoming->net_dev->dev_addr,
+ router->if_incoming->net_dev->dev_addr,
ETH_ALEN);
memcpy(entry->dest, orig_node->orig, ETH_ALEN);
- entry->quality = neigh_node->tq_avg;
+ entry->quality = router->tq_avg;
packet->entries++;
+next:
+ neigh_node_free_ref(router);
+
if (vis_packet_full(info))
goto unlock;
}
rcu_read_unlock();
}
- hash = bat_priv->hna_local_hash;
+ hash = bat_priv->tt_local_hash;
- spin_lock_bh(&bat_priv->hna_lhash_lock);
+ spin_lock_bh(&bat_priv->tt_lhash_lock);
for (i = 0; i < hash->size; i++) {
head = &hash->table[i];
- hlist_for_each_entry(hna_local_entry, node, head, hash_entry) {
+ hlist_for_each_entry(tt_local_entry, node, head, hash_entry) {
entry = (struct vis_info_entry *)
skb_put(info->skb_packet,
sizeof(*entry));
memset(entry->src, 0, ETH_ALEN);
- memcpy(entry->dest, hna_local_entry->addr, ETH_ALEN);
- entry->quality = 0; /* 0 means HNA */
+ memcpy(entry->dest, tt_local_entry->addr, ETH_ALEN);
+ entry->quality = 0; /* 0 means TT */
packet->entries++;
if (vis_packet_full(info)) {
- spin_unlock_bh(&bat_priv->hna_lhash_lock);
+ spin_unlock_bh(&bat_priv->tt_lhash_lock);
return 0;
}
}
}
- spin_unlock_bh(&bat_priv->hna_lhash_lock);
+ spin_unlock_bh(&bat_priv->tt_lhash_lock);
return 0;
unlock:
@@ -725,6 +740,7 @@ static void purge_vis_packets(struct bat_priv *bat_priv)
static void broadcast_vis_packet(struct bat_priv *bat_priv,
struct vis_info *info)
{
+ struct neigh_node *router;
struct hashtable_t *hash = bat_priv->orig_hash;
struct hlist_node *node;
struct hlist_head *head;
@@ -745,19 +761,26 @@ static void broadcast_vis_packet(struct bat_priv *bat_priv,
rcu_read_lock();
hlist_for_each_entry_rcu(orig_node, node, head, hash_entry) {
/* if it's a vis server and reachable, send it. */
- if ((!orig_node) || (!orig_node->router))
- continue;
if (!(orig_node->flags & VIS_SERVER))
continue;
+
+ router = orig_node_get_router(orig_node);
+ if (!router)
+ continue;
+
/* don't send it if we already received the packet from
- * this node. */
+ * this node. */
if (recv_list_is_in(bat_priv, &info->recv_list,
- orig_node->orig))
+ orig_node->orig)) {
+ neigh_node_free_ref(router);
continue;
+ }
memcpy(packet->target_orig, orig_node->orig, ETH_ALEN);
- hard_iface = orig_node->router->if_incoming;
- memcpy(dstaddr, orig_node->router->addr, ETH_ALEN);
+ hard_iface = router->if_incoming;
+ memcpy(dstaddr, router->addr, ETH_ALEN);
+
+ neigh_node_free_ref(router);
skb = skb_clone(info->skb_packet, GFP_ATOMIC);
if (skb)
@@ -772,60 +795,48 @@ static void unicast_vis_packet(struct bat_priv *bat_priv,
struct vis_info *info)
{
struct orig_node *orig_node;
- struct neigh_node *neigh_node = NULL;
+ struct neigh_node *router = NULL;
struct sk_buff *skb;
struct vis_packet *packet;
packet = (struct vis_packet *)info->skb_packet->data;
- rcu_read_lock();
orig_node = orig_hash_find(bat_priv, packet->target_orig);
-
if (!orig_node)
- goto unlock;
-
- neigh_node = orig_node->router;
+ goto out;
- if (!neigh_node)
- goto unlock;
-
- if (!atomic_inc_not_zero(&neigh_node->refcount)) {
- neigh_node = NULL;
- goto unlock;
- }
-
- rcu_read_unlock();
+ router = orig_node_get_router(orig_node);
+ if (!router)
+ goto out;
skb = skb_clone(info->skb_packet, GFP_ATOMIC);
if (skb)
- send_skb_packet(skb, neigh_node->if_incoming,
- neigh_node->addr);
+ send_skb_packet(skb, router->if_incoming, router->addr);
- goto out;
-
-unlock:
- rcu_read_unlock();
out:
- if (neigh_node)
- neigh_node_free_ref(neigh_node);
+ if (router)
+ neigh_node_free_ref(router);
if (orig_node)
orig_node_free_ref(orig_node);
- return;
}
/* only send one vis packet. called from send_vis_packets() */
static void send_vis_packet(struct bat_priv *bat_priv, struct vis_info *info)
{
+ struct hard_iface *primary_if;
struct vis_packet *packet;
+ primary_if = primary_if_get_selected(bat_priv);
+ if (!primary_if)
+ goto out;
+
packet = (struct vis_packet *)info->skb_packet->data;
if (packet->ttl < 2) {
pr_debug("Error - can't send vis packet: ttl exceeded\n");
- return;
+ goto out;
}
- memcpy(packet->sender_orig, bat_priv->primary_if->net_dev->dev_addr,
- ETH_ALEN);
+ memcpy(packet->sender_orig, primary_if->net_dev->dev_addr, ETH_ALEN);
packet->ttl--;
if (is_broadcast_ether_addr(packet->target_orig))
@@ -833,6 +844,10 @@ static void send_vis_packet(struct bat_priv *bat_priv, struct vis_info *info)
else
unicast_vis_packet(bat_priv, info);
packet->ttl++; /* restore TTL */
+
+out:
+ if (primary_if)
+ hardif_free_ref(primary_if);
}
/* called from timer; send (and maybe generate) vis packet. */
@@ -859,8 +874,7 @@ static void send_vis_packets(struct work_struct *work)
kref_get(&info->refcount);
spin_unlock_bh(&bat_priv->vis_hash_lock);
- if (bat_priv->primary_if)
- send_vis_packet(bat_priv, info);
+ send_vis_packet(bat_priv, info);
spin_lock_bh(&bat_priv->vis_hash_lock);
send_list_del(info);
diff --git a/net/bluetooth/bnep/bnep.h b/net/bluetooth/bnep/bnep.h
index 70672544db8..8e6c06158f8 100644
--- a/net/bluetooth/bnep/bnep.h
+++ b/net/bluetooth/bnep/bnep.h
@@ -23,88 +23,88 @@
#include <linux/crc32.h>
#include <net/bluetooth/bluetooth.h>
-// Limits
-#define BNEP_MAX_PROTO_FILTERS 5
-#define BNEP_MAX_MULTICAST_FILTERS 20
-
-// UUIDs
-#define BNEP_BASE_UUID 0x0000000000001000800000805F9B34FB
-#define BNEP_UUID16 0x02
-#define BNEP_UUID32 0x04
-#define BNEP_UUID128 0x16
-
-#define BNEP_SVC_PANU 0x1115
-#define BNEP_SVC_NAP 0x1116
-#define BNEP_SVC_GN 0x1117
-
-// Packet types
-#define BNEP_GENERAL 0x00
-#define BNEP_CONTROL 0x01
-#define BNEP_COMPRESSED 0x02
-#define BNEP_COMPRESSED_SRC_ONLY 0x03
-#define BNEP_COMPRESSED_DST_ONLY 0x04
-
-// Control types
-#define BNEP_CMD_NOT_UNDERSTOOD 0x00
-#define BNEP_SETUP_CONN_REQ 0x01
-#define BNEP_SETUP_CONN_RSP 0x02
-#define BNEP_FILTER_NET_TYPE_SET 0x03
-#define BNEP_FILTER_NET_TYPE_RSP 0x04
-#define BNEP_FILTER_MULTI_ADDR_SET 0x05
-#define BNEP_FILTER_MULTI_ADDR_RSP 0x06
-
-// Extension types
-#define BNEP_EXT_CONTROL 0x00
-
-// Response messages
-#define BNEP_SUCCESS 0x00
-
-#define BNEP_CONN_INVALID_DST 0x01
-#define BNEP_CONN_INVALID_SRC 0x02
-#define BNEP_CONN_INVALID_SVC 0x03
-#define BNEP_CONN_NOT_ALLOWED 0x04
-
-#define BNEP_FILTER_UNSUPPORTED_REQ 0x01
-#define BNEP_FILTER_INVALID_RANGE 0x02
-#define BNEP_FILTER_INVALID_MCADDR 0x02
-#define BNEP_FILTER_LIMIT_REACHED 0x03
-#define BNEP_FILTER_DENIED_SECURITY 0x04
-
-// L2CAP settings
-#define BNEP_MTU 1691
-#define BNEP_PSM 0x0f
-#define BNEP_FLUSH_TO 0xffff
-#define BNEP_CONNECT_TO 15
-#define BNEP_FILTER_TO 15
-
-// Headers
-#define BNEP_TYPE_MASK 0x7f
-#define BNEP_EXT_HEADER 0x80
+/* Limits */
+#define BNEP_MAX_PROTO_FILTERS 5
+#define BNEP_MAX_MULTICAST_FILTERS 20
+
+/* UUIDs */
+#define BNEP_BASE_UUID 0x0000000000001000800000805F9B34FB
+#define BNEP_UUID16 0x02
+#define BNEP_UUID32 0x04
+#define BNEP_UUID128 0x16
+
+#define BNEP_SVC_PANU 0x1115
+#define BNEP_SVC_NAP 0x1116
+#define BNEP_SVC_GN 0x1117
+
+/* Packet types */
+#define BNEP_GENERAL 0x00
+#define BNEP_CONTROL 0x01
+#define BNEP_COMPRESSED 0x02
+#define BNEP_COMPRESSED_SRC_ONLY 0x03
+#define BNEP_COMPRESSED_DST_ONLY 0x04
+
+/* Control types */
+#define BNEP_CMD_NOT_UNDERSTOOD 0x00
+#define BNEP_SETUP_CONN_REQ 0x01
+#define BNEP_SETUP_CONN_RSP 0x02
+#define BNEP_FILTER_NET_TYPE_SET 0x03
+#define BNEP_FILTER_NET_TYPE_RSP 0x04
+#define BNEP_FILTER_MULTI_ADDR_SET 0x05
+#define BNEP_FILTER_MULTI_ADDR_RSP 0x06
+
+/* Extension types */
+#define BNEP_EXT_CONTROL 0x00
+
+/* Response messages */
+#define BNEP_SUCCESS 0x00
+
+#define BNEP_CONN_INVALID_DST 0x01
+#define BNEP_CONN_INVALID_SRC 0x02
+#define BNEP_CONN_INVALID_SVC 0x03
+#define BNEP_CONN_NOT_ALLOWED 0x04
+
+#define BNEP_FILTER_UNSUPPORTED_REQ 0x01
+#define BNEP_FILTER_INVALID_RANGE 0x02
+#define BNEP_FILTER_INVALID_MCADDR 0x02
+#define BNEP_FILTER_LIMIT_REACHED 0x03
+#define BNEP_FILTER_DENIED_SECURITY 0x04
+
+/* L2CAP settings */
+#define BNEP_MTU 1691
+#define BNEP_PSM 0x0f
+#define BNEP_FLUSH_TO 0xffff
+#define BNEP_CONNECT_TO 15
+#define BNEP_FILTER_TO 15
+
+/* Headers */
+#define BNEP_TYPE_MASK 0x7f
+#define BNEP_EXT_HEADER 0x80
struct bnep_setup_conn_req {
- __u8 type;
- __u8 ctrl;
- __u8 uuid_size;
- __u8 service[0];
+ __u8 type;
+ __u8 ctrl;
+ __u8 uuid_size;
+ __u8 service[0];
} __packed;
struct bnep_set_filter_req {
- __u8 type;
- __u8 ctrl;
+ __u8 type;
+ __u8 ctrl;
__be16 len;
- __u8 list[0];
+ __u8 list[0];
} __packed;
struct bnep_control_rsp {
- __u8 type;
- __u8 ctrl;
+ __u8 type;
+ __u8 ctrl;
__be16 resp;
} __packed;
struct bnep_ext_hdr {
- __u8 type;
- __u8 len;
- __u8 data[0];
+ __u8 type;
+ __u8 len;
+ __u8 data[0];
} __packed;
/* BNEP ioctl defines */
@@ -114,10 +114,10 @@ struct bnep_ext_hdr {
#define BNEPGETCONNINFO _IOR('B', 211, int)
struct bnep_connadd_req {
- int sock; // Connected socket
+ int sock; /* Connected socket */
__u32 flags;
__u16 role;
- char device[16]; // Name of the Ethernet device
+ char device[16]; /* Name of the Ethernet device */
};
struct bnep_conndel_req {
@@ -148,14 +148,14 @@ int bnep_del_connection(struct bnep_conndel_req *req);
int bnep_get_connlist(struct bnep_connlist_req *req);
int bnep_get_conninfo(struct bnep_conninfo *ci);
-// BNEP sessions
+/* BNEP sessions */
struct bnep_session {
struct list_head list;
unsigned int role;
unsigned long state;
unsigned long flags;
- atomic_t killed;
+ struct task_struct *task;
struct ethhdr eh;
struct msghdr msg;
@@ -173,7 +173,7 @@ void bnep_sock_cleanup(void);
static inline int bnep_mc_hash(__u8 *addr)
{
- return (crc32_be(~0, addr, ETH_ALEN) >> 26);
+ return crc32_be(~0, addr, ETH_ALEN) >> 26;
}
#endif
diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c
index 03d4d1245d5..ca39fcf010c 100644
--- a/net/bluetooth/bnep/core.c
+++ b/net/bluetooth/bnep/core.c
@@ -36,6 +36,7 @@
#include <linux/errno.h>
#include <linux/net.h>
#include <linux/slab.h>
+#include <linux/kthread.h>
#include <net/sock.h>
#include <linux/socket.h>
@@ -131,7 +132,8 @@ static int bnep_ctrl_set_netfilter(struct bnep_session *s, __be16 *data, int len
return -EILSEQ;
n = get_unaligned_be16(data);
- data++; len -= 2;
+ data++;
+ len -= 2;
if (len < n)
return -EILSEQ;
@@ -176,7 +178,8 @@ static int bnep_ctrl_set_mcfilter(struct bnep_session *s, u8 *data, int len)
return -EILSEQ;
n = get_unaligned_be16(data);
- data += 2; len -= 2;
+ data += 2;
+ len -= 2;
if (len < n)
return -EILSEQ;
@@ -187,6 +190,8 @@ static int bnep_ctrl_set_mcfilter(struct bnep_session *s, u8 *data, int len)
n /= (ETH_ALEN * 2);
if (n > 0) {
+ int i;
+
s->mc_filter = 0;
/* Always send broadcast */
@@ -196,18 +201,22 @@ static int bnep_ctrl_set_mcfilter(struct bnep_session *s, u8 *data, int len)
for (; n > 0; n--) {
u8 a1[6], *a2;
- memcpy(a1, data, ETH_ALEN); data += ETH_ALEN;
- a2 = data; data += ETH_ALEN;
+ memcpy(a1, data, ETH_ALEN);
+ data += ETH_ALEN;
+ a2 = data;
+ data += ETH_ALEN;
BT_DBG("mc filter %s -> %s",
batostr((void *) a1), batostr((void *) a2));
- #define INCA(a) { int i = 5; while (i >=0 && ++a[i--] == 0); }
-
/* Iterate from a1 to a2 */
set_bit(bnep_mc_hash(a1), (ulong *) &s->mc_filter);
while (memcmp(a1, a2, 6) < 0 && s->mc_filter != ~0LL) {
- INCA(a1);
+ /* Increment a1 */
+ i = 5;
+ while (i >= 0 && ++a1[i--] == 0)
+ ;
+
set_bit(bnep_mc_hash(a1), (ulong *) &s->mc_filter);
}
}
@@ -227,7 +236,8 @@ static int bnep_rx_control(struct bnep_session *s, void *data, int len)
u8 cmd = *(u8 *)data;
int err = 0;
- data++; len--;
+ data++;
+ len--;
switch (cmd) {
case BNEP_CMD_NOT_UNDERSTOOD:
@@ -302,7 +312,6 @@ static u8 __bnep_rx_hlen[] = {
ETH_ALEN + 2, /* BNEP_COMPRESSED_SRC_ONLY */
ETH_ALEN + 2 /* BNEP_COMPRESSED_DST_ONLY */
};
-#define BNEP_RX_TYPES (sizeof(__bnep_rx_hlen) - 1)
static inline int bnep_rx_frame(struct bnep_session *s, struct sk_buff *skb)
{
@@ -312,9 +321,10 @@ static inline int bnep_rx_frame(struct bnep_session *s, struct sk_buff *skb)
dev->stats.rx_bytes += skb->len;
- type = *(u8 *) skb->data; skb_pull(skb, 1);
+ type = *(u8 *) skb->data;
+ skb_pull(skb, 1);
- if ((type & BNEP_TYPE_MASK) > BNEP_RX_TYPES)
+ if ((type & BNEP_TYPE_MASK) >= sizeof(__bnep_rx_hlen))
goto badframe;
if ((type & BNEP_TYPE_MASK) == BNEP_CONTROL) {
@@ -367,14 +377,14 @@ static inline int bnep_rx_frame(struct bnep_session *s, struct sk_buff *skb)
case BNEP_COMPRESSED_DST_ONLY:
memcpy(__skb_put(nskb, ETH_ALEN), skb_mac_header(skb),
- ETH_ALEN);
+ ETH_ALEN);
memcpy(__skb_put(nskb, ETH_ALEN + 2), s->eh.h_source,
- ETH_ALEN + 2);
+ ETH_ALEN + 2);
break;
case BNEP_GENERAL:
memcpy(__skb_put(nskb, ETH_ALEN * 2), skb_mac_header(skb),
- ETH_ALEN * 2);
+ ETH_ALEN * 2);
put_unaligned(s->eh.h_proto, (__be16 *) __skb_put(nskb, 2));
break;
}
@@ -470,15 +480,14 @@ static int bnep_session(void *arg)
BT_DBG("");
- daemonize("kbnepd %s", dev->name);
set_user_nice(current, -15);
init_waitqueue_entry(&wait, current);
add_wait_queue(sk_sleep(sk), &wait);
- while (!atomic_read(&s->killed)) {
+ while (!kthread_should_stop()) {
set_current_state(TASK_INTERRUPTIBLE);
- // RX
+ /* RX */
while ((skb = skb_dequeue(&sk->sk_receive_queue))) {
skb_orphan(skb);
bnep_rx_frame(s, skb);
@@ -487,7 +496,7 @@ static int bnep_session(void *arg)
if (sk->sk_state != BT_CONNECTED)
break;
- // TX
+ /* TX */
while ((skb = skb_dequeue(&sk->sk_write_queue)))
if (bnep_tx_frame(s, skb))
break;
@@ -555,8 +564,8 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
/* session struct allocated as private part of net_device */
dev = alloc_netdev(sizeof(struct bnep_session),
- (*req->device) ? req->device : "bnep%d",
- bnep_net_setup);
+ (*req->device) ? req->device : "bnep%d",
+ bnep_net_setup);
if (!dev)
return -ENOMEM;
@@ -571,7 +580,7 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
s = netdev_priv(dev);
/* This is rx header therefore addresses are swapped.
- * ie eh.h_dest is our local address. */
+ * ie. eh.h_dest is our local address. */
memcpy(s->eh.h_dest, &src, ETH_ALEN);
memcpy(s->eh.h_source, &dst, ETH_ALEN);
memcpy(dev->dev_addr, s->eh.h_dest, ETH_ALEN);
@@ -597,17 +606,17 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
SET_NETDEV_DEVTYPE(dev, &bnep_type);
err = register_netdev(dev);
- if (err) {
+ if (err)
goto failed;
- }
__bnep_link_session(s);
- err = kernel_thread(bnep_session, s, CLONE_KERNEL);
- if (err < 0) {
+ s->task = kthread_run(bnep_session, s, "kbnepd %s", dev->name);
+ if (IS_ERR(s->task)) {
/* Session thread start failed, gotta cleanup. */
unregister_netdev(dev);
__bnep_unlink_session(s);
+ err = PTR_ERR(s->task);
goto failed;
}
@@ -631,15 +640,9 @@ int bnep_del_connection(struct bnep_conndel_req *req)
down_read(&bnep_session_sem);
s = __bnep_get_session(req->dst);
- if (s) {
- /* Wakeup user-space which is polling for socket errors.
- * This is temporary hack until we have shutdown in L2CAP */
- s->sock->sk->sk_err = EUNATCH;
-
- /* Kill session thread */
- atomic_inc(&s->killed);
- wake_up_interruptible(sk_sleep(s->sock->sk));
- } else
+ if (s)
+ kthread_stop(s->task);
+ else
err = -ENOENT;
up_read(&bnep_session_sem);
diff --git a/net/bluetooth/bnep/sock.c b/net/bluetooth/bnep/sock.c
index d935da71ab3..17800b1d28e 100644
--- a/net/bluetooth/bnep/sock.c
+++ b/net/bluetooth/bnep/sock.c
@@ -39,10 +39,10 @@
#include <linux/init.h>
#include <linux/compat.h>
#include <linux/gfp.h>
+#include <linux/uaccess.h>
#include <net/sock.h>
#include <asm/system.h>
-#include <asm/uaccess.h>
#include "bnep.h"
diff --git a/net/bluetooth/cmtp/capi.c b/net/bluetooth/cmtp/capi.c
index 67cff810c77..744233cba24 100644
--- a/net/bluetooth/cmtp/capi.c
+++ b/net/bluetooth/cmtp/capi.c
@@ -35,6 +35,7 @@
#include <linux/ioctl.h>
#include <linux/file.h>
#include <linux/wait.h>
+#include <linux/kthread.h>
#include <net/sock.h>
#include <linux/isdn/capilli.h>
@@ -143,7 +144,7 @@ static void cmtp_send_capimsg(struct cmtp_session *session, struct sk_buff *skb)
skb_queue_tail(&session->transmit, skb);
- cmtp_schedule(session);
+ wake_up_interruptible(sk_sleep(session->sock->sk));
}
static void cmtp_send_interopmsg(struct cmtp_session *session,
@@ -386,8 +387,7 @@ static void cmtp_reset_ctr(struct capi_ctr *ctrl)
capi_ctr_down(ctrl);
- atomic_inc(&session->terminate);
- cmtp_schedule(session);
+ kthread_stop(session->task);
}
static void cmtp_register_appl(struct capi_ctr *ctrl, __u16 appl, capi_register_params *rp)
diff --git a/net/bluetooth/cmtp/cmtp.h b/net/bluetooth/cmtp/cmtp.h
index 785e79e953c..db43b54ac9a 100644
--- a/net/bluetooth/cmtp/cmtp.h
+++ b/net/bluetooth/cmtp/cmtp.h
@@ -37,7 +37,7 @@
#define CMTP_LOOPBACK 0
struct cmtp_connadd_req {
- int sock; // Connected socket
+ int sock; /* Connected socket */
__u32 flags;
};
@@ -81,7 +81,7 @@ struct cmtp_session {
char name[BTNAMSIZ];
- atomic_t terminate;
+ struct task_struct *task;
wait_queue_head_t wait;
@@ -121,13 +121,6 @@ void cmtp_detach_device(struct cmtp_session *session);
void cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb);
-static inline void cmtp_schedule(struct cmtp_session *session)
-{
- struct sock *sk = session->sock->sk;
-
- wake_up_interruptible(sk_sleep(sk));
-}
-
/* CMTP init defines */
int cmtp_init_sockets(void);
void cmtp_cleanup_sockets(void);
diff --git a/net/bluetooth/cmtp/core.c b/net/bluetooth/cmtp/core.c
index 964ea9126f9..c5b11af908b 100644
--- a/net/bluetooth/cmtp/core.c
+++ b/net/bluetooth/cmtp/core.c
@@ -35,6 +35,7 @@
#include <linux/ioctl.h>
#include <linux/file.h>
#include <linux/init.h>
+#include <linux/kthread.h>
#include <net/sock.h>
#include <linux/isdn/capilli.h>
@@ -235,9 +236,12 @@ static void cmtp_process_transmit(struct cmtp_session *session)
size = min_t(uint, ((tail < 258) ? (tail - 2) : (tail - 3)), skb->len);
- if ((scb->id < 0) && ((scb->id = cmtp_alloc_block_id(session)) < 0)) {
- skb_queue_head(&session->transmit, skb);
- break;
+ if (scb->id < 0) {
+ scb->id = cmtp_alloc_block_id(session);
+ if (scb->id < 0) {
+ skb_queue_head(&session->transmit, skb);
+ break;
+ }
}
if (size < 256) {
@@ -284,12 +288,11 @@ static int cmtp_session(void *arg)
BT_DBG("session %p", session);
- daemonize("kcmtpd_ctr_%d", session->num);
set_user_nice(current, -15);
init_waitqueue_entry(&wait, current);
add_wait_queue(sk_sleep(sk), &wait);
- while (!atomic_read(&session->terminate)) {
+ while (!kthread_should_stop()) {
set_current_state(TASK_INTERRUPTIBLE);
if (sk->sk_state != BT_CONNECTED)
@@ -343,7 +346,8 @@ int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock)
bacpy(&session->bdaddr, &bt_sk(sock->sk)->dst);
- session->mtu = min_t(uint, l2cap_pi(sock->sk)->omtu, l2cap_pi(sock->sk)->imtu);
+ session->mtu = min_t(uint, l2cap_pi(sock->sk)->chan->omtu,
+ l2cap_pi(sock->sk)->chan->imtu);
BT_DBG("mtu %d", session->mtu);
@@ -367,9 +371,12 @@ int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock)
__cmtp_link_session(session);
- err = kernel_thread(cmtp_session, session, CLONE_KERNEL);
- if (err < 0)
+ session->task = kthread_run(cmtp_session, session, "kcmtpd_ctr_%d",
+ session->num);
+ if (IS_ERR(session->task)) {
+ err = PTR_ERR(session->task);
goto unlink;
+ }
if (!(session->flags & (1 << CMTP_LOOPBACK))) {
err = cmtp_attach_device(session);
@@ -406,9 +413,8 @@ int cmtp_del_connection(struct cmtp_conndel_req *req)
/* Flush the transmit queue */
skb_queue_purge(&session->transmit);
- /* Kill session thread */
- atomic_inc(&session->terminate);
- cmtp_schedule(session);
+ /* Stop session thread */
+ kthread_stop(session->task);
} else
err = -ENOENT;
diff --git a/net/bluetooth/cmtp/sock.c b/net/bluetooth/cmtp/sock.c
index 7ea1979a8e4..3f2dd5c25ae 100644
--- a/net/bluetooth/cmtp/sock.c
+++ b/net/bluetooth/cmtp/sock.c
@@ -34,12 +34,12 @@
#include <linux/file.h>
#include <linux/compat.h>
#include <linux/gfp.h>
+#include <linux/uaccess.h>
#include <net/sock.h>
#include <linux/isdn/capilli.h>
#include <asm/system.h>
-#include <asm/uaccess.h>
#include "cmtp.h"
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index 7a6f56b2f49..3163330cd4f 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -269,6 +269,19 @@ static void hci_conn_idle(unsigned long arg)
hci_conn_enter_sniff_mode(conn);
}
+static void hci_conn_auto_accept(unsigned long arg)
+{
+ struct hci_conn *conn = (void *) arg;
+ struct hci_dev *hdev = conn->hdev;
+
+ hci_dev_lock(hdev);
+
+ hci_send_cmd(hdev, HCI_OP_USER_CONFIRM_REPLY, sizeof(conn->dst),
+ &conn->dst);
+
+ hci_dev_unlock(hdev);
+}
+
struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
{
struct hci_conn *conn;
@@ -287,6 +300,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
conn->auth_type = HCI_AT_GENERAL_BONDING;
conn->io_capability = hdev->io_capability;
conn->remote_auth = 0xff;
+ conn->key_type = 0xff;
conn->power_save = 1;
conn->disc_timeout = HCI_DISCONN_TIMEOUT;
@@ -311,6 +325,8 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
setup_timer(&conn->disc_timer, hci_conn_timeout, (unsigned long)conn);
setup_timer(&conn->idle_timer, hci_conn_idle, (unsigned long)conn);
+ setup_timer(&conn->auto_accept_timer, hci_conn_auto_accept,
+ (unsigned long) conn);
atomic_set(&conn->refcnt, 0);
@@ -341,6 +357,8 @@ int hci_conn_del(struct hci_conn *conn)
del_timer(&conn->disc_timer);
+ del_timer(&conn->auto_accept_timer);
+
if (conn->type == ACL_LINK) {
struct hci_conn *sco = conn->link;
if (sco)
@@ -535,36 +553,93 @@ static int hci_conn_auth(struct hci_conn *conn, __u8 sec_level, __u8 auth_type)
return 0;
}
+/* Encrypt the the link */
+static void hci_conn_encrypt(struct hci_conn *conn)
+{
+ BT_DBG("conn %p", conn);
+
+ if (!test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) {
+ struct hci_cp_set_conn_encrypt cp;
+ cp.handle = cpu_to_le16(conn->handle);
+ cp.encrypt = 0x01;
+ hci_send_cmd(conn->hdev, HCI_OP_SET_CONN_ENCRYPT, sizeof(cp),
+ &cp);
+ }
+}
+
/* Enable security */
int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type)
{
BT_DBG("conn %p", conn);
+ /* For sdp we don't need the link key. */
if (sec_level == BT_SECURITY_SDP)
return 1;
+ /* For non 2.1 devices and low security level we don't need the link
+ key. */
if (sec_level == BT_SECURITY_LOW &&
(!conn->ssp_mode || !conn->hdev->ssp_mode))
return 1;
- if (conn->link_mode & HCI_LM_ENCRYPT)
- return hci_conn_auth(conn, sec_level, auth_type);
-
+ /* For other security levels we need the link key. */
+ if (!(conn->link_mode & HCI_LM_AUTH))
+ goto auth;
+
+ /* An authenticated combination key has sufficient security for any
+ security level. */
+ if (conn->key_type == HCI_LK_AUTH_COMBINATION)
+ goto encrypt;
+
+ /* An unauthenticated combination key has sufficient security for
+ security level 1 and 2. */
+ if (conn->key_type == HCI_LK_UNAUTH_COMBINATION &&
+ (sec_level == BT_SECURITY_MEDIUM ||
+ sec_level == BT_SECURITY_LOW))
+ goto encrypt;
+
+ /* A combination key has always sufficient security for the security
+ levels 1 or 2. High security level requires the combination key
+ is generated using maximum PIN code length (16).
+ For pre 2.1 units. */
+ if (conn->key_type == HCI_LK_COMBINATION &&
+ (sec_level != BT_SECURITY_HIGH ||
+ conn->pin_length == 16))
+ goto encrypt;
+
+auth:
if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend))
return 0;
- if (hci_conn_auth(conn, sec_level, auth_type)) {
- struct hci_cp_set_conn_encrypt cp;
- cp.handle = cpu_to_le16(conn->handle);
- cp.encrypt = 1;
- hci_send_cmd(conn->hdev, HCI_OP_SET_CONN_ENCRYPT,
- sizeof(cp), &cp);
- }
+ hci_conn_auth(conn, sec_level, auth_type);
+ return 0;
+
+encrypt:
+ if (conn->link_mode & HCI_LM_ENCRYPT)
+ return 1;
+ hci_conn_encrypt(conn);
return 0;
}
EXPORT_SYMBOL(hci_conn_security);
+/* Check secure link requirement */
+int hci_conn_check_secure(struct hci_conn *conn, __u8 sec_level)
+{
+ BT_DBG("conn %p", conn);
+
+ if (sec_level != BT_SECURITY_HIGH)
+ return 1; /* Accept if non-secure is required */
+
+ if (conn->key_type == HCI_LK_AUTH_COMBINATION ||
+ (conn->key_type == HCI_LK_COMBINATION &&
+ conn->pin_length == 16))
+ return 1;
+
+ return 0; /* Reject not secure link */
+}
+EXPORT_SYMBOL(hci_conn_check_secure);
+
/* Change link key */
int hci_conn_change_link_key(struct hci_conn *conn)
{
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index b5a8afc2be3..815269b07f2 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -56,7 +56,6 @@
static void hci_cmd_task(unsigned long arg);
static void hci_rx_task(unsigned long arg);
static void hci_tx_task(unsigned long arg);
-static void hci_notify(struct hci_dev *hdev, int event);
static DEFINE_RWLOCK(hci_task_lock);
@@ -1021,18 +1020,54 @@ struct link_key *hci_find_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr)
return NULL;
}
-int hci_add_link_key(struct hci_dev *hdev, int new_key, bdaddr_t *bdaddr,
- u8 *val, u8 type, u8 pin_len)
+static int hci_persistent_key(struct hci_dev *hdev, struct hci_conn *conn,
+ u8 key_type, u8 old_key_type)
+{
+ /* Legacy key */
+ if (key_type < 0x03)
+ return 1;
+
+ /* Debug keys are insecure so don't store them persistently */
+ if (key_type == HCI_LK_DEBUG_COMBINATION)
+ return 0;
+
+ /* Changed combination key and there's no previous one */
+ if (key_type == HCI_LK_CHANGED_COMBINATION && old_key_type == 0xff)
+ return 0;
+
+ /* Security mode 3 case */
+ if (!conn)
+ return 1;
+
+ /* Neither local nor remote side had no-bonding as requirement */
+ if (conn->auth_type > 0x01 && conn->remote_auth > 0x01)
+ return 1;
+
+ /* Local side had dedicated bonding as requirement */
+ if (conn->auth_type == 0x02 || conn->auth_type == 0x03)
+ return 1;
+
+ /* Remote side had dedicated bonding as requirement */
+ if (conn->remote_auth == 0x02 || conn->remote_auth == 0x03)
+ return 1;
+
+ /* If none of the above criteria match, then don't store the key
+ * persistently */
+ return 0;
+}
+
+int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key,
+ bdaddr_t *bdaddr, u8 *val, u8 type, u8 pin_len)
{
struct link_key *key, *old_key;
- u8 old_key_type;
+ u8 old_key_type, persistent;
old_key = hci_find_link_key(hdev, bdaddr);
if (old_key) {
old_key_type = old_key->type;
key = old_key;
} else {
- old_key_type = 0xff;
+ old_key_type = conn ? conn->key_type : 0xff;
key = kzalloc(sizeof(*key), GFP_ATOMIC);
if (!key)
return -ENOMEM;
@@ -1041,16 +1076,37 @@ int hci_add_link_key(struct hci_dev *hdev, int new_key, bdaddr_t *bdaddr,
BT_DBG("%s key for %s type %u", hdev->name, batostr(bdaddr), type);
+ /* Some buggy controller combinations generate a changed
+ * combination key for legacy pairing even when there's no
+ * previous key */
+ if (type == HCI_LK_CHANGED_COMBINATION &&
+ (!conn || conn->remote_auth == 0xff) &&
+ old_key_type == 0xff) {
+ type = HCI_LK_COMBINATION;
+ if (conn)
+ conn->key_type = type;
+ }
+
bacpy(&key->bdaddr, bdaddr);
memcpy(key->val, val, 16);
- key->type = type;
key->pin_len = pin_len;
- if (new_key)
- mgmt_new_key(hdev->id, key, old_key_type);
-
- if (type == 0x06)
+ if (type == HCI_LK_CHANGED_COMBINATION)
key->type = old_key_type;
+ else
+ key->type = type;
+
+ if (!new_key)
+ return 0;
+
+ persistent = hci_persistent_key(hdev, conn, type, old_key_type);
+
+ mgmt_new_key(hdev->id, key, persistent);
+
+ if (!persistent) {
+ list_del(&key->list);
+ kfree(key);
+ }
return 0;
}
@@ -1082,6 +1138,70 @@ static void hci_cmd_timer(unsigned long arg)
tasklet_schedule(&hdev->cmd_task);
}
+struct oob_data *hci_find_remote_oob_data(struct hci_dev *hdev,
+ bdaddr_t *bdaddr)
+{
+ struct oob_data *data;
+
+ list_for_each_entry(data, &hdev->remote_oob_data, list)
+ if (bacmp(bdaddr, &data->bdaddr) == 0)
+ return data;
+
+ return NULL;
+}
+
+int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr)
+{
+ struct oob_data *data;
+
+ data = hci_find_remote_oob_data(hdev, bdaddr);
+ if (!data)
+ return -ENOENT;
+
+ BT_DBG("%s removing %s", hdev->name, batostr(bdaddr));
+
+ list_del(&data->list);
+ kfree(data);
+
+ return 0;
+}
+
+int hci_remote_oob_data_clear(struct hci_dev *hdev)
+{
+ struct oob_data *data, *n;
+
+ list_for_each_entry_safe(data, n, &hdev->remote_oob_data, list) {
+ list_del(&data->list);
+ kfree(data);
+ }
+
+ return 0;
+}
+
+int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash,
+ u8 *randomizer)
+{
+ struct oob_data *data;
+
+ data = hci_find_remote_oob_data(hdev, bdaddr);
+
+ if (!data) {
+ data = kmalloc(sizeof(*data), GFP_ATOMIC);
+ if (!data)
+ return -ENOMEM;
+
+ bacpy(&data->bdaddr, bdaddr);
+ list_add(&data->list, &hdev->remote_oob_data);
+ }
+
+ memcpy(data->hash, hash, sizeof(data->hash));
+ memcpy(data->randomizer, randomizer, sizeof(data->randomizer));
+
+ BT_DBG("%s for %s", hdev->name, batostr(bdaddr));
+
+ return 0;
+}
+
/* Register HCI device */
int hci_register_dev(struct hci_dev *hdev)
{
@@ -1146,6 +1266,8 @@ int hci_register_dev(struct hci_dev *hdev)
INIT_LIST_HEAD(&hdev->link_keys);
+ INIT_LIST_HEAD(&hdev->remote_oob_data);
+
INIT_WORK(&hdev->power_on, hci_power_on);
INIT_WORK(&hdev->power_off, hci_power_off);
setup_timer(&hdev->off_timer, hci_auto_off, (unsigned long) hdev);
@@ -1225,6 +1347,7 @@ int hci_unregister_dev(struct hci_dev *hdev)
hci_blacklist_clear(hdev);
hci_uuids_clear(hdev);
hci_link_keys_clear(hdev);
+ hci_remote_oob_data_clear(hdev);
hci_dev_unlock_bh(hdev);
__hci_dev_put(hdev);
@@ -1274,7 +1397,7 @@ int hci_recv_frame(struct sk_buff *skb)
EXPORT_SYMBOL(hci_recv_frame);
static int hci_reassembly(struct hci_dev *hdev, int type, void *data,
- int count, __u8 index, gfp_t gfp_mask)
+ int count, __u8 index)
{
int len = 0;
int hlen = 0;
@@ -1304,7 +1427,7 @@ static int hci_reassembly(struct hci_dev *hdev, int type, void *data,
break;
}
- skb = bt_skb_alloc(len, gfp_mask);
+ skb = bt_skb_alloc(len, GFP_ATOMIC);
if (!skb)
return -ENOMEM;
@@ -1390,8 +1513,7 @@ int hci_recv_fragment(struct hci_dev *hdev, int type, void *data, int count)
return -EILSEQ;
while (count) {
- rem = hci_reassembly(hdev, type, data, count,
- type - 1, GFP_ATOMIC);
+ rem = hci_reassembly(hdev, type, data, count, type - 1);
if (rem < 0)
return rem;
@@ -1425,8 +1547,8 @@ int hci_recv_stream_fragment(struct hci_dev *hdev, void *data, int count)
} else
type = bt_cb(skb)->pkt_type;
- rem = hci_reassembly(hdev, type, data,
- count, STREAM_REASSEMBLY, GFP_ATOMIC);
+ rem = hci_reassembly(hdev, type, data, count,
+ STREAM_REASSEMBLY);
if (rem < 0)
return rem;
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index b2570159a04..f13ddbf858b 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -56,7 +56,9 @@ static void hci_cc_inquiry_cancel(struct hci_dev *hdev, struct sk_buff *skb)
if (status)
return;
- clear_bit(HCI_INQUIRY, &hdev->flags);
+ if (test_bit(HCI_MGMT, &hdev->flags) &&
+ test_and_clear_bit(HCI_INQUIRY, &hdev->flags))
+ mgmt_discovering(hdev->id, 0);
hci_req_complete(hdev, HCI_OP_INQUIRY_CANCEL, status);
@@ -72,7 +74,9 @@ static void hci_cc_exit_periodic_inq(struct hci_dev *hdev, struct sk_buff *skb)
if (status)
return;
- clear_bit(HCI_INQUIRY, &hdev->flags);
+ if (test_bit(HCI_MGMT, &hdev->flags) &&
+ test_and_clear_bit(HCI_INQUIRY, &hdev->flags))
+ mgmt_discovering(hdev->id, 0);
hci_conn_check_pending(hdev);
}
@@ -195,14 +199,17 @@ static void hci_cc_write_local_name(struct hci_dev *hdev, struct sk_buff *skb)
BT_DBG("%s status 0x%x", hdev->name, status);
- if (status)
- return;
-
sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_LOCAL_NAME);
if (!sent)
return;
- memcpy(hdev->dev_name, sent, 248);
+ if (test_bit(HCI_MGMT, &hdev->flags))
+ mgmt_set_local_name_complete(hdev->id, sent, status);
+
+ if (status)
+ return;
+
+ memcpy(hdev->dev_name, sent, HCI_MAX_NAME_LENGTH);
}
static void hci_cc_read_local_name(struct hci_dev *hdev, struct sk_buff *skb)
@@ -214,7 +221,7 @@ static void hci_cc_read_local_name(struct hci_dev *hdev, struct sk_buff *skb)
if (rp->status)
return;
- memcpy(hdev->dev_name, rp->name, 248);
+ memcpy(hdev->dev_name, rp->name, HCI_MAX_NAME_LENGTH);
}
static void hci_cc_write_auth_enable(struct hci_dev *hdev, struct sk_buff *skb)
@@ -821,16 +828,31 @@ static void hci_cc_user_confirm_neg_reply(struct hci_dev *hdev,
rp->status);
}
+static void hci_cc_read_local_oob_data_reply(struct hci_dev *hdev,
+ struct sk_buff *skb)
+{
+ struct hci_rp_read_local_oob_data *rp = (void *) skb->data;
+
+ BT_DBG("%s status 0x%x", hdev->name, rp->status);
+
+ mgmt_read_local_oob_data_reply_complete(hdev->id, rp->hash,
+ rp->randomizer, rp->status);
+}
+
static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status)
{
BT_DBG("%s status 0x%x", hdev->name, status);
if (status) {
hci_req_complete(hdev, HCI_OP_INQUIRY, status);
-
hci_conn_check_pending(hdev);
- } else
- set_bit(HCI_INQUIRY, &hdev->flags);
+ return;
+ }
+
+ if (test_bit(HCI_MGMT, &hdev->flags) &&
+ !test_and_set_bit(HCI_INQUIRY,
+ &hdev->flags))
+ mgmt_discovering(hdev->id, 1);
}
static inline void hci_cs_create_conn(struct hci_dev *hdev, __u8 status)
@@ -999,12 +1021,19 @@ static void hci_cs_remote_name_req(struct hci_dev *hdev, __u8 status)
hci_dev_lock(hdev);
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr);
- if (conn && hci_outgoing_auth_needed(hdev, conn)) {
+ if (!conn)
+ goto unlock;
+
+ if (!hci_outgoing_auth_needed(hdev, conn))
+ goto unlock;
+
+ if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->pend)) {
struct hci_cp_auth_requested cp;
cp.handle = __cpu_to_le16(conn->handle);
hci_send_cmd(hdev, HCI_OP_AUTH_REQUESTED, sizeof(cp), &cp);
}
+unlock:
hci_dev_unlock(hdev);
}
@@ -1194,7 +1223,9 @@ static inline void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff
BT_DBG("%s status %d", hdev->name, status);
- clear_bit(HCI_INQUIRY, &hdev->flags);
+ if (test_bit(HCI_MGMT, &hdev->flags) &&
+ test_and_clear_bit(HCI_INQUIRY, &hdev->flags))
+ mgmt_discovering(hdev->id, 0);
hci_req_complete(hdev, HCI_OP_INQUIRY, status);
@@ -1214,7 +1245,13 @@ static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *
hci_dev_lock(hdev);
- for (; num_rsp; num_rsp--) {
+ if (!test_and_set_bit(HCI_INQUIRY, &hdev->flags)) {
+
+ if (test_bit(HCI_MGMT, &hdev->flags))
+ mgmt_discovering(hdev->id, 1);
+ }
+
+ for (; num_rsp; num_rsp--, info++) {
bacpy(&data.bdaddr, &info->bdaddr);
data.pscan_rep_mode = info->pscan_rep_mode;
data.pscan_period_mode = info->pscan_period_mode;
@@ -1223,8 +1260,9 @@ static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *
data.clock_offset = info->clock_offset;
data.rssi = 0x00;
data.ssp_mode = 0x00;
- info++;
hci_inquiry_cache_update(hdev, &data);
+ mgmt_device_found(hdev->id, &info->bdaddr, info->dev_class, 0,
+ NULL);
}
hci_dev_unlock(hdev);
@@ -1402,7 +1440,7 @@ static inline void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff
conn->state = BT_CLOSED;
- if (conn->type == ACL_LINK)
+ if (conn->type == ACL_LINK || conn->type == LE_LINK)
mgmt_disconnected(hdev->id, &conn->dst);
hci_proto_disconn_cfm(conn, ev->reason);
@@ -1428,7 +1466,6 @@ static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *s
conn->sec_level = conn->pending_sec_level;
} else {
mgmt_auth_failed(hdev->id, &conn->dst, ev->status);
- conn->sec_level = BT_SECURITY_LOW;
}
clear_bit(HCI_CONN_AUTH_PEND, &conn->pend);
@@ -1482,13 +1519,23 @@ static inline void hci_remote_name_evt(struct hci_dev *hdev, struct sk_buff *skb
hci_dev_lock(hdev);
+ if (ev->status == 0 && test_bit(HCI_MGMT, &hdev->flags))
+ mgmt_remote_name(hdev->id, &ev->bdaddr, ev->name);
+
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr);
- if (conn && hci_outgoing_auth_needed(hdev, conn)) {
+ if (!conn)
+ goto unlock;
+
+ if (!hci_outgoing_auth_needed(hdev, conn))
+ goto unlock;
+
+ if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->pend)) {
struct hci_cp_auth_requested cp;
cp.handle = __cpu_to_le16(conn->handle);
hci_send_cmd(hdev, HCI_OP_AUTH_REQUESTED, sizeof(cp), &cp);
}
+unlock:
hci_dev_unlock(hdev);
}
@@ -1751,6 +1798,10 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk
hci_cc_pin_code_neg_reply(hdev, skb);
break;
+ case HCI_OP_READ_LOCAL_OOB_DATA:
+ hci_cc_read_local_oob_data_reply(hdev, skb);
+ break;
+
case HCI_OP_LE_READ_BUFFER_SIZE:
hci_cc_le_read_buffer_size(hdev, skb);
break;
@@ -1984,9 +2035,16 @@ static inline void hci_pin_code_request_evt(struct hci_dev *hdev, struct sk_buff
if (!test_bit(HCI_PAIRABLE, &hdev->flags))
hci_send_cmd(hdev, HCI_OP_PIN_CODE_NEG_REPLY,
sizeof(ev->bdaddr), &ev->bdaddr);
+ else if (test_bit(HCI_MGMT, &hdev->flags)) {
+ u8 secure;
- if (test_bit(HCI_MGMT, &hdev->flags))
- mgmt_pin_code_request(hdev->id, &ev->bdaddr);
+ if (conn->pending_sec_level == BT_SECURITY_HIGH)
+ secure = 1;
+ else
+ secure = 0;
+
+ mgmt_pin_code_request(hdev->id, &ev->bdaddr, secure);
+ }
hci_dev_unlock(hdev);
}
@@ -2015,17 +2073,30 @@ static inline void hci_link_key_request_evt(struct hci_dev *hdev, struct sk_buff
BT_DBG("%s found key type %u for %s", hdev->name, key->type,
batostr(&ev->bdaddr));
- if (!test_bit(HCI_DEBUG_KEYS, &hdev->flags) && key->type == 0x03) {
+ if (!test_bit(HCI_DEBUG_KEYS, &hdev->flags) &&
+ key->type == HCI_LK_DEBUG_COMBINATION) {
BT_DBG("%s ignoring debug key", hdev->name);
goto not_found;
}
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr);
+ if (conn) {
+ if (key->type == HCI_LK_UNAUTH_COMBINATION &&
+ conn->auth_type != 0xff &&
+ (conn->auth_type & 0x01)) {
+ BT_DBG("%s ignoring unauthenticated key", hdev->name);
+ goto not_found;
+ }
- if (key->type == 0x04 && conn && conn->auth_type != 0xff &&
- (conn->auth_type & 0x01)) {
- BT_DBG("%s ignoring unauthenticated key", hdev->name);
- goto not_found;
+ if (key->type == HCI_LK_COMBINATION && key->pin_len < 16 &&
+ conn->pending_sec_level == BT_SECURITY_HIGH) {
+ BT_DBG("%s ignoring key unauthenticated for high \
+ security", hdev->name);
+ goto not_found;
+ }
+
+ conn->key_type = key->type;
+ conn->pin_length = key->pin_len;
}
bacpy(&cp.bdaddr, &ev->bdaddr);
@@ -2057,11 +2128,15 @@ static inline void hci_link_key_notify_evt(struct hci_dev *hdev, struct sk_buff
hci_conn_hold(conn);
conn->disc_timeout = HCI_DISCONN_TIMEOUT;
pin_len = conn->pin_length;
+
+ if (ev->key_type != HCI_LK_CHANGED_COMBINATION)
+ conn->key_type = ev->key_type;
+
hci_conn_put(conn);
}
if (test_bit(HCI_LINK_KEYS, &hdev->flags))
- hci_add_link_key(hdev, 1, &ev->bdaddr, ev->link_key,
+ hci_add_link_key(hdev, conn, 1, &ev->bdaddr, ev->link_key,
ev->key_type, pin_len);
hci_dev_unlock(hdev);
@@ -2136,11 +2211,17 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct
hci_dev_lock(hdev);
+ if (!test_and_set_bit(HCI_INQUIRY, &hdev->flags)) {
+
+ if (test_bit(HCI_MGMT, &hdev->flags))
+ mgmt_discovering(hdev->id, 1);
+ }
+
if ((skb->len - 1) / num_rsp != sizeof(struct inquiry_info_with_rssi)) {
struct inquiry_info_with_rssi_and_pscan_mode *info;
info = (void *) (skb->data + 1);
- for (; num_rsp; num_rsp--) {
+ for (; num_rsp; num_rsp--, info++) {
bacpy(&data.bdaddr, &info->bdaddr);
data.pscan_rep_mode = info->pscan_rep_mode;
data.pscan_period_mode = info->pscan_period_mode;
@@ -2149,13 +2230,15 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct
data.clock_offset = info->clock_offset;
data.rssi = info->rssi;
data.ssp_mode = 0x00;
- info++;
hci_inquiry_cache_update(hdev, &data);
+ mgmt_device_found(hdev->id, &info->bdaddr,
+ info->dev_class, info->rssi,
+ NULL);
}
} else {
struct inquiry_info_with_rssi *info = (void *) (skb->data + 1);
- for (; num_rsp; num_rsp--) {
+ for (; num_rsp; num_rsp--, info++) {
bacpy(&data.bdaddr, &info->bdaddr);
data.pscan_rep_mode = info->pscan_rep_mode;
data.pscan_period_mode = info->pscan_period_mode;
@@ -2164,8 +2247,10 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct
data.clock_offset = info->clock_offset;
data.rssi = info->rssi;
data.ssp_mode = 0x00;
- info++;
hci_inquiry_cache_update(hdev, &data);
+ mgmt_device_found(hdev->id, &info->bdaddr,
+ info->dev_class, info->rssi,
+ NULL);
}
}
@@ -2294,9 +2379,15 @@ static inline void hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct
if (!num_rsp)
return;
+ if (!test_and_set_bit(HCI_INQUIRY, &hdev->flags)) {
+
+ if (test_bit(HCI_MGMT, &hdev->flags))
+ mgmt_discovering(hdev->id, 1);
+ }
+
hci_dev_lock(hdev);
- for (; num_rsp; num_rsp--) {
+ for (; num_rsp; num_rsp--, info++) {
bacpy(&data.bdaddr, &info->bdaddr);
data.pscan_rep_mode = info->pscan_rep_mode;
data.pscan_period_mode = info->pscan_period_mode;
@@ -2305,8 +2396,9 @@ static inline void hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct
data.clock_offset = info->clock_offset;
data.rssi = info->rssi;
data.ssp_mode = 0x01;
- info++;
hci_inquiry_cache_update(hdev, &data);
+ mgmt_device_found(hdev->id, &info->bdaddr, info->dev_class,
+ info->rssi, info->data);
}
hci_dev_unlock(hdev);
@@ -2326,7 +2418,7 @@ static inline u8 hci_get_auth_req(struct hci_conn *conn)
/* If remote requests no-bonding follow that lead */
if (conn->remote_auth == 0x00 || conn->remote_auth == 0x01)
- return 0x00;
+ return conn->remote_auth | (conn->auth_type & 0x01);
return conn->auth_type;
}
@@ -2355,8 +2447,14 @@ static inline void hci_io_capa_request_evt(struct hci_dev *hdev, struct sk_buff
bacpy(&cp.bdaddr, &ev->bdaddr);
cp.capability = conn->io_capability;
- cp.oob_data = 0;
- cp.authentication = hci_get_auth_req(conn);
+ conn->auth_type = hci_get_auth_req(conn);
+ cp.authentication = conn->auth_type;
+
+ if ((conn->out == 0x01 || conn->remote_oob == 0x01) &&
+ hci_find_remote_oob_data(hdev, &conn->dst))
+ cp.oob_data = 0x01;
+ else
+ cp.oob_data = 0x00;
hci_send_cmd(hdev, HCI_OP_IO_CAPABILITY_REPLY,
sizeof(cp), &cp);
@@ -2364,7 +2462,7 @@ static inline void hci_io_capa_request_evt(struct hci_dev *hdev, struct sk_buff
struct hci_cp_io_capability_neg_reply cp;
bacpy(&cp.bdaddr, &ev->bdaddr);
- cp.reason = 0x16; /* Pairing not allowed */
+ cp.reason = 0x18; /* Pairing not allowed */
hci_send_cmd(hdev, HCI_OP_IO_CAPABILITY_NEG_REPLY,
sizeof(cp), &cp);
@@ -2399,14 +2497,67 @@ static inline void hci_user_confirm_request_evt(struct hci_dev *hdev,
struct sk_buff *skb)
{
struct hci_ev_user_confirm_req *ev = (void *) skb->data;
+ int loc_mitm, rem_mitm, confirm_hint = 0;
+ struct hci_conn *conn;
BT_DBG("%s", hdev->name);
hci_dev_lock(hdev);
- if (test_bit(HCI_MGMT, &hdev->flags))
- mgmt_user_confirm_request(hdev->id, &ev->bdaddr, ev->passkey);
+ if (!test_bit(HCI_MGMT, &hdev->flags))
+ goto unlock;
+
+ conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr);
+ if (!conn)
+ goto unlock;
+
+ loc_mitm = (conn->auth_type & 0x01);
+ rem_mitm = (conn->remote_auth & 0x01);
+
+ /* If we require MITM but the remote device can't provide that
+ * (it has NoInputNoOutput) then reject the confirmation
+ * request. The only exception is when we're dedicated bonding
+ * initiators (connect_cfm_cb set) since then we always have the MITM
+ * bit set. */
+ if (!conn->connect_cfm_cb && loc_mitm && conn->remote_cap == 0x03) {
+ BT_DBG("Rejecting request: remote device can't provide MITM");
+ hci_send_cmd(hdev, HCI_OP_USER_CONFIRM_NEG_REPLY,
+ sizeof(ev->bdaddr), &ev->bdaddr);
+ goto unlock;
+ }
+
+ /* If no side requires MITM protection; auto-accept */
+ if ((!loc_mitm || conn->remote_cap == 0x03) &&
+ (!rem_mitm || conn->io_capability == 0x03)) {
+
+ /* If we're not the initiators request authorization to
+ * proceed from user space (mgmt_user_confirm with
+ * confirm_hint set to 1). */
+ if (!test_bit(HCI_CONN_AUTH_PEND, &conn->pend)) {
+ BT_DBG("Confirming auto-accept as acceptor");
+ confirm_hint = 1;
+ goto confirm;
+ }
+
+ BT_DBG("Auto-accept of user confirmation with %ums delay",
+ hdev->auto_accept_delay);
+
+ if (hdev->auto_accept_delay > 0) {
+ int delay = msecs_to_jiffies(hdev->auto_accept_delay);
+ mod_timer(&conn->auto_accept_timer, jiffies + delay);
+ goto unlock;
+ }
+
+ hci_send_cmd(hdev, HCI_OP_USER_CONFIRM_REPLY,
+ sizeof(ev->bdaddr), &ev->bdaddr);
+ goto unlock;
+ }
+
+confirm:
+ mgmt_user_confirm_request(hdev->id, &ev->bdaddr, ev->passkey,
+ confirm_hint);
+unlock:
hci_dev_unlock(hdev);
}
@@ -2453,6 +2604,41 @@ static inline void hci_remote_host_features_evt(struct hci_dev *hdev, struct sk_
hci_dev_unlock(hdev);
}
+static inline void hci_remote_oob_data_request_evt(struct hci_dev *hdev,
+ struct sk_buff *skb)
+{
+ struct hci_ev_remote_oob_data_request *ev = (void *) skb->data;
+ struct oob_data *data;
+
+ BT_DBG("%s", hdev->name);
+
+ hci_dev_lock(hdev);
+
+ if (!test_bit(HCI_MGMT, &hdev->flags))
+ goto unlock;
+
+ data = hci_find_remote_oob_data(hdev, &ev->bdaddr);
+ if (data) {
+ struct hci_cp_remote_oob_data_reply cp;
+
+ bacpy(&cp.bdaddr, &ev->bdaddr);
+ memcpy(cp.hash, data->hash, sizeof(cp.hash));
+ memcpy(cp.randomizer, data->randomizer, sizeof(cp.randomizer));
+
+ hci_send_cmd(hdev, HCI_OP_REMOTE_OOB_DATA_REPLY, sizeof(cp),
+ &cp);
+ } else {
+ struct hci_cp_remote_oob_data_neg_reply cp;
+
+ bacpy(&cp.bdaddr, &ev->bdaddr);
+ hci_send_cmd(hdev, HCI_OP_REMOTE_OOB_DATA_NEG_REPLY, sizeof(cp),
+ &cp);
+ }
+
+unlock:
+ hci_dev_unlock(hdev);
+}
+
static inline void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_ev_le_conn_complete *ev = (void *) skb->data;
@@ -2473,12 +2659,15 @@ static inline void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff
}
if (ev->status) {
+ mgmt_connect_failed(hdev->id, &ev->bdaddr, ev->status);
hci_proto_connect_cfm(conn, ev->status);
conn->state = BT_CLOSED;
hci_conn_del(conn);
goto unlock;
}
+ mgmt_connected(hdev->id, &ev->bdaddr);
+
conn->handle = __le16_to_cpu(ev->handle);
conn->state = BT_CONNECTED;
@@ -2655,6 +2844,10 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
hci_le_meta_evt(hdev, skb);
break;
+ case HCI_EV_REMOTE_OOB_DATA_REQUEST:
+ hci_remote_oob_data_request_evt(hdev, skb);
+ break;
+
default:
BT_DBG("%s event 0x%x", hdev->name, event);
break;
diff --git a/net/bluetooth/hci_sysfs.c b/net/bluetooth/hci_sysfs.c
index 3c838a65a75..a6c3aa8be1f 100644
--- a/net/bluetooth/hci_sysfs.c
+++ b/net/bluetooth/hci_sysfs.c
@@ -216,13 +216,13 @@ static ssize_t show_type(struct device *dev, struct device_attribute *attr, char
static ssize_t show_name(struct device *dev, struct device_attribute *attr, char *buf)
{
struct hci_dev *hdev = dev_get_drvdata(dev);
- char name[249];
+ char name[HCI_MAX_NAME_LENGTH + 1];
int i;
- for (i = 0; i < 248; i++)
+ for (i = 0; i < HCI_MAX_NAME_LENGTH; i++)
name[i] = hdev->dev_name[i];
- name[248] = '\0';
+ name[HCI_MAX_NAME_LENGTH] = '\0';
return sprintf(buf, "%s\n", name);
}
@@ -277,10 +277,12 @@ static ssize_t show_idle_timeout(struct device *dev, struct device_attribute *at
static ssize_t store_idle_timeout(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct hci_dev *hdev = dev_get_drvdata(dev);
- unsigned long val;
+ unsigned int val;
+ int rv;
- if (strict_strtoul(buf, 0, &val) < 0)
- return -EINVAL;
+ rv = kstrtouint(buf, 0, &val);
+ if (rv < 0)
+ return rv;
if (val != 0 && (val < 500 || val > 3600000))
return -EINVAL;
@@ -299,15 +301,14 @@ static ssize_t show_sniff_max_interval(struct device *dev, struct device_attribu
static ssize_t store_sniff_max_interval(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct hci_dev *hdev = dev_get_drvdata(dev);
- unsigned long val;
-
- if (strict_strtoul(buf, 0, &val) < 0)
- return -EINVAL;
+ u16 val;
+ int rv;
- if (val < 0x0002 || val > 0xFFFE || val % 2)
- return -EINVAL;
+ rv = kstrtou16(buf, 0, &val);
+ if (rv < 0)
+ return rv;
- if (val < hdev->sniff_min_interval)
+ if (val == 0 || val % 2 || val < hdev->sniff_min_interval)
return -EINVAL;
hdev->sniff_max_interval = val;
@@ -324,15 +325,14 @@ static ssize_t show_sniff_min_interval(struct device *dev, struct device_attribu
static ssize_t store_sniff_min_interval(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct hci_dev *hdev = dev_get_drvdata(dev);
- unsigned long val;
+ u16 val;
+ int rv;
- if (strict_strtoul(buf, 0, &val) < 0)
- return -EINVAL;
-
- if (val < 0x0002 || val > 0xFFFE || val % 2)
- return -EINVAL;
+ rv = kstrtou16(buf, 0, &val);
+ if (rv < 0)
+ return rv;
- if (val > hdev->sniff_max_interval)
+ if (val == 0 || val % 2 || val > hdev->sniff_max_interval)
return -EINVAL;
hdev->sniff_min_interval = val;
@@ -511,6 +511,35 @@ static const struct file_operations uuids_fops = {
.release = single_release,
};
+static int auto_accept_delay_set(void *data, u64 val)
+{
+ struct hci_dev *hdev = data;
+
+ hci_dev_lock_bh(hdev);
+
+ hdev->auto_accept_delay = val;
+
+ hci_dev_unlock_bh(hdev);
+
+ return 0;
+}
+
+static int auto_accept_delay_get(void *data, u64 *val)
+{
+ struct hci_dev *hdev = data;
+
+ hci_dev_lock_bh(hdev);
+
+ *val = hdev->auto_accept_delay;
+
+ hci_dev_unlock_bh(hdev);
+
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(auto_accept_delay_fops, auto_accept_delay_get,
+ auto_accept_delay_set, "%llu\n");
+
int hci_register_sysfs(struct hci_dev *hdev)
{
struct device *dev = &hdev->dev;
@@ -545,6 +574,8 @@ int hci_register_sysfs(struct hci_dev *hdev)
debugfs_create_file("uuids", 0444, hdev->debugfs, hdev, &uuids_fops);
+ debugfs_create_file("auto_accept_delay", 0444, hdev->debugfs, hdev,
+ &auto_accept_delay_fops);
return 0;
}
diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c
index 5ec12971af6..c405a954a60 100644
--- a/net/bluetooth/hidp/core.c
+++ b/net/bluetooth/hidp/core.c
@@ -37,6 +37,7 @@
#include <linux/init.h>
#include <linux/wait.h>
#include <linux/mutex.h>
+#include <linux/kthread.h>
#include <net/sock.h>
#include <linux/input.h>
@@ -55,22 +56,24 @@ static DECLARE_RWSEM(hidp_session_sem);
static LIST_HEAD(hidp_session_list);
static unsigned char hidp_keycode[256] = {
- 0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38,
- 50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44, 2, 3,
- 4, 5, 6, 7, 8, 9, 10, 11, 28, 1, 14, 15, 57, 12, 13, 26,
- 27, 43, 43, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64,
- 65, 66, 67, 68, 87, 88, 99, 70,119,110,102,104,111,107,109,106,
- 105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71,
- 72, 73, 82, 83, 86,127,116,117,183,184,185,186,187,188,189,190,
- 191,192,193,194,134,138,130,132,128,129,131,137,133,135,136,113,
- 115,114, 0, 0, 0,121, 0, 89, 93,124, 92, 94, 95, 0, 0, 0,
- 122,123, 90, 91, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113,
- 150,158,159,128,136,177,178,176,142,152,173,140
+ 0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36,
+ 37, 38, 50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45,
+ 21, 44, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 28, 1,
+ 14, 15, 57, 12, 13, 26, 27, 43, 43, 39, 40, 41, 51, 52,
+ 53, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 87, 88,
+ 99, 70, 119, 110, 102, 104, 111, 107, 109, 106, 105, 108, 103, 69,
+ 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71, 72, 73,
+ 82, 83, 86, 127, 116, 117, 183, 184, 185, 186, 187, 188, 189, 190,
+ 191, 192, 193, 194, 134, 138, 130, 132, 128, 129, 131, 137, 133, 135,
+ 136, 113, 115, 114, 0, 0, 0, 121, 0, 89, 93, 124, 92, 94,
+ 95, 0, 0, 0, 122, 123, 90, 91, 85, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 29, 42, 56, 125, 97, 54, 100, 126, 164, 166, 165, 163, 161, 115,
+ 114, 113, 150, 158, 159, 128, 136, 177, 178, 176, 142, 152, 173, 140
};
static unsigned char hidp_mkeyspat[] = { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 };
@@ -461,8 +464,7 @@ static void hidp_idle_timeout(unsigned long arg)
{
struct hidp_session *session = (struct hidp_session *) arg;
- atomic_inc(&session->terminate);
- hidp_schedule(session);
+ kthread_stop(session->task);
}
static void hidp_set_timer(struct hidp_session *session)
@@ -533,9 +535,7 @@ static void hidp_process_hid_control(struct hidp_session *session,
skb_queue_purge(&session->ctrl_transmit);
skb_queue_purge(&session->intr_transmit);
- /* Kill session thread */
- atomic_inc(&session->terminate);
- hidp_schedule(session);
+ kthread_stop(session->task);
}
}
@@ -694,22 +694,10 @@ static int hidp_session(void *arg)
struct sock *ctrl_sk = session->ctrl_sock->sk;
struct sock *intr_sk = session->intr_sock->sk;
struct sk_buff *skb;
- int vendor = 0x0000, product = 0x0000;
wait_queue_t ctrl_wait, intr_wait;
BT_DBG("session %p", session);
- if (session->input) {
- vendor = session->input->id.vendor;
- product = session->input->id.product;
- }
-
- if (session->hid) {
- vendor = session->hid->vendor;
- product = session->hid->product;
- }
-
- daemonize("khidpd_%04x%04x", vendor, product);
set_user_nice(current, -15);
init_waitqueue_entry(&ctrl_wait, current);
@@ -718,10 +706,11 @@ static int hidp_session(void *arg)
add_wait_queue(sk_sleep(intr_sk), &intr_wait);
session->waiting_for_startup = 0;
wake_up_interruptible(&session->startup_queue);
- while (!atomic_read(&session->terminate)) {
+ while (!kthread_should_stop()) {
set_current_state(TASK_INTERRUPTIBLE);
- if (ctrl_sk->sk_state != BT_CONNECTED || intr_sk->sk_state != BT_CONNECTED)
+ if (ctrl_sk->sk_state != BT_CONNECTED ||
+ intr_sk->sk_state != BT_CONNECTED)
break;
while ((skb = skb_dequeue(&ctrl_sk->sk_receive_queue))) {
@@ -965,6 +954,7 @@ fault:
int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, struct socket *intr_sock)
{
struct hidp_session *session, *s;
+ int vendor, product;
int err;
BT_DBG("");
@@ -989,8 +979,10 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock,
bacpy(&session->bdaddr, &bt_sk(ctrl_sock->sk)->dst);
- session->ctrl_mtu = min_t(uint, l2cap_pi(ctrl_sock->sk)->omtu, l2cap_pi(ctrl_sock->sk)->imtu);
- session->intr_mtu = min_t(uint, l2cap_pi(intr_sock->sk)->omtu, l2cap_pi(intr_sock->sk)->imtu);
+ session->ctrl_mtu = min_t(uint, l2cap_pi(ctrl_sock->sk)->chan->omtu,
+ l2cap_pi(ctrl_sock->sk)->chan->imtu);
+ session->intr_mtu = min_t(uint, l2cap_pi(intr_sock->sk)->chan->omtu,
+ l2cap_pi(intr_sock->sk)->chan->imtu);
BT_DBG("ctrl mtu %d intr mtu %d", session->ctrl_mtu, session->intr_mtu);
@@ -1026,9 +1018,24 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock,
hidp_set_timer(session);
- err = kernel_thread(hidp_session, session, CLONE_KERNEL);
- if (err < 0)
+ if (session->hid) {
+ vendor = session->hid->vendor;
+ product = session->hid->product;
+ } else if (session->input) {
+ vendor = session->input->id.vendor;
+ product = session->input->id.product;
+ } else {
+ vendor = 0x0000;
+ product = 0x0000;
+ }
+
+ session->task = kthread_run(hidp_session, session, "khidpd_%04x%04x",
+ vendor, product);
+ if (IS_ERR(session->task)) {
+ err = PTR_ERR(session->task);
goto unlink;
+ }
+
while (session->waiting_for_startup) {
wait_event_interruptible(session->startup_queue,
!session->waiting_for_startup);
@@ -1053,8 +1060,7 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock,
err_add_device:
hid_destroy_device(session->hid);
session->hid = NULL;
- atomic_inc(&session->terminate);
- hidp_schedule(session);
+ kthread_stop(session->task);
unlink:
hidp_del_timer(session);
@@ -1105,13 +1111,7 @@ int hidp_del_connection(struct hidp_conndel_req *req)
skb_queue_purge(&session->ctrl_transmit);
skb_queue_purge(&session->intr_transmit);
- /* Wakeup user-space polling for socket errors */
- session->intr_sock->sk->sk_err = EUNATCH;
- session->ctrl_sock->sk->sk_err = EUNATCH;
-
- /* Kill session thread */
- atomic_inc(&session->terminate);
- hidp_schedule(session);
+ kthread_stop(session->task);
}
} else
err = -ENOENT;
diff --git a/net/bluetooth/hidp/hidp.h b/net/bluetooth/hidp/hidp.h
index 13de5fa0348..12822cde4b4 100644
--- a/net/bluetooth/hidp/hidp.h
+++ b/net/bluetooth/hidp/hidp.h
@@ -84,8 +84,8 @@
#define HIDP_WAITING_FOR_SEND_ACK 11
struct hidp_connadd_req {
- int ctrl_sock; // Connected control socket
- int intr_sock; // Connteted interrupt socket
+ int ctrl_sock; /* Connected control socket */
+ int intr_sock; /* Connected interrupt socket */
__u16 parser;
__u16 rd_size;
__u8 __user *rd_data;
@@ -142,7 +142,7 @@ struct hidp_session {
uint ctrl_mtu;
uint intr_mtu;
- atomic_t terminate;
+ struct task_struct *task;
unsigned char keys[8];
unsigned char leds;
diff --git a/net/bluetooth/hidp/sock.c b/net/bluetooth/hidp/sock.c
index 250dfd46237..178ac7f127a 100644
--- a/net/bluetooth/hidp/sock.c
+++ b/net/bluetooth/hidp/sock.c
@@ -85,7 +85,8 @@ static int hidp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long
return err;
}
- if (csock->sk->sk_state != BT_CONNECTED || isock->sk->sk_state != BT_CONNECTED) {
+ if (csock->sk->sk_state != BT_CONNECTED ||
+ isock->sk->sk_state != BT_CONNECTED) {
sockfd_put(csock);
sockfd_put(isock);
return -EBADFD;
@@ -140,8 +141,8 @@ static int hidp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long
#ifdef CONFIG_COMPAT
struct compat_hidp_connadd_req {
- int ctrl_sock; // Connected control socket
- int intr_sock; // Connteted interrupt socket
+ int ctrl_sock; /* Connected control socket */
+ int intr_sock; /* Connected interrupt socket */
__u16 parser;
__u16 rd_size;
compat_uptr_t rd_data;
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 2c8dd4494c6..a86f9ba4f05 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -62,168 +62,233 @@ static u8 l2cap_fixed_chan[8] = { 0x02, };
static struct workqueue_struct *_busy_wq;
-struct bt_sock_list l2cap_sk_list = {
- .lock = __RW_LOCK_UNLOCKED(l2cap_sk_list.lock)
-};
+LIST_HEAD(chan_list);
+DEFINE_RWLOCK(chan_list_lock);
static void l2cap_busy_work(struct work_struct *work);
static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn,
u8 code, u8 ident, u16 dlen, void *data);
+static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data);
static int l2cap_ertm_data_rcv(struct sock *sk, struct sk_buff *skb);
/* ---- L2CAP channels ---- */
-static struct sock *__l2cap_get_chan_by_dcid(struct l2cap_chan_list *l, u16 cid)
+static struct l2cap_chan *__l2cap_get_chan_by_dcid(struct l2cap_conn *conn, u16 cid)
{
- struct sock *s;
- for (s = l->head; s; s = l2cap_pi(s)->next_c) {
- if (l2cap_pi(s)->dcid == cid)
- break;
+ struct l2cap_chan *c;
+
+ list_for_each_entry(c, &conn->chan_l, list) {
+ if (c->dcid == cid)
+ return c;
}
- return s;
+ return NULL;
+
}
-static struct sock *__l2cap_get_chan_by_scid(struct l2cap_chan_list *l, u16 cid)
+static struct l2cap_chan *__l2cap_get_chan_by_scid(struct l2cap_conn *conn, u16 cid)
{
- struct sock *s;
- for (s = l->head; s; s = l2cap_pi(s)->next_c) {
- if (l2cap_pi(s)->scid == cid)
- break;
+ struct l2cap_chan *c;
+
+ list_for_each_entry(c, &conn->chan_l, list) {
+ if (c->scid == cid)
+ return c;
}
- return s;
+ return NULL;
}
/* Find channel with given SCID.
* Returns locked socket */
-static inline struct sock *l2cap_get_chan_by_scid(struct l2cap_chan_list *l, u16 cid)
+static struct l2cap_chan *l2cap_get_chan_by_scid(struct l2cap_conn *conn, u16 cid)
{
- struct sock *s;
- read_lock(&l->lock);
- s = __l2cap_get_chan_by_scid(l, cid);
- if (s)
- bh_lock_sock(s);
- read_unlock(&l->lock);
- return s;
+ struct l2cap_chan *c;
+
+ read_lock(&conn->chan_lock);
+ c = __l2cap_get_chan_by_scid(conn, cid);
+ if (c)
+ bh_lock_sock(c->sk);
+ read_unlock(&conn->chan_lock);
+ return c;
}
-static struct sock *__l2cap_get_chan_by_ident(struct l2cap_chan_list *l, u8 ident)
+static struct l2cap_chan *__l2cap_get_chan_by_ident(struct l2cap_conn *conn, u8 ident)
{
- struct sock *s;
- for (s = l->head; s; s = l2cap_pi(s)->next_c) {
- if (l2cap_pi(s)->ident == ident)
- break;
+ struct l2cap_chan *c;
+
+ list_for_each_entry(c, &conn->chan_l, list) {
+ if (c->ident == ident)
+ return c;
+ }
+ return NULL;
+}
+
+static inline struct l2cap_chan *l2cap_get_chan_by_ident(struct l2cap_conn *conn, u8 ident)
+{
+ struct l2cap_chan *c;
+
+ read_lock(&conn->chan_lock);
+ c = __l2cap_get_chan_by_ident(conn, ident);
+ if (c)
+ bh_lock_sock(c->sk);
+ read_unlock(&conn->chan_lock);
+ return c;
+}
+
+static struct l2cap_chan *__l2cap_global_chan_by_addr(__le16 psm, bdaddr_t *src)
+{
+ struct l2cap_chan *c;
+
+ list_for_each_entry(c, &chan_list, global_l) {
+ if (c->sport == psm && !bacmp(&bt_sk(c->sk)->src, src))
+ goto found;
+ }
+
+ c = NULL;
+found:
+ return c;
+}
+
+int l2cap_add_psm(struct l2cap_chan *chan, bdaddr_t *src, __le16 psm)
+{
+ int err;
+
+ write_lock_bh(&chan_list_lock);
+
+ if (psm && __l2cap_global_chan_by_addr(psm, src)) {
+ err = -EADDRINUSE;
+ goto done;
}
- return s;
+
+ if (psm) {
+ chan->psm = psm;
+ chan->sport = psm;
+ err = 0;
+ } else {
+ u16 p;
+
+ err = -EINVAL;
+ for (p = 0x1001; p < 0x1100; p += 2)
+ if (!__l2cap_global_chan_by_addr(cpu_to_le16(p), src)) {
+ chan->psm = cpu_to_le16(p);
+ chan->sport = cpu_to_le16(p);
+ err = 0;
+ break;
+ }
+ }
+
+done:
+ write_unlock_bh(&chan_list_lock);
+ return err;
}
-static inline struct sock *l2cap_get_chan_by_ident(struct l2cap_chan_list *l, u8 ident)
+int l2cap_add_scid(struct l2cap_chan *chan, __u16 scid)
{
- struct sock *s;
- read_lock(&l->lock);
- s = __l2cap_get_chan_by_ident(l, ident);
- if (s)
- bh_lock_sock(s);
- read_unlock(&l->lock);
- return s;
+ write_lock_bh(&chan_list_lock);
+
+ chan->scid = scid;
+
+ write_unlock_bh(&chan_list_lock);
+
+ return 0;
}
-static u16 l2cap_alloc_cid(struct l2cap_chan_list *l)
+static u16 l2cap_alloc_cid(struct l2cap_conn *conn)
{
u16 cid = L2CAP_CID_DYN_START;
for (; cid < L2CAP_CID_DYN_END; cid++) {
- if (!__l2cap_get_chan_by_scid(l, cid))
+ if (!__l2cap_get_chan_by_scid(conn, cid))
return cid;
}
return 0;
}
-static inline void __l2cap_chan_link(struct l2cap_chan_list *l, struct sock *sk)
+struct l2cap_chan *l2cap_chan_create(struct sock *sk)
{
- sock_hold(sk);
+ struct l2cap_chan *chan;
- if (l->head)
- l2cap_pi(l->head)->prev_c = sk;
+ chan = kzalloc(sizeof(*chan), GFP_ATOMIC);
+ if (!chan)
+ return NULL;
- l2cap_pi(sk)->next_c = l->head;
- l2cap_pi(sk)->prev_c = NULL;
- l->head = sk;
-}
+ chan->sk = sk;
-static inline void l2cap_chan_unlink(struct l2cap_chan_list *l, struct sock *sk)
-{
- struct sock *next = l2cap_pi(sk)->next_c, *prev = l2cap_pi(sk)->prev_c;
+ write_lock_bh(&chan_list_lock);
+ list_add(&chan->global_l, &chan_list);
+ write_unlock_bh(&chan_list_lock);
- write_lock_bh(&l->lock);
- if (sk == l->head)
- l->head = next;
+ return chan;
+}
- if (next)
- l2cap_pi(next)->prev_c = prev;
- if (prev)
- l2cap_pi(prev)->next_c = next;
- write_unlock_bh(&l->lock);
+void l2cap_chan_destroy(struct l2cap_chan *chan)
+{
+ write_lock_bh(&chan_list_lock);
+ list_del(&chan->global_l);
+ write_unlock_bh(&chan_list_lock);
- __sock_put(sk);
+ kfree(chan);
}
-static void __l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent)
+static void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
{
- struct l2cap_chan_list *l = &conn->chan_list;
+ struct sock *sk = chan->sk;
BT_DBG("conn %p, psm 0x%2.2x, dcid 0x%4.4x", conn,
- l2cap_pi(sk)->psm, l2cap_pi(sk)->dcid);
+ chan->psm, chan->dcid);
conn->disc_reason = 0x13;
- l2cap_pi(sk)->conn = conn;
+ chan->conn = conn;
if (sk->sk_type == SOCK_SEQPACKET || sk->sk_type == SOCK_STREAM) {
if (conn->hcon->type == LE_LINK) {
/* LE connection */
- l2cap_pi(sk)->omtu = L2CAP_LE_DEFAULT_MTU;
- l2cap_pi(sk)->scid = L2CAP_CID_LE_DATA;
- l2cap_pi(sk)->dcid = L2CAP_CID_LE_DATA;
+ chan->omtu = L2CAP_LE_DEFAULT_MTU;
+ chan->scid = L2CAP_CID_LE_DATA;
+ chan->dcid = L2CAP_CID_LE_DATA;
} else {
/* Alloc CID for connection-oriented socket */
- l2cap_pi(sk)->scid = l2cap_alloc_cid(l);
- l2cap_pi(sk)->omtu = L2CAP_DEFAULT_MTU;
+ chan->scid = l2cap_alloc_cid(conn);
+ chan->omtu = L2CAP_DEFAULT_MTU;
}
} else if (sk->sk_type == SOCK_DGRAM) {
/* Connectionless socket */
- l2cap_pi(sk)->scid = L2CAP_CID_CONN_LESS;
- l2cap_pi(sk)->dcid = L2CAP_CID_CONN_LESS;
- l2cap_pi(sk)->omtu = L2CAP_DEFAULT_MTU;
+ chan->scid = L2CAP_CID_CONN_LESS;
+ chan->dcid = L2CAP_CID_CONN_LESS;
+ chan->omtu = L2CAP_DEFAULT_MTU;
} else {
/* Raw socket can send/recv signalling messages only */
- l2cap_pi(sk)->scid = L2CAP_CID_SIGNALING;
- l2cap_pi(sk)->dcid = L2CAP_CID_SIGNALING;
- l2cap_pi(sk)->omtu = L2CAP_DEFAULT_MTU;
+ chan->scid = L2CAP_CID_SIGNALING;
+ chan->dcid = L2CAP_CID_SIGNALING;
+ chan->omtu = L2CAP_DEFAULT_MTU;
}
- __l2cap_chan_link(l, sk);
+ sock_hold(sk);
- if (parent)
- bt_accept_enqueue(parent, sk);
+ list_add(&chan->list, &conn->chan_l);
}
/* Delete channel.
* Must be called on the locked socket. */
-void l2cap_chan_del(struct sock *sk, int err)
+void l2cap_chan_del(struct l2cap_chan *chan, int err)
{
- struct l2cap_conn *conn = l2cap_pi(sk)->conn;
+ struct sock *sk = chan->sk;
+ struct l2cap_conn *conn = chan->conn;
struct sock *parent = bt_sk(sk)->parent;
l2cap_sock_clear_timer(sk);
- BT_DBG("sk %p, conn %p, err %d", sk, conn, err);
+ BT_DBG("chan %p, conn %p, err %d", chan, conn, err);
if (conn) {
- /* Unlink from channel list */
- l2cap_chan_unlink(&conn->chan_list, sk);
- l2cap_pi(sk)->conn = NULL;
+ /* Delete from channel list */
+ write_lock_bh(&conn->chan_lock);
+ list_del(&chan->list);
+ write_unlock_bh(&conn->chan_lock);
+ __sock_put(sk);
+
+ chan->conn = NULL;
hci_conn_put(conn->hcon);
}
@@ -239,29 +304,35 @@ void l2cap_chan_del(struct sock *sk, int err)
} else
sk->sk_state_change(sk);
- skb_queue_purge(TX_QUEUE(sk));
+ if (!(chan->conf_state & L2CAP_CONF_OUTPUT_DONE &&
+ chan->conf_state & L2CAP_CONF_INPUT_DONE))
+ return;
+
+ skb_queue_purge(&chan->tx_q);
- if (l2cap_pi(sk)->mode == L2CAP_MODE_ERTM) {
+ if (chan->mode == L2CAP_MODE_ERTM) {
struct srej_list *l, *tmp;
- del_timer(&l2cap_pi(sk)->retrans_timer);
- del_timer(&l2cap_pi(sk)->monitor_timer);
- del_timer(&l2cap_pi(sk)->ack_timer);
+ del_timer(&chan->retrans_timer);
+ del_timer(&chan->monitor_timer);
+ del_timer(&chan->ack_timer);
- skb_queue_purge(SREJ_QUEUE(sk));
- skb_queue_purge(BUSY_QUEUE(sk));
+ skb_queue_purge(&chan->srej_q);
+ skb_queue_purge(&chan->busy_q);
- list_for_each_entry_safe(l, tmp, SREJ_LIST(sk), list) {
+ list_for_each_entry_safe(l, tmp, &chan->srej_l, list) {
list_del(&l->list);
kfree(l);
}
}
}
-static inline u8 l2cap_get_auth_type(struct sock *sk)
+static inline u8 l2cap_get_auth_type(struct l2cap_chan *chan)
{
+ struct sock *sk = chan->sk;
+
if (sk->sk_type == SOCK_RAW) {
- switch (l2cap_pi(sk)->sec_level) {
+ switch (chan->sec_level) {
case BT_SECURITY_HIGH:
return HCI_AT_DEDICATED_BONDING_MITM;
case BT_SECURITY_MEDIUM:
@@ -269,16 +340,16 @@ static inline u8 l2cap_get_auth_type(struct sock *sk)
default:
return HCI_AT_NO_BONDING;
}
- } else if (l2cap_pi(sk)->psm == cpu_to_le16(0x0001)) {
- if (l2cap_pi(sk)->sec_level == BT_SECURITY_LOW)
- l2cap_pi(sk)->sec_level = BT_SECURITY_SDP;
+ } else if (chan->psm == cpu_to_le16(0x0001)) {
+ if (chan->sec_level == BT_SECURITY_LOW)
+ chan->sec_level = BT_SECURITY_SDP;
- if (l2cap_pi(sk)->sec_level == BT_SECURITY_HIGH)
+ if (chan->sec_level == BT_SECURITY_HIGH)
return HCI_AT_NO_BONDING_MITM;
else
return HCI_AT_NO_BONDING;
} else {
- switch (l2cap_pi(sk)->sec_level) {
+ switch (chan->sec_level) {
case BT_SECURITY_HIGH:
return HCI_AT_GENERAL_BONDING_MITM;
case BT_SECURITY_MEDIUM:
@@ -290,15 +361,14 @@ static inline u8 l2cap_get_auth_type(struct sock *sk)
}
/* Service level security */
-static inline int l2cap_check_security(struct sock *sk)
+static inline int l2cap_check_security(struct l2cap_chan *chan)
{
- struct l2cap_conn *conn = l2cap_pi(sk)->conn;
+ struct l2cap_conn *conn = chan->conn;
__u8 auth_type;
- auth_type = l2cap_get_auth_type(sk);
+ auth_type = l2cap_get_auth_type(chan);
- return hci_conn_security(conn->hcon, l2cap_pi(sk)->sec_level,
- auth_type);
+ return hci_conn_security(conn->hcon, chan->sec_level, auth_type);
}
u8 l2cap_get_ident(struct l2cap_conn *conn)
@@ -341,11 +411,12 @@ void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, void *d
hci_send_acl(conn->hcon, skb, flags);
}
-static inline void l2cap_send_sframe(struct l2cap_pinfo *pi, u16 control)
+static inline void l2cap_send_sframe(struct l2cap_chan *chan, u16 control)
{
struct sk_buff *skb;
struct l2cap_hdr *lh;
- struct l2cap_conn *conn = pi->conn;
+ struct l2cap_pinfo *pi = l2cap_pi(chan->sk);
+ struct l2cap_conn *conn = chan->conn;
struct sock *sk = (struct sock *)pi;
int count, hlen = L2CAP_HDR_SIZE + 2;
u8 flags;
@@ -353,22 +424,22 @@ static inline void l2cap_send_sframe(struct l2cap_pinfo *pi, u16 control)
if (sk->sk_state != BT_CONNECTED)
return;
- if (pi->fcs == L2CAP_FCS_CRC16)
+ if (chan->fcs == L2CAP_FCS_CRC16)
hlen += 2;
- BT_DBG("pi %p, control 0x%2.2x", pi, control);
+ BT_DBG("chan %p, control 0x%2.2x", chan, control);
count = min_t(unsigned int, conn->mtu, hlen);
control |= L2CAP_CTRL_FRAME_TYPE;
- if (pi->conn_state & L2CAP_CONN_SEND_FBIT) {
+ if (chan->conn_state & L2CAP_CONN_SEND_FBIT) {
control |= L2CAP_CTRL_FINAL;
- pi->conn_state &= ~L2CAP_CONN_SEND_FBIT;
+ chan->conn_state &= ~L2CAP_CONN_SEND_FBIT;
}
- if (pi->conn_state & L2CAP_CONN_SEND_PBIT) {
+ if (chan->conn_state & L2CAP_CONN_SEND_PBIT) {
control |= L2CAP_CTRL_POLL;
- pi->conn_state &= ~L2CAP_CONN_SEND_PBIT;
+ chan->conn_state &= ~L2CAP_CONN_SEND_PBIT;
}
skb = bt_skb_alloc(count, GFP_ATOMIC);
@@ -377,10 +448,10 @@ static inline void l2cap_send_sframe(struct l2cap_pinfo *pi, u16 control)
lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
lh->len = cpu_to_le16(hlen - L2CAP_HDR_SIZE);
- lh->cid = cpu_to_le16(pi->dcid);
+ lh->cid = cpu_to_le16(chan->dcid);
put_unaligned_le16(control, skb_put(skb, 2));
- if (pi->fcs == L2CAP_FCS_CRC16) {
+ if (chan->fcs == L2CAP_FCS_CRC16) {
u16 fcs = crc16(0, (u8 *)lh, count - 2);
put_unaligned_le16(fcs, skb_put(skb, 2));
}
@@ -390,45 +461,46 @@ static inline void l2cap_send_sframe(struct l2cap_pinfo *pi, u16 control)
else
flags = ACL_START;
- hci_send_acl(pi->conn->hcon, skb, flags);
+ hci_send_acl(chan->conn->hcon, skb, flags);
}
-static inline void l2cap_send_rr_or_rnr(struct l2cap_pinfo *pi, u16 control)
+static inline void l2cap_send_rr_or_rnr(struct l2cap_chan *chan, u16 control)
{
- if (pi->conn_state & L2CAP_CONN_LOCAL_BUSY) {
+ if (chan->conn_state & L2CAP_CONN_LOCAL_BUSY) {
control |= L2CAP_SUPER_RCV_NOT_READY;
- pi->conn_state |= L2CAP_CONN_RNR_SENT;
+ chan->conn_state |= L2CAP_CONN_RNR_SENT;
} else
control |= L2CAP_SUPER_RCV_READY;
- control |= pi->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT;
+ control |= chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT;
- l2cap_send_sframe(pi, control);
+ l2cap_send_sframe(chan, control);
}
-static inline int __l2cap_no_conn_pending(struct sock *sk)
+static inline int __l2cap_no_conn_pending(struct l2cap_chan *chan)
{
- return !(l2cap_pi(sk)->conf_state & L2CAP_CONF_CONNECT_PEND);
+ return !(chan->conf_state & L2CAP_CONF_CONNECT_PEND);
}
-static void l2cap_do_start(struct sock *sk)
+static void l2cap_do_start(struct l2cap_chan *chan)
{
- struct l2cap_conn *conn = l2cap_pi(sk)->conn;
+ struct l2cap_conn *conn = chan->conn;
if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) {
if (!(conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE))
return;
- if (l2cap_check_security(sk) && __l2cap_no_conn_pending(sk)) {
+ if (l2cap_check_security(chan) &&
+ __l2cap_no_conn_pending(chan)) {
struct l2cap_conn_req req;
- req.scid = cpu_to_le16(l2cap_pi(sk)->scid);
- req.psm = l2cap_pi(sk)->psm;
+ req.scid = cpu_to_le16(chan->scid);
+ req.psm = chan->psm;
- l2cap_pi(sk)->ident = l2cap_get_ident(conn);
- l2cap_pi(sk)->conf_state |= L2CAP_CONF_CONNECT_PEND;
+ chan->ident = l2cap_get_ident(conn);
+ chan->conf_state |= L2CAP_CONF_CONNECT_PEND;
- l2cap_send_cmd(conn, l2cap_pi(sk)->ident,
- L2CAP_CONN_REQ, sizeof(req), &req);
+ l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_REQ,
+ sizeof(req), &req);
}
} else {
struct l2cap_info_req req;
@@ -461,23 +533,24 @@ static inline int l2cap_mode_supported(__u8 mode, __u32 feat_mask)
}
}
-void l2cap_send_disconn_req(struct l2cap_conn *conn, struct sock *sk, int err)
+void l2cap_send_disconn_req(struct l2cap_conn *conn, struct l2cap_chan *chan, int err)
{
+ struct sock *sk;
struct l2cap_disconn_req req;
if (!conn)
return;
- skb_queue_purge(TX_QUEUE(sk));
+ sk = chan->sk;
- if (l2cap_pi(sk)->mode == L2CAP_MODE_ERTM) {
- del_timer(&l2cap_pi(sk)->retrans_timer);
- del_timer(&l2cap_pi(sk)->monitor_timer);
- del_timer(&l2cap_pi(sk)->ack_timer);
+ if (chan->mode == L2CAP_MODE_ERTM) {
+ del_timer(&chan->retrans_timer);
+ del_timer(&chan->monitor_timer);
+ del_timer(&chan->ack_timer);
}
- req.dcid = cpu_to_le16(l2cap_pi(sk)->dcid);
- req.scid = cpu_to_le16(l2cap_pi(sk)->scid);
+ req.dcid = cpu_to_le16(chan->dcid);
+ req.scid = cpu_to_le16(chan->scid);
l2cap_send_cmd(conn, l2cap_get_ident(conn),
L2CAP_DISCONN_REQ, sizeof(req), &req);
@@ -488,17 +561,15 @@ void l2cap_send_disconn_req(struct l2cap_conn *conn, struct sock *sk, int err)
/* ---- L2CAP connections ---- */
static void l2cap_conn_start(struct l2cap_conn *conn)
{
- struct l2cap_chan_list *l = &conn->chan_list;
- struct sock_del_list del, *tmp1, *tmp2;
- struct sock *sk;
+ struct l2cap_chan *chan, *tmp;
BT_DBG("conn %p", conn);
- INIT_LIST_HEAD(&del.list);
+ read_lock(&conn->chan_lock);
- read_lock(&l->lock);
+ list_for_each_entry_safe(chan, tmp, &conn->chan_l, list) {
+ struct sock *sk = chan->sk;
- for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
bh_lock_sock(sk);
if (sk->sk_type != SOCK_SEQPACKET &&
@@ -510,40 +581,41 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
if (sk->sk_state == BT_CONNECT) {
struct l2cap_conn_req req;
- if (!l2cap_check_security(sk) ||
- !__l2cap_no_conn_pending(sk)) {
+ if (!l2cap_check_security(chan) ||
+ !__l2cap_no_conn_pending(chan)) {
bh_unlock_sock(sk);
continue;
}
- if (!l2cap_mode_supported(l2cap_pi(sk)->mode,
+ if (!l2cap_mode_supported(chan->mode,
conn->feat_mask)
- && l2cap_pi(sk)->conf_state &
+ && chan->conf_state &
L2CAP_CONF_STATE2_DEVICE) {
- tmp1 = kzalloc(sizeof(struct sock_del_list),
- GFP_ATOMIC);
- tmp1->sk = sk;
- list_add_tail(&tmp1->list, &del.list);
+ /* __l2cap_sock_close() calls list_del(chan)
+ * so release the lock */
+ read_unlock_bh(&conn->chan_lock);
+ __l2cap_sock_close(sk, ECONNRESET);
+ read_lock_bh(&conn->chan_lock);
bh_unlock_sock(sk);
continue;
}
- req.scid = cpu_to_le16(l2cap_pi(sk)->scid);
- req.psm = l2cap_pi(sk)->psm;
+ req.scid = cpu_to_le16(chan->scid);
+ req.psm = chan->psm;
- l2cap_pi(sk)->ident = l2cap_get_ident(conn);
- l2cap_pi(sk)->conf_state |= L2CAP_CONF_CONNECT_PEND;
+ chan->ident = l2cap_get_ident(conn);
+ chan->conf_state |= L2CAP_CONF_CONNECT_PEND;
- l2cap_send_cmd(conn, l2cap_pi(sk)->ident,
- L2CAP_CONN_REQ, sizeof(req), &req);
+ l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_REQ,
+ sizeof(req), &req);
} else if (sk->sk_state == BT_CONNECT2) {
struct l2cap_conn_rsp rsp;
char buf[128];
- rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid);
- rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid);
+ rsp.scid = cpu_to_le16(chan->dcid);
+ rsp.dcid = cpu_to_le16(chan->scid);
- if (l2cap_check_security(sk)) {
+ if (l2cap_check_security(chan)) {
if (bt_sk(sk)->defer_setup) {
struct sock *parent = bt_sk(sk)->parent;
rsp.result = cpu_to_le16(L2CAP_CR_PEND);
@@ -560,80 +632,77 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
rsp.status = cpu_to_le16(L2CAP_CS_AUTHEN_PEND);
}
- l2cap_send_cmd(conn, l2cap_pi(sk)->ident,
- L2CAP_CONN_RSP, sizeof(rsp), &rsp);
+ l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP,
+ sizeof(rsp), &rsp);
- if (l2cap_pi(sk)->conf_state & L2CAP_CONF_REQ_SENT ||
+ if (chan->conf_state & L2CAP_CONF_REQ_SENT ||
rsp.result != L2CAP_CR_SUCCESS) {
bh_unlock_sock(sk);
continue;
}
- l2cap_pi(sk)->conf_state |= L2CAP_CONF_REQ_SENT;
+ chan->conf_state |= L2CAP_CONF_REQ_SENT;
l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
- l2cap_build_conf_req(sk, buf), buf);
- l2cap_pi(sk)->num_conf_req++;
+ l2cap_build_conf_req(chan, buf), buf);
+ chan->num_conf_req++;
}
bh_unlock_sock(sk);
}
- read_unlock(&l->lock);
-
- list_for_each_entry_safe(tmp1, tmp2, &del.list, list) {
- bh_lock_sock(tmp1->sk);
- __l2cap_sock_close(tmp1->sk, ECONNRESET);
- bh_unlock_sock(tmp1->sk);
- list_del(&tmp1->list);
- kfree(tmp1);
- }
+ read_unlock(&conn->chan_lock);
}
/* Find socket with cid and source bdaddr.
* Returns closest match, locked.
*/
-static struct sock *l2cap_get_sock_by_scid(int state, __le16 cid, bdaddr_t *src)
+static struct l2cap_chan *l2cap_global_chan_by_scid(int state, __le16 cid, bdaddr_t *src)
{
- struct sock *s, *sk = NULL, *sk1 = NULL;
- struct hlist_node *node;
+ struct l2cap_chan *c, *c1 = NULL;
+
+ read_lock(&chan_list_lock);
- read_lock(&l2cap_sk_list.lock);
+ list_for_each_entry(c, &chan_list, global_l) {
+ struct sock *sk = c->sk;
- sk_for_each(sk, node, &l2cap_sk_list.head) {
if (state && sk->sk_state != state)
continue;
- if (l2cap_pi(sk)->scid == cid) {
+ if (c->scid == cid) {
/* Exact match. */
- if (!bacmp(&bt_sk(sk)->src, src))
- break;
+ if (!bacmp(&bt_sk(sk)->src, src)) {
+ read_unlock(&chan_list_lock);
+ return c;
+ }
/* Closest match */
if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY))
- sk1 = sk;
+ c1 = c;
}
}
- s = node ? sk : sk1;
- if (s)
- bh_lock_sock(s);
- read_unlock(&l2cap_sk_list.lock);
- return s;
+ read_unlock(&chan_list_lock);
+
+ return c1;
}
static void l2cap_le_conn_ready(struct l2cap_conn *conn)
{
- struct l2cap_chan_list *list = &conn->chan_list;
- struct sock *parent, *uninitialized_var(sk);
+ struct sock *parent, *sk;
+ struct l2cap_chan *chan, *pchan;
BT_DBG("");
/* Check if we have socket listening on cid */
- parent = l2cap_get_sock_by_scid(BT_LISTEN, L2CAP_CID_LE_DATA,
+ pchan = l2cap_global_chan_by_scid(BT_LISTEN, L2CAP_CID_LE_DATA,
conn->src);
- if (!parent)
+ if (!pchan)
return;
+ parent = pchan->sk;
+
+ bh_lock_sock(parent);
+
/* Check for backlog size */
if (sk_acceptq_is_full(parent)) {
BT_DBG("backlog full %d", parent->sk_ack_backlog);
@@ -644,22 +713,33 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn)
if (!sk)
goto clean;
- write_lock_bh(&list->lock);
+ chan = l2cap_chan_create(sk);
+ if (!chan) {
+ l2cap_sock_kill(sk);
+ goto clean;
+ }
+
+ l2cap_pi(sk)->chan = chan;
+
+ write_lock_bh(&conn->chan_lock);
hci_conn_hold(conn->hcon);
l2cap_sock_init(sk, parent);
+
bacpy(&bt_sk(sk)->src, conn->src);
bacpy(&bt_sk(sk)->dst, conn->dst);
- __l2cap_chan_add(conn, sk, parent);
+ bt_accept_enqueue(parent, sk);
+
+ __l2cap_chan_add(conn, chan);
l2cap_sock_set_timer(sk, sk->sk_sndtimeo);
sk->sk_state = BT_CONNECTED;
parent->sk_data_ready(parent, 0);
- write_unlock_bh(&list->lock);
+ write_unlock_bh(&conn->chan_lock);
clean:
bh_unlock_sock(parent);
@@ -667,17 +747,18 @@ clean:
static void l2cap_conn_ready(struct l2cap_conn *conn)
{
- struct l2cap_chan_list *l = &conn->chan_list;
- struct sock *sk;
+ struct l2cap_chan *chan;
BT_DBG("conn %p", conn);
if (!conn->hcon->out && conn->hcon->type == LE_LINK)
l2cap_le_conn_ready(conn);
- read_lock(&l->lock);
+ read_lock(&conn->chan_lock);
+
+ list_for_each_entry(chan, &conn->chan_l, list) {
+ struct sock *sk = chan->sk;
- for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
bh_lock_sock(sk);
if (conn->hcon->type == LE_LINK) {
@@ -692,30 +773,31 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
sk->sk_state = BT_CONNECTED;
sk->sk_state_change(sk);
} else if (sk->sk_state == BT_CONNECT)
- l2cap_do_start(sk);
+ l2cap_do_start(chan);
bh_unlock_sock(sk);
}
- read_unlock(&l->lock);
+ read_unlock(&conn->chan_lock);
}
/* Notify sockets that we cannot guaranty reliability anymore */
static void l2cap_conn_unreliable(struct l2cap_conn *conn, int err)
{
- struct l2cap_chan_list *l = &conn->chan_list;
- struct sock *sk;
+ struct l2cap_chan *chan;
BT_DBG("conn %p", conn);
- read_lock(&l->lock);
+ read_lock(&conn->chan_lock);
- for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
- if (l2cap_pi(sk)->force_reliable)
+ list_for_each_entry(chan, &conn->chan_l, list) {
+ struct sock *sk = chan->sk;
+
+ if (chan->force_reliable)
sk->sk_err = err;
}
- read_unlock(&l->lock);
+ read_unlock(&conn->chan_lock);
}
static void l2cap_info_timeout(unsigned long arg)
@@ -755,7 +837,9 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status)
conn->feat_mask = 0;
spin_lock_init(&conn->lock);
- rwlock_init(&conn->chan_list.lock);
+ rwlock_init(&conn->chan_lock);
+
+ INIT_LIST_HEAD(&conn->chan_l);
if (hcon->type != LE_LINK)
setup_timer(&conn->info_timer, l2cap_info_timeout,
@@ -769,6 +853,7 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status)
static void l2cap_conn_del(struct hci_conn *hcon, int err)
{
struct l2cap_conn *conn = hcon->l2cap_data;
+ struct l2cap_chan *chan, *l;
struct sock *sk;
if (!conn)
@@ -779,9 +864,10 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err)
kfree_skb(conn->rx_skb);
/* Kill channels */
- while ((sk = conn->chan_list.head)) {
+ list_for_each_entry_safe(chan, l, &conn->chan_l, list) {
+ sk = chan->sk;
bh_lock_sock(sk);
- l2cap_chan_del(sk, err);
+ l2cap_chan_del(chan, err);
bh_unlock_sock(sk);
l2cap_sock_kill(sk);
}
@@ -793,12 +879,11 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err)
kfree(conn);
}
-static inline void l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent)
+static inline void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
{
- struct l2cap_chan_list *l = &conn->chan_list;
- write_lock_bh(&l->lock);
- __l2cap_chan_add(conn, sk, parent);
- write_unlock_bh(&l->lock);
+ write_lock_bh(&conn->chan_lock);
+ __l2cap_chan_add(conn, chan);
+ write_unlock_bh(&conn->chan_lock);
}
/* ---- Socket interface ---- */
@@ -806,35 +891,39 @@ static inline void l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, stru
/* Find socket with psm and source bdaddr.
* Returns closest match.
*/
-static struct sock *l2cap_get_sock_by_psm(int state, __le16 psm, bdaddr_t *src)
+static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm, bdaddr_t *src)
{
- struct sock *sk = NULL, *sk1 = NULL;
- struct hlist_node *node;
+ struct l2cap_chan *c, *c1 = NULL;
- read_lock(&l2cap_sk_list.lock);
+ read_lock(&chan_list_lock);
+
+ list_for_each_entry(c, &chan_list, global_l) {
+ struct sock *sk = c->sk;
- sk_for_each(sk, node, &l2cap_sk_list.head) {
if (state && sk->sk_state != state)
continue;
- if (l2cap_pi(sk)->psm == psm) {
+ if (c->psm == psm) {
/* Exact match. */
- if (!bacmp(&bt_sk(sk)->src, src))
- break;
+ if (!bacmp(&bt_sk(sk)->src, src)) {
+ read_unlock_bh(&chan_list_lock);
+ return c;
+ }
/* Closest match */
if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY))
- sk1 = sk;
+ c1 = c;
}
}
- read_unlock(&l2cap_sk_list.lock);
+ read_unlock(&chan_list_lock);
- return node ? sk : sk1;
+ return c1;
}
-int l2cap_do_connect(struct sock *sk)
+int l2cap_chan_connect(struct l2cap_chan *chan)
{
+ struct sock *sk = chan->sk;
bdaddr_t *src = &bt_sk(sk)->src;
bdaddr_t *dst = &bt_sk(sk)->dst;
struct l2cap_conn *conn;
@@ -844,7 +933,7 @@ int l2cap_do_connect(struct sock *sk)
int err;
BT_DBG("%s -> %s psm 0x%2.2x", batostr(src), batostr(dst),
- l2cap_pi(sk)->psm);
+ chan->psm);
hdev = hci_get_route(dst, src);
if (!hdev)
@@ -852,14 +941,14 @@ int l2cap_do_connect(struct sock *sk)
hci_dev_lock_bh(hdev);
- auth_type = l2cap_get_auth_type(sk);
+ auth_type = l2cap_get_auth_type(chan);
- if (l2cap_pi(sk)->dcid == L2CAP_CID_LE_DATA)
+ if (chan->dcid == L2CAP_CID_LE_DATA)
hcon = hci_connect(hdev, LE_LINK, dst,
- l2cap_pi(sk)->sec_level, auth_type);
+ chan->sec_level, auth_type);
else
hcon = hci_connect(hdev, ACL_LINK, dst,
- l2cap_pi(sk)->sec_level, auth_type);
+ chan->sec_level, auth_type);
if (IS_ERR(hcon)) {
err = PTR_ERR(hcon);
@@ -876,7 +965,7 @@ int l2cap_do_connect(struct sock *sk)
/* Update source addr of the socket */
bacpy(src, conn->src);
- l2cap_chan_add(conn, sk, NULL);
+ l2cap_chan_add(conn, chan);
sk->sk_state = BT_CONNECT;
l2cap_sock_set_timer(sk, sk->sk_sndtimeo);
@@ -885,10 +974,10 @@ int l2cap_do_connect(struct sock *sk)
if (sk->sk_type != SOCK_SEQPACKET &&
sk->sk_type != SOCK_STREAM) {
l2cap_sock_clear_timer(sk);
- if (l2cap_check_security(sk))
+ if (l2cap_check_security(chan))
sk->sk_state = BT_CONNECTED;
} else
- l2cap_do_start(sk);
+ l2cap_do_start(chan);
}
err = 0;
@@ -901,12 +990,13 @@ done:
int __l2cap_wait_ack(struct sock *sk)
{
+ struct l2cap_chan *chan = l2cap_pi(sk)->chan;
DECLARE_WAITQUEUE(wait, current);
int err = 0;
int timeo = HZ/5;
add_wait_queue(sk_sleep(sk), &wait);
- while ((l2cap_pi(sk)->unacked_frames > 0 && l2cap_pi(sk)->conn)) {
+ while ((chan->unacked_frames > 0 && chan->conn)) {
set_current_state(TASK_INTERRUPTIBLE);
if (!timeo)
@@ -932,68 +1022,69 @@ int __l2cap_wait_ack(struct sock *sk)
static void l2cap_monitor_timeout(unsigned long arg)
{
- struct sock *sk = (void *) arg;
+ struct l2cap_chan *chan = (void *) arg;
+ struct sock *sk = chan->sk;
- BT_DBG("sk %p", sk);
+ BT_DBG("chan %p", chan);
bh_lock_sock(sk);
- if (l2cap_pi(sk)->retry_count >= l2cap_pi(sk)->remote_max_tx) {
- l2cap_send_disconn_req(l2cap_pi(sk)->conn, sk, ECONNABORTED);
+ if (chan->retry_count >= chan->remote_max_tx) {
+ l2cap_send_disconn_req(chan->conn, chan, ECONNABORTED);
bh_unlock_sock(sk);
return;
}
- l2cap_pi(sk)->retry_count++;
+ chan->retry_count++;
__mod_monitor_timer();
- l2cap_send_rr_or_rnr(l2cap_pi(sk), L2CAP_CTRL_POLL);
+ l2cap_send_rr_or_rnr(chan, L2CAP_CTRL_POLL);
bh_unlock_sock(sk);
}
static void l2cap_retrans_timeout(unsigned long arg)
{
- struct sock *sk = (void *) arg;
+ struct l2cap_chan *chan = (void *) arg;
+ struct sock *sk = chan->sk;
- BT_DBG("sk %p", sk);
+ BT_DBG("chan %p", chan);
bh_lock_sock(sk);
- l2cap_pi(sk)->retry_count = 1;
+ chan->retry_count = 1;
__mod_monitor_timer();
- l2cap_pi(sk)->conn_state |= L2CAP_CONN_WAIT_F;
+ chan->conn_state |= L2CAP_CONN_WAIT_F;
- l2cap_send_rr_or_rnr(l2cap_pi(sk), L2CAP_CTRL_POLL);
+ l2cap_send_rr_or_rnr(chan, L2CAP_CTRL_POLL);
bh_unlock_sock(sk);
}
-static void l2cap_drop_acked_frames(struct sock *sk)
+static void l2cap_drop_acked_frames(struct l2cap_chan *chan)
{
struct sk_buff *skb;
- while ((skb = skb_peek(TX_QUEUE(sk))) &&
- l2cap_pi(sk)->unacked_frames) {
- if (bt_cb(skb)->tx_seq == l2cap_pi(sk)->expected_ack_seq)
+ while ((skb = skb_peek(&chan->tx_q)) &&
+ chan->unacked_frames) {
+ if (bt_cb(skb)->tx_seq == chan->expected_ack_seq)
break;
- skb = skb_dequeue(TX_QUEUE(sk));
+ skb = skb_dequeue(&chan->tx_q);
kfree_skb(skb);
- l2cap_pi(sk)->unacked_frames--;
+ chan->unacked_frames--;
}
- if (!l2cap_pi(sk)->unacked_frames)
- del_timer(&l2cap_pi(sk)->retrans_timer);
+ if (!chan->unacked_frames)
+ del_timer(&chan->retrans_timer);
}
-void l2cap_do_send(struct sock *sk, struct sk_buff *skb)
+void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb)
{
- struct l2cap_pinfo *pi = l2cap_pi(sk);
- struct hci_conn *hcon = pi->conn->hcon;
+ struct hci_conn *hcon = chan->conn->hcon;
u16 flags;
- BT_DBG("sk %p, skb %p len %d", sk, skb, skb->len);
+ BT_DBG("chan %p, skb %p len %d", chan, skb, skb->len);
- if (!pi->flushable && lmp_no_flush_capable(hcon->hdev))
+ if (!chan->flushable && lmp_no_flush_capable(hcon->hdev))
flags = ACL_START_NO_FLUSH;
else
flags = ACL_START;
@@ -1001,35 +1092,33 @@ void l2cap_do_send(struct sock *sk, struct sk_buff *skb)
hci_send_acl(hcon, skb, flags);
}
-void l2cap_streaming_send(struct sock *sk)
+void l2cap_streaming_send(struct l2cap_chan *chan)
{
struct sk_buff *skb;
- struct l2cap_pinfo *pi = l2cap_pi(sk);
u16 control, fcs;
- while ((skb = skb_dequeue(TX_QUEUE(sk)))) {
+ while ((skb = skb_dequeue(&chan->tx_q))) {
control = get_unaligned_le16(skb->data + L2CAP_HDR_SIZE);
- control |= pi->next_tx_seq << L2CAP_CTRL_TXSEQ_SHIFT;
+ control |= chan->next_tx_seq << L2CAP_CTRL_TXSEQ_SHIFT;
put_unaligned_le16(control, skb->data + L2CAP_HDR_SIZE);
- if (pi->fcs == L2CAP_FCS_CRC16) {
+ if (chan->fcs == L2CAP_FCS_CRC16) {
fcs = crc16(0, (u8 *)skb->data, skb->len - 2);
put_unaligned_le16(fcs, skb->data + skb->len - 2);
}
- l2cap_do_send(sk, skb);
+ l2cap_do_send(chan, skb);
- pi->next_tx_seq = (pi->next_tx_seq + 1) % 64;
+ chan->next_tx_seq = (chan->next_tx_seq + 1) % 64;
}
}
-static void l2cap_retransmit_one_frame(struct sock *sk, u8 tx_seq)
+static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u8 tx_seq)
{
- struct l2cap_pinfo *pi = l2cap_pi(sk);
struct sk_buff *skb, *tx_skb;
u16 control, fcs;
- skb = skb_peek(TX_QUEUE(sk));
+ skb = skb_peek(&chan->tx_q);
if (!skb)
return;
@@ -1037,14 +1126,14 @@ static void l2cap_retransmit_one_frame(struct sock *sk, u8 tx_seq)
if (bt_cb(skb)->tx_seq == tx_seq)
break;
- if (skb_queue_is_last(TX_QUEUE(sk), skb))
+ if (skb_queue_is_last(&chan->tx_q, skb))
return;
- } while ((skb = skb_queue_next(TX_QUEUE(sk), skb)));
+ } while ((skb = skb_queue_next(&chan->tx_q, skb)));
- if (pi->remote_max_tx &&
- bt_cb(skb)->retries == pi->remote_max_tx) {
- l2cap_send_disconn_req(pi->conn, sk, ECONNABORTED);
+ if (chan->remote_max_tx &&
+ bt_cb(skb)->retries == chan->remote_max_tx) {
+ l2cap_send_disconn_req(chan->conn, chan, ECONNABORTED);
return;
}
@@ -1053,39 +1142,39 @@ static void l2cap_retransmit_one_frame(struct sock *sk, u8 tx_seq)
control = get_unaligned_le16(tx_skb->data + L2CAP_HDR_SIZE);
control &= L2CAP_CTRL_SAR;
- if (pi->conn_state & L2CAP_CONN_SEND_FBIT) {
+ if (chan->conn_state & L2CAP_CONN_SEND_FBIT) {
control |= L2CAP_CTRL_FINAL;
- pi->conn_state &= ~L2CAP_CONN_SEND_FBIT;
+ chan->conn_state &= ~L2CAP_CONN_SEND_FBIT;
}
- control |= (pi->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT)
+ control |= (chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT)
| (tx_seq << L2CAP_CTRL_TXSEQ_SHIFT);
put_unaligned_le16(control, tx_skb->data + L2CAP_HDR_SIZE);
- if (pi->fcs == L2CAP_FCS_CRC16) {
+ if (chan->fcs == L2CAP_FCS_CRC16) {
fcs = crc16(0, (u8 *)tx_skb->data, tx_skb->len - 2);
put_unaligned_le16(fcs, tx_skb->data + tx_skb->len - 2);
}
- l2cap_do_send(sk, tx_skb);
+ l2cap_do_send(chan, tx_skb);
}
-int l2cap_ertm_send(struct sock *sk)
+int l2cap_ertm_send(struct l2cap_chan *chan)
{
struct sk_buff *skb, *tx_skb;
- struct l2cap_pinfo *pi = l2cap_pi(sk);
+ struct sock *sk = chan->sk;
u16 control, fcs;
int nsent = 0;
if (sk->sk_state != BT_CONNECTED)
return -ENOTCONN;
- while ((skb = sk->sk_send_head) && (!l2cap_tx_window_full(sk))) {
+ while ((skb = chan->tx_send_head) && (!l2cap_tx_window_full(chan))) {
- if (pi->remote_max_tx &&
- bt_cb(skb)->retries == pi->remote_max_tx) {
- l2cap_send_disconn_req(pi->conn, sk, ECONNABORTED);
+ if (chan->remote_max_tx &&
+ bt_cb(skb)->retries == chan->remote_max_tx) {
+ l2cap_send_disconn_req(chan->conn, chan, ECONNABORTED);
break;
}
@@ -1096,36 +1185,36 @@ int l2cap_ertm_send(struct sock *sk)
control = get_unaligned_le16(tx_skb->data + L2CAP_HDR_SIZE);
control &= L2CAP_CTRL_SAR;
- if (pi->conn_state & L2CAP_CONN_SEND_FBIT) {
+ if (chan->conn_state & L2CAP_CONN_SEND_FBIT) {
control |= L2CAP_CTRL_FINAL;
- pi->conn_state &= ~L2CAP_CONN_SEND_FBIT;
+ chan->conn_state &= ~L2CAP_CONN_SEND_FBIT;
}
- control |= (pi->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT)
- | (pi->next_tx_seq << L2CAP_CTRL_TXSEQ_SHIFT);
+ control |= (chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT)
+ | (chan->next_tx_seq << L2CAP_CTRL_TXSEQ_SHIFT);
put_unaligned_le16(control, tx_skb->data + L2CAP_HDR_SIZE);
- if (pi->fcs == L2CAP_FCS_CRC16) {
+ if (chan->fcs == L2CAP_FCS_CRC16) {
fcs = crc16(0, (u8 *)skb->data, tx_skb->len - 2);
put_unaligned_le16(fcs, skb->data + tx_skb->len - 2);
}
- l2cap_do_send(sk, tx_skb);
+ l2cap_do_send(chan, tx_skb);
__mod_retrans_timer();
- bt_cb(skb)->tx_seq = pi->next_tx_seq;
- pi->next_tx_seq = (pi->next_tx_seq + 1) % 64;
+ bt_cb(skb)->tx_seq = chan->next_tx_seq;
+ chan->next_tx_seq = (chan->next_tx_seq + 1) % 64;
if (bt_cb(skb)->retries == 1)
- pi->unacked_frames++;
+ chan->unacked_frames++;
- pi->frames_sent++;
+ chan->frames_sent++;
- if (skb_queue_is_last(TX_QUEUE(sk), skb))
- sk->sk_send_head = NULL;
+ if (skb_queue_is_last(&chan->tx_q, skb))
+ chan->tx_send_head = NULL;
else
- sk->sk_send_head = skb_queue_next(TX_QUEUE(sk), skb);
+ chan->tx_send_head = skb_queue_next(&chan->tx_q, skb);
nsent++;
}
@@ -1133,41 +1222,39 @@ int l2cap_ertm_send(struct sock *sk)
return nsent;
}
-static int l2cap_retransmit_frames(struct sock *sk)
+static int l2cap_retransmit_frames(struct l2cap_chan *chan)
{
- struct l2cap_pinfo *pi = l2cap_pi(sk);
int ret;
- if (!skb_queue_empty(TX_QUEUE(sk)))
- sk->sk_send_head = TX_QUEUE(sk)->next;
+ if (!skb_queue_empty(&chan->tx_q))
+ chan->tx_send_head = chan->tx_q.next;
- pi->next_tx_seq = pi->expected_ack_seq;
- ret = l2cap_ertm_send(sk);
+ chan->next_tx_seq = chan->expected_ack_seq;
+ ret = l2cap_ertm_send(chan);
return ret;
}
-static void l2cap_send_ack(struct l2cap_pinfo *pi)
+static void l2cap_send_ack(struct l2cap_chan *chan)
{
- struct sock *sk = (struct sock *)pi;
u16 control = 0;
- control |= pi->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT;
+ control |= chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT;
- if (pi->conn_state & L2CAP_CONN_LOCAL_BUSY) {
+ if (chan->conn_state & L2CAP_CONN_LOCAL_BUSY) {
control |= L2CAP_SUPER_RCV_NOT_READY;
- pi->conn_state |= L2CAP_CONN_RNR_SENT;
- l2cap_send_sframe(pi, control);
+ chan->conn_state |= L2CAP_CONN_RNR_SENT;
+ l2cap_send_sframe(chan, control);
return;
}
- if (l2cap_ertm_send(sk) > 0)
+ if (l2cap_ertm_send(chan) > 0)
return;
control |= L2CAP_SUPER_RCV_READY;
- l2cap_send_sframe(pi, control);
+ l2cap_send_sframe(chan, control);
}
-static void l2cap_send_srejtail(struct sock *sk)
+static void l2cap_send_srejtail(struct l2cap_chan *chan)
{
struct srej_list *tail;
u16 control;
@@ -1175,15 +1262,15 @@ static void l2cap_send_srejtail(struct sock *sk)
control = L2CAP_SUPER_SELECT_REJECT;
control |= L2CAP_CTRL_FINAL;
- tail = list_entry(SREJ_LIST(sk)->prev, struct srej_list, list);
+ tail = list_entry((&chan->srej_l)->prev, struct srej_list, list);
control |= tail->tx_seq << L2CAP_CTRL_REQSEQ_SHIFT;
- l2cap_send_sframe(l2cap_pi(sk), control);
+ l2cap_send_sframe(chan, control);
}
static inline int l2cap_skbuff_fromiovec(struct sock *sk, struct msghdr *msg, int len, int count, struct sk_buff *skb)
{
- struct l2cap_conn *conn = l2cap_pi(sk)->conn;
+ struct l2cap_conn *conn = l2cap_pi(sk)->chan->conn;
struct sk_buff **frag;
int err, sent = 0;
@@ -1213,9 +1300,10 @@ static inline int l2cap_skbuff_fromiovec(struct sock *sk, struct msghdr *msg, in
return sent;
}
-struct sk_buff *l2cap_create_connless_pdu(struct sock *sk, struct msghdr *msg, size_t len)
+struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan, struct msghdr *msg, size_t len)
{
- struct l2cap_conn *conn = l2cap_pi(sk)->conn;
+ struct sock *sk = chan->sk;
+ struct l2cap_conn *conn = chan->conn;
struct sk_buff *skb;
int err, count, hlen = L2CAP_HDR_SIZE + 2;
struct l2cap_hdr *lh;
@@ -1230,9 +1318,9 @@ struct sk_buff *l2cap_create_connless_pdu(struct sock *sk, struct msghdr *msg, s
/* Create L2CAP header */
lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
- lh->cid = cpu_to_le16(l2cap_pi(sk)->dcid);
+ lh->cid = cpu_to_le16(chan->dcid);
lh->len = cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE));
- put_unaligned_le16(l2cap_pi(sk)->psm, skb_put(skb, 2));
+ put_unaligned_le16(chan->psm, skb_put(skb, 2));
err = l2cap_skbuff_fromiovec(sk, msg, len, count, skb);
if (unlikely(err < 0)) {
@@ -1242,9 +1330,10 @@ struct sk_buff *l2cap_create_connless_pdu(struct sock *sk, struct msghdr *msg, s
return skb;
}
-struct sk_buff *l2cap_create_basic_pdu(struct sock *sk, struct msghdr *msg, size_t len)
+struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan, struct msghdr *msg, size_t len)
{
- struct l2cap_conn *conn = l2cap_pi(sk)->conn;
+ struct sock *sk = chan->sk;
+ struct l2cap_conn *conn = chan->conn;
struct sk_buff *skb;
int err, count, hlen = L2CAP_HDR_SIZE;
struct l2cap_hdr *lh;
@@ -1259,7 +1348,7 @@ struct sk_buff *l2cap_create_basic_pdu(struct sock *sk, struct msghdr *msg, size
/* Create L2CAP header */
lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
- lh->cid = cpu_to_le16(l2cap_pi(sk)->dcid);
+ lh->cid = cpu_to_le16(chan->dcid);
lh->len = cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE));
err = l2cap_skbuff_fromiovec(sk, msg, len, count, skb);
@@ -1270,9 +1359,10 @@ struct sk_buff *l2cap_create_basic_pdu(struct sock *sk, struct msghdr *msg, size
return skb;
}
-struct sk_buff *l2cap_create_iframe_pdu(struct sock *sk, struct msghdr *msg, size_t len, u16 control, u16 sdulen)
+struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan, struct msghdr *msg, size_t len, u16 control, u16 sdulen)
{
- struct l2cap_conn *conn = l2cap_pi(sk)->conn;
+ struct sock *sk = chan->sk;
+ struct l2cap_conn *conn = chan->conn;
struct sk_buff *skb;
int err, count, hlen = L2CAP_HDR_SIZE + 2;
struct l2cap_hdr *lh;
@@ -1285,7 +1375,7 @@ struct sk_buff *l2cap_create_iframe_pdu(struct sock *sk, struct msghdr *msg, siz
if (sdulen)
hlen += 2;
- if (l2cap_pi(sk)->fcs == L2CAP_FCS_CRC16)
+ if (chan->fcs == L2CAP_FCS_CRC16)
hlen += 2;
count = min_t(unsigned int, (conn->mtu - hlen), len);
@@ -1296,7 +1386,7 @@ struct sk_buff *l2cap_create_iframe_pdu(struct sock *sk, struct msghdr *msg, siz
/* Create L2CAP header */
lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
- lh->cid = cpu_to_le16(l2cap_pi(sk)->dcid);
+ lh->cid = cpu_to_le16(chan->dcid);
lh->len = cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE));
put_unaligned_le16(control, skb_put(skb, 2));
if (sdulen)
@@ -1308,16 +1398,15 @@ struct sk_buff *l2cap_create_iframe_pdu(struct sock *sk, struct msghdr *msg, siz
return ERR_PTR(err);
}
- if (l2cap_pi(sk)->fcs == L2CAP_FCS_CRC16)
+ if (chan->fcs == L2CAP_FCS_CRC16)
put_unaligned_le16(0, skb_put(skb, 2));
bt_cb(skb)->retries = 0;
return skb;
}
-int l2cap_sar_segment_sdu(struct sock *sk, struct msghdr *msg, size_t len)
+int l2cap_sar_segment_sdu(struct l2cap_chan *chan, struct msghdr *msg, size_t len)
{
- struct l2cap_pinfo *pi = l2cap_pi(sk);
struct sk_buff *skb;
struct sk_buff_head sar_queue;
u16 control;
@@ -1325,26 +1414,26 @@ int l2cap_sar_segment_sdu(struct sock *sk, struct msghdr *msg, size_t len)
skb_queue_head_init(&sar_queue);
control = L2CAP_SDU_START;
- skb = l2cap_create_iframe_pdu(sk, msg, pi->remote_mps, control, len);
+ skb = l2cap_create_iframe_pdu(chan, msg, chan->remote_mps, control, len);
if (IS_ERR(skb))
return PTR_ERR(skb);
__skb_queue_tail(&sar_queue, skb);
- len -= pi->remote_mps;
- size += pi->remote_mps;
+ len -= chan->remote_mps;
+ size += chan->remote_mps;
while (len > 0) {
size_t buflen;
- if (len > pi->remote_mps) {
+ if (len > chan->remote_mps) {
control = L2CAP_SDU_CONTINUE;
- buflen = pi->remote_mps;
+ buflen = chan->remote_mps;
} else {
control = L2CAP_SDU_END;
buflen = len;
}
- skb = l2cap_create_iframe_pdu(sk, msg, buflen, control, 0);
+ skb = l2cap_create_iframe_pdu(chan, msg, buflen, control, 0);
if (IS_ERR(skb)) {
skb_queue_purge(&sar_queue);
return PTR_ERR(skb);
@@ -1354,9 +1443,9 @@ int l2cap_sar_segment_sdu(struct sock *sk, struct msghdr *msg, size_t len)
len -= buflen;
size += buflen;
}
- skb_queue_splice_tail(&sar_queue, TX_QUEUE(sk));
- if (sk->sk_send_head == NULL)
- sk->sk_send_head = sar_queue.next;
+ skb_queue_splice_tail(&sar_queue, &chan->tx_q);
+ if (chan->tx_send_head == NULL)
+ chan->tx_send_head = sar_queue.next;
return size;
}
@@ -1364,10 +1453,11 @@ int l2cap_sar_segment_sdu(struct sock *sk, struct msghdr *msg, size_t len)
static void l2cap_chan_ready(struct sock *sk)
{
struct sock *parent = bt_sk(sk)->parent;
+ struct l2cap_chan *chan = l2cap_pi(sk)->chan;
BT_DBG("sk %p, parent %p", sk, parent);
- l2cap_pi(sk)->conf_state = 0;
+ chan->conf_state = 0;
l2cap_sock_clear_timer(sk);
if (!parent) {
@@ -1387,14 +1477,14 @@ static void l2cap_chan_ready(struct sock *sk)
/* Copy frame to all raw sockets on that connection */
static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb)
{
- struct l2cap_chan_list *l = &conn->chan_list;
struct sk_buff *nskb;
- struct sock *sk;
+ struct l2cap_chan *chan;
BT_DBG("conn %p", conn);
- read_lock(&l->lock);
- for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
+ read_lock(&conn->chan_lock);
+ list_for_each_entry(chan, &conn->chan_l, list) {
+ struct sock *sk = chan->sk;
if (sk->sk_type != SOCK_RAW)
continue;
@@ -1408,7 +1498,7 @@ static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb)
if (sock_queue_rcv_skb(sk, nskb))
kfree_skb(nskb);
}
- read_unlock(&l->lock);
+ read_unlock(&conn->chan_lock);
}
/* ---- L2CAP signalling commands ---- */
@@ -1540,32 +1630,35 @@ static void l2cap_add_conf_opt(void **ptr, u8 type, u8 len, unsigned long val)
static void l2cap_ack_timeout(unsigned long arg)
{
- struct sock *sk = (void *) arg;
+ struct l2cap_chan *chan = (void *) arg;
- bh_lock_sock(sk);
- l2cap_send_ack(l2cap_pi(sk));
- bh_unlock_sock(sk);
+ bh_lock_sock(chan->sk);
+ l2cap_send_ack(chan);
+ bh_unlock_sock(chan->sk);
}
-static inline void l2cap_ertm_init(struct sock *sk)
+static inline void l2cap_ertm_init(struct l2cap_chan *chan)
{
- l2cap_pi(sk)->expected_ack_seq = 0;
- l2cap_pi(sk)->unacked_frames = 0;
- l2cap_pi(sk)->buffer_seq = 0;
- l2cap_pi(sk)->num_acked = 0;
- l2cap_pi(sk)->frames_sent = 0;
+ struct sock *sk = chan->sk;
+
+ chan->expected_ack_seq = 0;
+ chan->unacked_frames = 0;
+ chan->buffer_seq = 0;
+ chan->num_acked = 0;
+ chan->frames_sent = 0;
- setup_timer(&l2cap_pi(sk)->retrans_timer,
- l2cap_retrans_timeout, (unsigned long) sk);
- setup_timer(&l2cap_pi(sk)->monitor_timer,
- l2cap_monitor_timeout, (unsigned long) sk);
- setup_timer(&l2cap_pi(sk)->ack_timer,
- l2cap_ack_timeout, (unsigned long) sk);
+ setup_timer(&chan->retrans_timer, l2cap_retrans_timeout,
+ (unsigned long) chan);
+ setup_timer(&chan->monitor_timer, l2cap_monitor_timeout,
+ (unsigned long) chan);
+ setup_timer(&chan->ack_timer, l2cap_ack_timeout, (unsigned long) chan);
- __skb_queue_head_init(SREJ_QUEUE(sk));
- __skb_queue_head_init(BUSY_QUEUE(sk));
+ skb_queue_head_init(&chan->srej_q);
+ skb_queue_head_init(&chan->busy_q);
- INIT_WORK(&l2cap_pi(sk)->busy_work, l2cap_busy_work);
+ INIT_LIST_HEAD(&chan->srej_l);
+
+ INIT_WORK(&chan->busy_work, l2cap_busy_work);
sk->sk_backlog_rcv = l2cap_ertm_data_rcv;
}
@@ -1583,38 +1676,37 @@ static inline __u8 l2cap_select_mode(__u8 mode, __u16 remote_feat_mask)
}
}
-int l2cap_build_conf_req(struct sock *sk, void *data)
+static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data)
{
- struct l2cap_pinfo *pi = l2cap_pi(sk);
struct l2cap_conf_req *req = data;
- struct l2cap_conf_rfc rfc = { .mode = pi->mode };
+ struct l2cap_conf_rfc rfc = { .mode = chan->mode };
void *ptr = req->data;
- BT_DBG("sk %p", sk);
+ BT_DBG("chan %p", chan);
- if (pi->num_conf_req || pi->num_conf_rsp)
+ if (chan->num_conf_req || chan->num_conf_rsp)
goto done;
- switch (pi->mode) {
+ switch (chan->mode) {
case L2CAP_MODE_STREAMING:
case L2CAP_MODE_ERTM:
- if (pi->conf_state & L2CAP_CONF_STATE2_DEVICE)
+ if (chan->conf_state & L2CAP_CONF_STATE2_DEVICE)
break;
/* fall through */
default:
- pi->mode = l2cap_select_mode(rfc.mode, pi->conn->feat_mask);
+ chan->mode = l2cap_select_mode(rfc.mode, chan->conn->feat_mask);
break;
}
done:
- if (pi->imtu != L2CAP_DEFAULT_MTU)
- l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->imtu);
+ if (chan->imtu != L2CAP_DEFAULT_MTU)
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, chan->imtu);
- switch (pi->mode) {
+ switch (chan->mode) {
case L2CAP_MODE_BASIC:
- if (!(pi->conn->feat_mask & L2CAP_FEAT_ERTM) &&
- !(pi->conn->feat_mask & L2CAP_FEAT_STREAMING))
+ if (!(chan->conn->feat_mask & L2CAP_FEAT_ERTM) &&
+ !(chan->conn->feat_mask & L2CAP_FEAT_STREAMING))
break;
rfc.mode = L2CAP_MODE_BASIC;
@@ -1630,24 +1722,24 @@ done:
case L2CAP_MODE_ERTM:
rfc.mode = L2CAP_MODE_ERTM;
- rfc.txwin_size = pi->tx_win;
- rfc.max_transmit = pi->max_tx;
+ rfc.txwin_size = chan->tx_win;
+ rfc.max_transmit = chan->max_tx;
rfc.retrans_timeout = 0;
rfc.monitor_timeout = 0;
rfc.max_pdu_size = cpu_to_le16(L2CAP_DEFAULT_MAX_PDU_SIZE);
- if (L2CAP_DEFAULT_MAX_PDU_SIZE > pi->conn->mtu - 10)
- rfc.max_pdu_size = cpu_to_le16(pi->conn->mtu - 10);
+ if (L2CAP_DEFAULT_MAX_PDU_SIZE > chan->conn->mtu - 10)
+ rfc.max_pdu_size = cpu_to_le16(chan->conn->mtu - 10);
l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
(unsigned long) &rfc);
- if (!(pi->conn->feat_mask & L2CAP_FEAT_FCS))
+ if (!(chan->conn->feat_mask & L2CAP_FEAT_FCS))
break;
- if (pi->fcs == L2CAP_FCS_NONE ||
- pi->conf_state & L2CAP_CONF_NO_FCS_RECV) {
- pi->fcs = L2CAP_FCS_NONE;
- l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, pi->fcs);
+ if (chan->fcs == L2CAP_FCS_NONE ||
+ chan->conf_state & L2CAP_CONF_NO_FCS_RECV) {
+ chan->fcs = L2CAP_FCS_NONE;
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, chan->fcs);
}
break;
@@ -1658,43 +1750,42 @@ done:
rfc.retrans_timeout = 0;
rfc.monitor_timeout = 0;
rfc.max_pdu_size = cpu_to_le16(L2CAP_DEFAULT_MAX_PDU_SIZE);
- if (L2CAP_DEFAULT_MAX_PDU_SIZE > pi->conn->mtu - 10)
- rfc.max_pdu_size = cpu_to_le16(pi->conn->mtu - 10);
+ if (L2CAP_DEFAULT_MAX_PDU_SIZE > chan->conn->mtu - 10)
+ rfc.max_pdu_size = cpu_to_le16(chan->conn->mtu - 10);
l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
(unsigned long) &rfc);
- if (!(pi->conn->feat_mask & L2CAP_FEAT_FCS))
+ if (!(chan->conn->feat_mask & L2CAP_FEAT_FCS))
break;
- if (pi->fcs == L2CAP_FCS_NONE ||
- pi->conf_state & L2CAP_CONF_NO_FCS_RECV) {
- pi->fcs = L2CAP_FCS_NONE;
- l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, pi->fcs);
+ if (chan->fcs == L2CAP_FCS_NONE ||
+ chan->conf_state & L2CAP_CONF_NO_FCS_RECV) {
+ chan->fcs = L2CAP_FCS_NONE;
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, chan->fcs);
}
break;
}
- req->dcid = cpu_to_le16(pi->dcid);
+ req->dcid = cpu_to_le16(chan->dcid);
req->flags = cpu_to_le16(0);
return ptr - data;
}
-static int l2cap_parse_conf_req(struct sock *sk, void *data)
+static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data)
{
- struct l2cap_pinfo *pi = l2cap_pi(sk);
struct l2cap_conf_rsp *rsp = data;
void *ptr = rsp->data;
- void *req = pi->conf_req;
- int len = pi->conf_len;
+ void *req = chan->conf_req;
+ int len = chan->conf_len;
int type, hint, olen;
unsigned long val;
struct l2cap_conf_rfc rfc = { .mode = L2CAP_MODE_BASIC };
u16 mtu = L2CAP_DEFAULT_MTU;
u16 result = L2CAP_CONF_SUCCESS;
- BT_DBG("sk %p", sk);
+ BT_DBG("chan %p", chan);
while (len >= L2CAP_CONF_OPT_SIZE) {
len -= l2cap_get_conf_opt(&req, &type, &olen, &val);
@@ -1708,7 +1799,7 @@ static int l2cap_parse_conf_req(struct sock *sk, void *data)
break;
case L2CAP_CONF_FLUSH_TO:
- pi->flush_to = val;
+ chan->flush_to = val;
break;
case L2CAP_CONF_QOS:
@@ -1721,7 +1812,7 @@ static int l2cap_parse_conf_req(struct sock *sk, void *data)
case L2CAP_CONF_FCS:
if (val == L2CAP_FCS_NONE)
- pi->conf_state |= L2CAP_CONF_NO_FCS_RECV;
+ chan->conf_state |= L2CAP_CONF_NO_FCS_RECV;
break;
@@ -1735,30 +1826,30 @@ static int l2cap_parse_conf_req(struct sock *sk, void *data)
}
}
- if (pi->num_conf_rsp || pi->num_conf_req > 1)
+ if (chan->num_conf_rsp || chan->num_conf_req > 1)
goto done;
- switch (pi->mode) {
+ switch (chan->mode) {
case L2CAP_MODE_STREAMING:
case L2CAP_MODE_ERTM:
- if (!(pi->conf_state & L2CAP_CONF_STATE2_DEVICE)) {
- pi->mode = l2cap_select_mode(rfc.mode,
- pi->conn->feat_mask);
+ if (!(chan->conf_state & L2CAP_CONF_STATE2_DEVICE)) {
+ chan->mode = l2cap_select_mode(rfc.mode,
+ chan->conn->feat_mask);
break;
}
- if (pi->mode != rfc.mode)
+ if (chan->mode != rfc.mode)
return -ECONNREFUSED;
break;
}
done:
- if (pi->mode != rfc.mode) {
+ if (chan->mode != rfc.mode) {
result = L2CAP_CONF_UNACCEPT;
- rfc.mode = pi->mode;
+ rfc.mode = chan->mode;
- if (pi->num_conf_rsp == 1)
+ if (chan->num_conf_rsp == 1)
return -ECONNREFUSED;
l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
@@ -1773,32 +1864,32 @@ done:
if (mtu < L2CAP_DEFAULT_MIN_MTU)
result = L2CAP_CONF_UNACCEPT;
else {
- pi->omtu = mtu;
- pi->conf_state |= L2CAP_CONF_MTU_DONE;
+ chan->omtu = mtu;
+ chan->conf_state |= L2CAP_CONF_MTU_DONE;
}
- l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->omtu);
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, chan->omtu);
switch (rfc.mode) {
case L2CAP_MODE_BASIC:
- pi->fcs = L2CAP_FCS_NONE;
- pi->conf_state |= L2CAP_CONF_MODE_DONE;
+ chan->fcs = L2CAP_FCS_NONE;
+ chan->conf_state |= L2CAP_CONF_MODE_DONE;
break;
case L2CAP_MODE_ERTM:
- pi->remote_tx_win = rfc.txwin_size;
- pi->remote_max_tx = rfc.max_transmit;
+ chan->remote_tx_win = rfc.txwin_size;
+ chan->remote_max_tx = rfc.max_transmit;
- if (le16_to_cpu(rfc.max_pdu_size) > pi->conn->mtu - 10)
- rfc.max_pdu_size = cpu_to_le16(pi->conn->mtu - 10);
+ if (le16_to_cpu(rfc.max_pdu_size) > chan->conn->mtu - 10)
+ rfc.max_pdu_size = cpu_to_le16(chan->conn->mtu - 10);
- pi->remote_mps = le16_to_cpu(rfc.max_pdu_size);
+ chan->remote_mps = le16_to_cpu(rfc.max_pdu_size);
rfc.retrans_timeout =
le16_to_cpu(L2CAP_DEFAULT_RETRANS_TO);
rfc.monitor_timeout =
le16_to_cpu(L2CAP_DEFAULT_MONITOR_TO);
- pi->conf_state |= L2CAP_CONF_MODE_DONE;
+ chan->conf_state |= L2CAP_CONF_MODE_DONE;
l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
sizeof(rfc), (unsigned long) &rfc);
@@ -1806,12 +1897,12 @@ done:
break;
case L2CAP_MODE_STREAMING:
- if (le16_to_cpu(rfc.max_pdu_size) > pi->conn->mtu - 10)
- rfc.max_pdu_size = cpu_to_le16(pi->conn->mtu - 10);
+ if (le16_to_cpu(rfc.max_pdu_size) > chan->conn->mtu - 10)
+ rfc.max_pdu_size = cpu_to_le16(chan->conn->mtu - 10);
- pi->remote_mps = le16_to_cpu(rfc.max_pdu_size);
+ chan->remote_mps = le16_to_cpu(rfc.max_pdu_size);
- pi->conf_state |= L2CAP_CONF_MODE_DONE;
+ chan->conf_state |= L2CAP_CONF_MODE_DONE;
l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
sizeof(rfc), (unsigned long) &rfc);
@@ -1822,29 +1913,28 @@ done:
result = L2CAP_CONF_UNACCEPT;
memset(&rfc, 0, sizeof(rfc));
- rfc.mode = pi->mode;
+ rfc.mode = chan->mode;
}
if (result == L2CAP_CONF_SUCCESS)
- pi->conf_state |= L2CAP_CONF_OUTPUT_DONE;
+ chan->conf_state |= L2CAP_CONF_OUTPUT_DONE;
}
- rsp->scid = cpu_to_le16(pi->dcid);
+ rsp->scid = cpu_to_le16(chan->dcid);
rsp->result = cpu_to_le16(result);
rsp->flags = cpu_to_le16(0x0000);
return ptr - data;
}
-static int l2cap_parse_conf_rsp(struct sock *sk, void *rsp, int len, void *data, u16 *result)
+static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, void *data, u16 *result)
{
- struct l2cap_pinfo *pi = l2cap_pi(sk);
struct l2cap_conf_req *req = data;
void *ptr = req->data;
int type, olen;
unsigned long val;
struct l2cap_conf_rfc rfc;
- BT_DBG("sk %p, rsp %p, len %d, req %p", sk, rsp, len, data);
+ BT_DBG("chan %p, rsp %p, len %d, req %p", chan, rsp, len, data);
while (len >= L2CAP_CONF_OPT_SIZE) {
len -= l2cap_get_conf_opt(&rsp, &type, &olen, &val);
@@ -1853,27 +1943,27 @@ static int l2cap_parse_conf_rsp(struct sock *sk, void *rsp, int len, void *data,
case L2CAP_CONF_MTU:
if (val < L2CAP_DEFAULT_MIN_MTU) {
*result = L2CAP_CONF_UNACCEPT;
- pi->imtu = L2CAP_DEFAULT_MIN_MTU;
+ chan->imtu = L2CAP_DEFAULT_MIN_MTU;
} else
- pi->imtu = val;
- l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->imtu);
+ chan->imtu = val;
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, chan->imtu);
break;
case L2CAP_CONF_FLUSH_TO:
- pi->flush_to = val;
+ chan->flush_to = val;
l2cap_add_conf_opt(&ptr, L2CAP_CONF_FLUSH_TO,
- 2, pi->flush_to);
+ 2, chan->flush_to);
break;
case L2CAP_CONF_RFC:
if (olen == sizeof(rfc))
memcpy(&rfc, (void *)val, olen);
- if ((pi->conf_state & L2CAP_CONF_STATE2_DEVICE) &&
- rfc.mode != pi->mode)
+ if ((chan->conf_state & L2CAP_CONF_STATE2_DEVICE) &&
+ rfc.mode != chan->mode)
return -ECONNREFUSED;
- pi->fcs = 0;
+ chan->fcs = 0;
l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
sizeof(rfc), (unsigned long) &rfc);
@@ -1881,53 +1971,74 @@ static int l2cap_parse_conf_rsp(struct sock *sk, void *rsp, int len, void *data,
}
}
- if (pi->mode == L2CAP_MODE_BASIC && pi->mode != rfc.mode)
+ if (chan->mode == L2CAP_MODE_BASIC && chan->mode != rfc.mode)
return -ECONNREFUSED;
- pi->mode = rfc.mode;
+ chan->mode = rfc.mode;
if (*result == L2CAP_CONF_SUCCESS) {
switch (rfc.mode) {
case L2CAP_MODE_ERTM:
- pi->retrans_timeout = le16_to_cpu(rfc.retrans_timeout);
- pi->monitor_timeout = le16_to_cpu(rfc.monitor_timeout);
- pi->mps = le16_to_cpu(rfc.max_pdu_size);
+ chan->retrans_timeout = le16_to_cpu(rfc.retrans_timeout);
+ chan->monitor_timeout = le16_to_cpu(rfc.monitor_timeout);
+ chan->mps = le16_to_cpu(rfc.max_pdu_size);
break;
case L2CAP_MODE_STREAMING:
- pi->mps = le16_to_cpu(rfc.max_pdu_size);
+ chan->mps = le16_to_cpu(rfc.max_pdu_size);
}
}
- req->dcid = cpu_to_le16(pi->dcid);
+ req->dcid = cpu_to_le16(chan->dcid);
req->flags = cpu_to_le16(0x0000);
return ptr - data;
}
-static int l2cap_build_conf_rsp(struct sock *sk, void *data, u16 result, u16 flags)
+static int l2cap_build_conf_rsp(struct l2cap_chan *chan, void *data, u16 result, u16 flags)
{
struct l2cap_conf_rsp *rsp = data;
void *ptr = rsp->data;
- BT_DBG("sk %p", sk);
+ BT_DBG("chan %p", chan);
- rsp->scid = cpu_to_le16(l2cap_pi(sk)->dcid);
+ rsp->scid = cpu_to_le16(chan->dcid);
rsp->result = cpu_to_le16(result);
rsp->flags = cpu_to_le16(flags);
return ptr - data;
}
-static void l2cap_conf_rfc_get(struct sock *sk, void *rsp, int len)
+void __l2cap_connect_rsp_defer(struct l2cap_chan *chan)
+{
+ struct l2cap_conn_rsp rsp;
+ struct l2cap_conn *conn = chan->conn;
+ u8 buf[128];
+
+ rsp.scid = cpu_to_le16(chan->dcid);
+ rsp.dcid = cpu_to_le16(chan->scid);
+ rsp.result = cpu_to_le16(L2CAP_CR_SUCCESS);
+ rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
+ l2cap_send_cmd(conn, chan->ident,
+ L2CAP_CONN_RSP, sizeof(rsp), &rsp);
+
+ if (chan->conf_state & L2CAP_CONF_REQ_SENT)
+ return;
+
+ chan->conf_state |= L2CAP_CONF_REQ_SENT;
+ l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
+ l2cap_build_conf_req(chan, buf), buf);
+ chan->num_conf_req++;
+}
+
+static void l2cap_conf_rfc_get(struct l2cap_chan *chan, void *rsp, int len)
{
- struct l2cap_pinfo *pi = l2cap_pi(sk);
int type, olen;
unsigned long val;
struct l2cap_conf_rfc rfc;
- BT_DBG("sk %p, rsp %p, len %d", sk, rsp, len);
+ BT_DBG("chan %p, rsp %p, len %d", chan, rsp, len);
- if ((pi->mode != L2CAP_MODE_ERTM) && (pi->mode != L2CAP_MODE_STREAMING))
+ if ((chan->mode != L2CAP_MODE_ERTM) && (chan->mode != L2CAP_MODE_STREAMING))
return;
while (len >= L2CAP_CONF_OPT_SIZE) {
@@ -1944,12 +2055,12 @@ static void l2cap_conf_rfc_get(struct sock *sk, void *rsp, int len)
done:
switch (rfc.mode) {
case L2CAP_MODE_ERTM:
- pi->retrans_timeout = le16_to_cpu(rfc.retrans_timeout);
- pi->monitor_timeout = le16_to_cpu(rfc.monitor_timeout);
- pi->mps = le16_to_cpu(rfc.max_pdu_size);
+ chan->retrans_timeout = le16_to_cpu(rfc.retrans_timeout);
+ chan->monitor_timeout = le16_to_cpu(rfc.monitor_timeout);
+ chan->mps = le16_to_cpu(rfc.max_pdu_size);
break;
case L2CAP_MODE_STREAMING:
- pi->mps = le16_to_cpu(rfc.max_pdu_size);
+ chan->mps = le16_to_cpu(rfc.max_pdu_size);
}
}
@@ -1975,9 +2086,9 @@ static inline int l2cap_command_rej(struct l2cap_conn *conn, struct l2cap_cmd_hd
static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data)
{
- struct l2cap_chan_list *list = &conn->chan_list;
struct l2cap_conn_req *req = (struct l2cap_conn_req *) data;
struct l2cap_conn_rsp rsp;
+ struct l2cap_chan *chan = NULL, *pchan;
struct sock *parent, *sk = NULL;
int result, status = L2CAP_CS_NO_INFO;
@@ -1987,12 +2098,14 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd
BT_DBG("psm 0x%2.2x scid 0x%4.4x", psm, scid);
/* Check if we have socket listening on psm */
- parent = l2cap_get_sock_by_psm(BT_LISTEN, psm, conn->src);
- if (!parent) {
+ pchan = l2cap_global_chan_by_psm(BT_LISTEN, psm, conn->src);
+ if (!pchan) {
result = L2CAP_CR_BAD_PSM;
goto sendresp;
}
+ parent = pchan->sk;
+
bh_lock_sock(parent);
/* Check if the ACL is secure enough (if not SDP) */
@@ -2015,11 +2128,19 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd
if (!sk)
goto response;
- write_lock_bh(&list->lock);
+ chan = l2cap_chan_create(sk);
+ if (!chan) {
+ l2cap_sock_kill(sk);
+ goto response;
+ }
+
+ l2cap_pi(sk)->chan = chan;
+
+ write_lock_bh(&conn->chan_lock);
/* Check if we already have channel with that dcid */
- if (__l2cap_get_chan_by_dcid(list, scid)) {
- write_unlock_bh(&list->lock);
+ if (__l2cap_get_chan_by_dcid(conn, scid)) {
+ write_unlock_bh(&conn->chan_lock);
sock_set_flag(sk, SOCK_ZAPPED);
l2cap_sock_kill(sk);
goto response;
@@ -2030,18 +2151,21 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd
l2cap_sock_init(sk, parent);
bacpy(&bt_sk(sk)->src, conn->src);
bacpy(&bt_sk(sk)->dst, conn->dst);
- l2cap_pi(sk)->psm = psm;
- l2cap_pi(sk)->dcid = scid;
+ chan->psm = psm;
+ chan->dcid = scid;
+
+ bt_accept_enqueue(parent, sk);
+
+ __l2cap_chan_add(conn, chan);
- __l2cap_chan_add(conn, sk, parent);
- dcid = l2cap_pi(sk)->scid;
+ dcid = chan->scid;
l2cap_sock_set_timer(sk, sk->sk_sndtimeo);
- l2cap_pi(sk)->ident = cmd->ident;
+ chan->ident = cmd->ident;
if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE) {
- if (l2cap_check_security(sk)) {
+ if (l2cap_check_security(chan)) {
if (bt_sk(sk)->defer_setup) {
sk->sk_state = BT_CONNECT2;
result = L2CAP_CR_PEND;
@@ -2063,7 +2187,7 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd
status = L2CAP_CS_NO_INFO;
}
- write_unlock_bh(&list->lock);
+ write_unlock_bh(&conn->chan_lock);
response:
bh_unlock_sock(parent);
@@ -2089,13 +2213,13 @@ sendresp:
L2CAP_INFO_REQ, sizeof(info), &info);
}
- if (sk && !(l2cap_pi(sk)->conf_state & L2CAP_CONF_REQ_SENT) &&
+ if (chan && !(chan->conf_state & L2CAP_CONF_REQ_SENT) &&
result == L2CAP_CR_SUCCESS) {
u8 buf[128];
- l2cap_pi(sk)->conf_state |= L2CAP_CONF_REQ_SENT;
+ chan->conf_state |= L2CAP_CONF_REQ_SENT;
l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
- l2cap_build_conf_req(sk, buf), buf);
- l2cap_pi(sk)->num_conf_req++;
+ l2cap_build_conf_req(chan, buf), buf);
+ chan->num_conf_req++;
}
return 0;
@@ -2105,6 +2229,7 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd
{
struct l2cap_conn_rsp *rsp = (struct l2cap_conn_rsp *) data;
u16 scid, dcid, result, status;
+ struct l2cap_chan *chan;
struct sock *sk;
u8 req[128];
@@ -2116,34 +2241,36 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd
BT_DBG("dcid 0x%4.4x scid 0x%4.4x result 0x%2.2x status 0x%2.2x", dcid, scid, result, status);
if (scid) {
- sk = l2cap_get_chan_by_scid(&conn->chan_list, scid);
- if (!sk)
+ chan = l2cap_get_chan_by_scid(conn, scid);
+ if (!chan)
return -EFAULT;
} else {
- sk = l2cap_get_chan_by_ident(&conn->chan_list, cmd->ident);
- if (!sk)
+ chan = l2cap_get_chan_by_ident(conn, cmd->ident);
+ if (!chan)
return -EFAULT;
}
+ sk = chan->sk;
+
switch (result) {
case L2CAP_CR_SUCCESS:
sk->sk_state = BT_CONFIG;
- l2cap_pi(sk)->ident = 0;
- l2cap_pi(sk)->dcid = dcid;
- l2cap_pi(sk)->conf_state &= ~L2CAP_CONF_CONNECT_PEND;
+ chan->ident = 0;
+ chan->dcid = dcid;
+ chan->conf_state &= ~L2CAP_CONF_CONNECT_PEND;
- if (l2cap_pi(sk)->conf_state & L2CAP_CONF_REQ_SENT)
+ if (chan->conf_state & L2CAP_CONF_REQ_SENT)
break;
- l2cap_pi(sk)->conf_state |= L2CAP_CONF_REQ_SENT;
+ chan->conf_state |= L2CAP_CONF_REQ_SENT;
l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
- l2cap_build_conf_req(sk, req), req);
- l2cap_pi(sk)->num_conf_req++;
+ l2cap_build_conf_req(chan, req), req);
+ chan->num_conf_req++;
break;
case L2CAP_CR_PEND:
- l2cap_pi(sk)->conf_state |= L2CAP_CONF_CONNECT_PEND;
+ chan->conf_state |= L2CAP_CONF_CONNECT_PEND;
break;
default:
@@ -2155,7 +2282,7 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd
break;
}
- l2cap_chan_del(sk, ECONNREFUSED);
+ l2cap_chan_del(chan, ECONNREFUSED);
break;
}
@@ -2163,15 +2290,17 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd
return 0;
}
-static inline void set_default_fcs(struct l2cap_pinfo *pi)
+static inline void set_default_fcs(struct l2cap_chan *chan)
{
+ struct l2cap_pinfo *pi = l2cap_pi(chan->sk);
+
/* FCS is enabled only in ERTM or streaming mode, if one or both
* sides request it.
*/
- if (pi->mode != L2CAP_MODE_ERTM && pi->mode != L2CAP_MODE_STREAMING)
- pi->fcs = L2CAP_FCS_NONE;
- else if (!(pi->conf_state & L2CAP_CONF_NO_FCS_RECV))
- pi->fcs = L2CAP_FCS_CRC16;
+ if (chan->mode != L2CAP_MODE_ERTM && chan->mode != L2CAP_MODE_STREAMING)
+ chan->fcs = L2CAP_FCS_NONE;
+ else if (!(pi->chan->conf_state & L2CAP_CONF_NO_FCS_RECV))
+ chan->fcs = L2CAP_FCS_CRC16;
}
static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u16 cmd_len, u8 *data)
@@ -2179,6 +2308,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
struct l2cap_conf_req *req = (struct l2cap_conf_req *) data;
u16 dcid, flags;
u8 rsp[64];
+ struct l2cap_chan *chan;
struct sock *sk;
int len;
@@ -2187,10 +2317,12 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
BT_DBG("dcid 0x%4.4x flags 0x%2.2x", dcid, flags);
- sk = l2cap_get_chan_by_scid(&conn->chan_list, dcid);
- if (!sk)
+ chan = l2cap_get_chan_by_scid(conn, dcid);
+ if (!chan)
return -ENOENT;
+ sk = chan->sk;
+
if (sk->sk_state != BT_CONFIG) {
struct l2cap_cmd_rej rej;
@@ -2202,62 +2334,62 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
/* Reject if config buffer is too small. */
len = cmd_len - sizeof(*req);
- if (l2cap_pi(sk)->conf_len + len > sizeof(l2cap_pi(sk)->conf_req)) {
+ if (chan->conf_len + len > sizeof(chan->conf_req)) {
l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP,
- l2cap_build_conf_rsp(sk, rsp,
+ l2cap_build_conf_rsp(chan, rsp,
L2CAP_CONF_REJECT, flags), rsp);
goto unlock;
}
/* Store config. */
- memcpy(l2cap_pi(sk)->conf_req + l2cap_pi(sk)->conf_len, req->data, len);
- l2cap_pi(sk)->conf_len += len;
+ memcpy(chan->conf_req + chan->conf_len, req->data, len);
+ chan->conf_len += len;
if (flags & 0x0001) {
/* Incomplete config. Send empty response. */
l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP,
- l2cap_build_conf_rsp(sk, rsp,
+ l2cap_build_conf_rsp(chan, rsp,
L2CAP_CONF_SUCCESS, 0x0001), rsp);
goto unlock;
}
/* Complete config. */
- len = l2cap_parse_conf_req(sk, rsp);
+ len = l2cap_parse_conf_req(chan, rsp);
if (len < 0) {
- l2cap_send_disconn_req(conn, sk, ECONNRESET);
+ l2cap_send_disconn_req(conn, chan, ECONNRESET);
goto unlock;
}
l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, len, rsp);
- l2cap_pi(sk)->num_conf_rsp++;
+ chan->num_conf_rsp++;
/* Reset config buffer. */
- l2cap_pi(sk)->conf_len = 0;
+ chan->conf_len = 0;
- if (!(l2cap_pi(sk)->conf_state & L2CAP_CONF_OUTPUT_DONE))
+ if (!(chan->conf_state & L2CAP_CONF_OUTPUT_DONE))
goto unlock;
- if (l2cap_pi(sk)->conf_state & L2CAP_CONF_INPUT_DONE) {
- set_default_fcs(l2cap_pi(sk));
+ if (chan->conf_state & L2CAP_CONF_INPUT_DONE) {
+ set_default_fcs(chan);
sk->sk_state = BT_CONNECTED;
- l2cap_pi(sk)->next_tx_seq = 0;
- l2cap_pi(sk)->expected_tx_seq = 0;
- __skb_queue_head_init(TX_QUEUE(sk));
- if (l2cap_pi(sk)->mode == L2CAP_MODE_ERTM)
- l2cap_ertm_init(sk);
+ chan->next_tx_seq = 0;
+ chan->expected_tx_seq = 0;
+ skb_queue_head_init(&chan->tx_q);
+ if (chan->mode == L2CAP_MODE_ERTM)
+ l2cap_ertm_init(chan);
l2cap_chan_ready(sk);
goto unlock;
}
- if (!(l2cap_pi(sk)->conf_state & L2CAP_CONF_REQ_SENT)) {
+ if (!(chan->conf_state & L2CAP_CONF_REQ_SENT)) {
u8 buf[64];
- l2cap_pi(sk)->conf_state |= L2CAP_CONF_REQ_SENT;
+ chan->conf_state |= L2CAP_CONF_REQ_SENT;
l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
- l2cap_build_conf_req(sk, buf), buf);
- l2cap_pi(sk)->num_conf_req++;
+ l2cap_build_conf_req(chan, buf), buf);
+ chan->num_conf_req++;
}
unlock:
@@ -2269,6 +2401,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr
{
struct l2cap_conf_rsp *rsp = (struct l2cap_conf_rsp *)data;
u16 scid, flags, result;
+ struct l2cap_chan *chan;
struct sock *sk;
int len = cmd->len - sizeof(*rsp);
@@ -2279,36 +2412,38 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr
BT_DBG("scid 0x%4.4x flags 0x%2.2x result 0x%2.2x",
scid, flags, result);
- sk = l2cap_get_chan_by_scid(&conn->chan_list, scid);
- if (!sk)
+ chan = l2cap_get_chan_by_scid(conn, scid);
+ if (!chan)
return 0;
+ sk = chan->sk;
+
switch (result) {
case L2CAP_CONF_SUCCESS:
- l2cap_conf_rfc_get(sk, rsp->data, len);
+ l2cap_conf_rfc_get(chan, rsp->data, len);
break;
case L2CAP_CONF_UNACCEPT:
- if (l2cap_pi(sk)->num_conf_rsp <= L2CAP_CONF_MAX_CONF_RSP) {
+ if (chan->num_conf_rsp <= L2CAP_CONF_MAX_CONF_RSP) {
char req[64];
if (len > sizeof(req) - sizeof(struct l2cap_conf_req)) {
- l2cap_send_disconn_req(conn, sk, ECONNRESET);
+ l2cap_send_disconn_req(conn, chan, ECONNRESET);
goto done;
}
/* throw out any old stored conf requests */
result = L2CAP_CONF_SUCCESS;
- len = l2cap_parse_conf_rsp(sk, rsp->data,
- len, req, &result);
+ len = l2cap_parse_conf_rsp(chan, rsp->data, len,
+ req, &result);
if (len < 0) {
- l2cap_send_disconn_req(conn, sk, ECONNRESET);
+ l2cap_send_disconn_req(conn, chan, ECONNRESET);
goto done;
}
l2cap_send_cmd(conn, l2cap_get_ident(conn),
L2CAP_CONF_REQ, len, req);
- l2cap_pi(sk)->num_conf_req++;
+ chan->num_conf_req++;
if (result != L2CAP_CONF_SUCCESS)
goto done;
break;
@@ -2317,24 +2452,24 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr
default:
sk->sk_err = ECONNRESET;
l2cap_sock_set_timer(sk, HZ * 5);
- l2cap_send_disconn_req(conn, sk, ECONNRESET);
+ l2cap_send_disconn_req(conn, chan, ECONNRESET);
goto done;
}
if (flags & 0x01)
goto done;
- l2cap_pi(sk)->conf_state |= L2CAP_CONF_INPUT_DONE;
+ chan->conf_state |= L2CAP_CONF_INPUT_DONE;
- if (l2cap_pi(sk)->conf_state & L2CAP_CONF_OUTPUT_DONE) {
- set_default_fcs(l2cap_pi(sk));
+ if (chan->conf_state & L2CAP_CONF_OUTPUT_DONE) {
+ set_default_fcs(chan);
sk->sk_state = BT_CONNECTED;
- l2cap_pi(sk)->next_tx_seq = 0;
- l2cap_pi(sk)->expected_tx_seq = 0;
- __skb_queue_head_init(TX_QUEUE(sk));
- if (l2cap_pi(sk)->mode == L2CAP_MODE_ERTM)
- l2cap_ertm_init(sk);
+ chan->next_tx_seq = 0;
+ chan->expected_tx_seq = 0;
+ skb_queue_head_init(&chan->tx_q);
+ if (chan->mode == L2CAP_MODE_ERTM)
+ l2cap_ertm_init(chan);
l2cap_chan_ready(sk);
}
@@ -2349,6 +2484,7 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd
struct l2cap_disconn_req *req = (struct l2cap_disconn_req *) data;
struct l2cap_disconn_rsp rsp;
u16 dcid, scid;
+ struct l2cap_chan *chan;
struct sock *sk;
scid = __le16_to_cpu(req->scid);
@@ -2356,12 +2492,14 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd
BT_DBG("scid 0x%4.4x dcid 0x%4.4x", scid, dcid);
- sk = l2cap_get_chan_by_scid(&conn->chan_list, dcid);
- if (!sk)
+ chan = l2cap_get_chan_by_scid(conn, dcid);
+ if (!chan)
return 0;
- rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid);
- rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid);
+ sk = chan->sk;
+
+ rsp.dcid = cpu_to_le16(chan->scid);
+ rsp.scid = cpu_to_le16(chan->dcid);
l2cap_send_cmd(conn, cmd->ident, L2CAP_DISCONN_RSP, sizeof(rsp), &rsp);
sk->sk_shutdown = SHUTDOWN_MASK;
@@ -2375,7 +2513,7 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd
return 0;
}
- l2cap_chan_del(sk, ECONNRESET);
+ l2cap_chan_del(chan, ECONNRESET);
bh_unlock_sock(sk);
l2cap_sock_kill(sk);
@@ -2386,6 +2524,7 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd
{
struct l2cap_disconn_rsp *rsp = (struct l2cap_disconn_rsp *) data;
u16 dcid, scid;
+ struct l2cap_chan *chan;
struct sock *sk;
scid = __le16_to_cpu(rsp->scid);
@@ -2393,10 +2532,12 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd
BT_DBG("dcid 0x%4.4x scid 0x%4.4x", dcid, scid);
- sk = l2cap_get_chan_by_scid(&conn->chan_list, scid);
- if (!sk)
+ chan = l2cap_get_chan_by_scid(conn, scid);
+ if (!chan)
return 0;
+ sk = chan->sk;
+
/* don't delete l2cap channel if sk is owned by user */
if (sock_owned_by_user(sk)) {
sk->sk_state = BT_DISCONN;
@@ -2406,7 +2547,7 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd
return 0;
}
- l2cap_chan_del(sk, 0);
+ l2cap_chan_del(chan, 0);
bh_unlock_sock(sk);
l2cap_sock_kill(sk);
@@ -2463,6 +2604,11 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn, struct l2cap_cm
BT_DBG("type 0x%4.4x result 0x%2.2x", type, result);
+ /* L2CAP Info req/rsp are unbound to channels, add extra checks */
+ if (cmd->ident != conn->info_ident ||
+ conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE)
+ return 0;
+
del_timer(&conn->info_timer);
if (result != L2CAP_IR_SUCCESS) {
@@ -2673,7 +2819,8 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn,
if (err) {
struct l2cap_cmd_rej rej;
- BT_DBG("error %d", err);
+
+ BT_ERR("Wrong link type (%d)", err);
/* FIXME: Map err to a valid reason */
rej.reason = cpu_to_le16(0);
@@ -2687,12 +2834,12 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn,
kfree_skb(skb);
}
-static int l2cap_check_fcs(struct l2cap_pinfo *pi, struct sk_buff *skb)
+static int l2cap_check_fcs(struct l2cap_chan *chan, struct sk_buff *skb)
{
u16 our_fcs, rcv_fcs;
int hdr_size = L2CAP_HDR_SIZE + 2;
- if (pi->fcs == L2CAP_FCS_CRC16) {
+ if (chan->fcs == L2CAP_FCS_CRC16) {
skb_trim(skb, skb->len - 2);
rcv_fcs = get_unaligned_le16(skb->data + skb->len);
our_fcs = crc16(0, skb->data - hdr_size, skb->len + hdr_size);
@@ -2703,49 +2850,47 @@ static int l2cap_check_fcs(struct l2cap_pinfo *pi, struct sk_buff *skb)
return 0;
}
-static inline void l2cap_send_i_or_rr_or_rnr(struct sock *sk)
+static inline void l2cap_send_i_or_rr_or_rnr(struct l2cap_chan *chan)
{
- struct l2cap_pinfo *pi = l2cap_pi(sk);
u16 control = 0;
- pi->frames_sent = 0;
+ chan->frames_sent = 0;
- control |= pi->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT;
+ control |= chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT;
- if (pi->conn_state & L2CAP_CONN_LOCAL_BUSY) {
+ if (chan->conn_state & L2CAP_CONN_LOCAL_BUSY) {
control |= L2CAP_SUPER_RCV_NOT_READY;
- l2cap_send_sframe(pi, control);
- pi->conn_state |= L2CAP_CONN_RNR_SENT;
+ l2cap_send_sframe(chan, control);
+ chan->conn_state |= L2CAP_CONN_RNR_SENT;
}
- if (pi->conn_state & L2CAP_CONN_REMOTE_BUSY)
- l2cap_retransmit_frames(sk);
+ if (chan->conn_state & L2CAP_CONN_REMOTE_BUSY)
+ l2cap_retransmit_frames(chan);
- l2cap_ertm_send(sk);
+ l2cap_ertm_send(chan);
- if (!(pi->conn_state & L2CAP_CONN_LOCAL_BUSY) &&
- pi->frames_sent == 0) {
+ if (!(chan->conn_state & L2CAP_CONN_LOCAL_BUSY) &&
+ chan->frames_sent == 0) {
control |= L2CAP_SUPER_RCV_READY;
- l2cap_send_sframe(pi, control);
+ l2cap_send_sframe(chan, control);
}
}
-static int l2cap_add_to_srej_queue(struct sock *sk, struct sk_buff *skb, u8 tx_seq, u8 sar)
+static int l2cap_add_to_srej_queue(struct l2cap_chan *chan, struct sk_buff *skb, u8 tx_seq, u8 sar)
{
struct sk_buff *next_skb;
- struct l2cap_pinfo *pi = l2cap_pi(sk);
int tx_seq_offset, next_tx_seq_offset;
bt_cb(skb)->tx_seq = tx_seq;
bt_cb(skb)->sar = sar;
- next_skb = skb_peek(SREJ_QUEUE(sk));
+ next_skb = skb_peek(&chan->srej_q);
if (!next_skb) {
- __skb_queue_tail(SREJ_QUEUE(sk), skb);
+ __skb_queue_tail(&chan->srej_q, skb);
return 0;
}
- tx_seq_offset = (tx_seq - pi->buffer_seq) % 64;
+ tx_seq_offset = (tx_seq - chan->buffer_seq) % 64;
if (tx_seq_offset < 0)
tx_seq_offset += 64;
@@ -2754,53 +2899,52 @@ static int l2cap_add_to_srej_queue(struct sock *sk, struct sk_buff *skb, u8 tx_s
return -EINVAL;
next_tx_seq_offset = (bt_cb(next_skb)->tx_seq -
- pi->buffer_seq) % 64;
+ chan->buffer_seq) % 64;
if (next_tx_seq_offset < 0)
next_tx_seq_offset += 64;
if (next_tx_seq_offset > tx_seq_offset) {
- __skb_queue_before(SREJ_QUEUE(sk), next_skb, skb);
+ __skb_queue_before(&chan->srej_q, next_skb, skb);
return 0;
}
- if (skb_queue_is_last(SREJ_QUEUE(sk), next_skb))
+ if (skb_queue_is_last(&chan->srej_q, next_skb))
break;
- } while ((next_skb = skb_queue_next(SREJ_QUEUE(sk), next_skb)));
+ } while ((next_skb = skb_queue_next(&chan->srej_q, next_skb)));
- __skb_queue_tail(SREJ_QUEUE(sk), skb);
+ __skb_queue_tail(&chan->srej_q, skb);
return 0;
}
-static int l2cap_ertm_reassembly_sdu(struct sock *sk, struct sk_buff *skb, u16 control)
+static int l2cap_ertm_reassembly_sdu(struct l2cap_chan *chan, struct sk_buff *skb, u16 control)
{
- struct l2cap_pinfo *pi = l2cap_pi(sk);
struct sk_buff *_skb;
int err;
switch (control & L2CAP_CTRL_SAR) {
case L2CAP_SDU_UNSEGMENTED:
- if (pi->conn_state & L2CAP_CONN_SAR_SDU)
+ if (chan->conn_state & L2CAP_CONN_SAR_SDU)
goto drop;
- err = sock_queue_rcv_skb(sk, skb);
+ err = sock_queue_rcv_skb(chan->sk, skb);
if (!err)
return err;
break;
case L2CAP_SDU_START:
- if (pi->conn_state & L2CAP_CONN_SAR_SDU)
+ if (chan->conn_state & L2CAP_CONN_SAR_SDU)
goto drop;
- pi->sdu_len = get_unaligned_le16(skb->data);
+ chan->sdu_len = get_unaligned_le16(skb->data);
- if (pi->sdu_len > pi->imtu)
+ if (chan->sdu_len > chan->imtu)
goto disconnect;
- pi->sdu = bt_skb_alloc(pi->sdu_len, GFP_ATOMIC);
- if (!pi->sdu)
+ chan->sdu = bt_skb_alloc(chan->sdu_len, GFP_ATOMIC);
+ if (!chan->sdu)
return -ENOMEM;
/* pull sdu_len bytes only after alloc, because of Local Busy
@@ -2808,63 +2952,63 @@ static int l2cap_ertm_reassembly_sdu(struct sock *sk, struct sk_buff *skb, u16 c
* only once, i.e., when alloc does not fail */
skb_pull(skb, 2);
- memcpy(skb_put(pi->sdu, skb->len), skb->data, skb->len);
+ memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len);
- pi->conn_state |= L2CAP_CONN_SAR_SDU;
- pi->partial_sdu_len = skb->len;
+ chan->conn_state |= L2CAP_CONN_SAR_SDU;
+ chan->partial_sdu_len = skb->len;
break;
case L2CAP_SDU_CONTINUE:
- if (!(pi->conn_state & L2CAP_CONN_SAR_SDU))
+ if (!(chan->conn_state & L2CAP_CONN_SAR_SDU))
goto disconnect;
- if (!pi->sdu)
+ if (!chan->sdu)
goto disconnect;
- pi->partial_sdu_len += skb->len;
- if (pi->partial_sdu_len > pi->sdu_len)
+ chan->partial_sdu_len += skb->len;
+ if (chan->partial_sdu_len > chan->sdu_len)
goto drop;
- memcpy(skb_put(pi->sdu, skb->len), skb->data, skb->len);
+ memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len);
break;
case L2CAP_SDU_END:
- if (!(pi->conn_state & L2CAP_CONN_SAR_SDU))
+ if (!(chan->conn_state & L2CAP_CONN_SAR_SDU))
goto disconnect;
- if (!pi->sdu)
+ if (!chan->sdu)
goto disconnect;
- if (!(pi->conn_state & L2CAP_CONN_SAR_RETRY)) {
- pi->partial_sdu_len += skb->len;
+ if (!(chan->conn_state & L2CAP_CONN_SAR_RETRY)) {
+ chan->partial_sdu_len += skb->len;
- if (pi->partial_sdu_len > pi->imtu)
+ if (chan->partial_sdu_len > chan->imtu)
goto drop;
- if (pi->partial_sdu_len != pi->sdu_len)
+ if (chan->partial_sdu_len != chan->sdu_len)
goto drop;
- memcpy(skb_put(pi->sdu, skb->len), skb->data, skb->len);
+ memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len);
}
- _skb = skb_clone(pi->sdu, GFP_ATOMIC);
+ _skb = skb_clone(chan->sdu, GFP_ATOMIC);
if (!_skb) {
- pi->conn_state |= L2CAP_CONN_SAR_RETRY;
+ chan->conn_state |= L2CAP_CONN_SAR_RETRY;
return -ENOMEM;
}
- err = sock_queue_rcv_skb(sk, _skb);
+ err = sock_queue_rcv_skb(chan->sk, _skb);
if (err < 0) {
kfree_skb(_skb);
- pi->conn_state |= L2CAP_CONN_SAR_RETRY;
+ chan->conn_state |= L2CAP_CONN_SAR_RETRY;
return err;
}
- pi->conn_state &= ~L2CAP_CONN_SAR_RETRY;
- pi->conn_state &= ~L2CAP_CONN_SAR_SDU;
+ chan->conn_state &= ~L2CAP_CONN_SAR_RETRY;
+ chan->conn_state &= ~L2CAP_CONN_SAR_SDU;
- kfree_skb(pi->sdu);
+ kfree_skb(chan->sdu);
break;
}
@@ -2872,51 +3016,50 @@ static int l2cap_ertm_reassembly_sdu(struct sock *sk, struct sk_buff *skb, u16 c
return 0;
drop:
- kfree_skb(pi->sdu);
- pi->sdu = NULL;
+ kfree_skb(chan->sdu);
+ chan->sdu = NULL;
disconnect:
- l2cap_send_disconn_req(pi->conn, sk, ECONNRESET);
+ l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
kfree_skb(skb);
return 0;
}
-static int l2cap_try_push_rx_skb(struct sock *sk)
+static int l2cap_try_push_rx_skb(struct l2cap_chan *chan)
{
- struct l2cap_pinfo *pi = l2cap_pi(sk);
struct sk_buff *skb;
u16 control;
int err;
- while ((skb = skb_dequeue(BUSY_QUEUE(sk)))) {
+ while ((skb = skb_dequeue(&chan->busy_q))) {
control = bt_cb(skb)->sar << L2CAP_CTRL_SAR_SHIFT;
- err = l2cap_ertm_reassembly_sdu(sk, skb, control);
+ err = l2cap_ertm_reassembly_sdu(chan, skb, control);
if (err < 0) {
- skb_queue_head(BUSY_QUEUE(sk), skb);
+ skb_queue_head(&chan->busy_q, skb);
return -EBUSY;
}
- pi->buffer_seq = (pi->buffer_seq + 1) % 64;
+ chan->buffer_seq = (chan->buffer_seq + 1) % 64;
}
- if (!(pi->conn_state & L2CAP_CONN_RNR_SENT))
+ if (!(chan->conn_state & L2CAP_CONN_RNR_SENT))
goto done;
- control = pi->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT;
+ control = chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT;
control |= L2CAP_SUPER_RCV_READY | L2CAP_CTRL_POLL;
- l2cap_send_sframe(pi, control);
- l2cap_pi(sk)->retry_count = 1;
+ l2cap_send_sframe(chan, control);
+ chan->retry_count = 1;
- del_timer(&pi->retrans_timer);
+ del_timer(&chan->retrans_timer);
__mod_monitor_timer();
- l2cap_pi(sk)->conn_state |= L2CAP_CONN_WAIT_F;
+ chan->conn_state |= L2CAP_CONN_WAIT_F;
done:
- pi->conn_state &= ~L2CAP_CONN_LOCAL_BUSY;
- pi->conn_state &= ~L2CAP_CONN_RNR_SENT;
+ chan->conn_state &= ~L2CAP_CONN_LOCAL_BUSY;
+ chan->conn_state &= ~L2CAP_CONN_RNR_SENT;
- BT_DBG("sk %p, Exit local busy", sk);
+ BT_DBG("chan %p, Exit local busy", chan);
return 0;
}
@@ -2924,21 +3067,21 @@ done:
static void l2cap_busy_work(struct work_struct *work)
{
DECLARE_WAITQUEUE(wait, current);
- struct l2cap_pinfo *pi =
- container_of(work, struct l2cap_pinfo, busy_work);
- struct sock *sk = (struct sock *)pi;
+ struct l2cap_chan *chan =
+ container_of(work, struct l2cap_chan, busy_work);
+ struct sock *sk = chan->sk;
int n_tries = 0, timeo = HZ/5, err;
struct sk_buff *skb;
lock_sock(sk);
add_wait_queue(sk_sleep(sk), &wait);
- while ((skb = skb_peek(BUSY_QUEUE(sk)))) {
+ while ((skb = skb_peek(&chan->busy_q))) {
set_current_state(TASK_INTERRUPTIBLE);
if (n_tries++ > L2CAP_LOCAL_BUSY_TRIES) {
err = -EBUSY;
- l2cap_send_disconn_req(pi->conn, sk, EBUSY);
+ l2cap_send_disconn_req(chan->conn, chan, EBUSY);
break;
}
@@ -2958,7 +3101,7 @@ static void l2cap_busy_work(struct work_struct *work)
if (err)
break;
- if (l2cap_try_push_rx_skb(sk) == 0)
+ if (l2cap_try_push_rx_skb(chan) == 0)
break;
}
@@ -2968,48 +3111,46 @@ static void l2cap_busy_work(struct work_struct *work)
release_sock(sk);
}
-static int l2cap_push_rx_skb(struct sock *sk, struct sk_buff *skb, u16 control)
+static int l2cap_push_rx_skb(struct l2cap_chan *chan, struct sk_buff *skb, u16 control)
{
- struct l2cap_pinfo *pi = l2cap_pi(sk);
int sctrl, err;
- if (pi->conn_state & L2CAP_CONN_LOCAL_BUSY) {
+ if (chan->conn_state & L2CAP_CONN_LOCAL_BUSY) {
bt_cb(skb)->sar = control >> L2CAP_CTRL_SAR_SHIFT;
- __skb_queue_tail(BUSY_QUEUE(sk), skb);
- return l2cap_try_push_rx_skb(sk);
+ __skb_queue_tail(&chan->busy_q, skb);
+ return l2cap_try_push_rx_skb(chan);
}
- err = l2cap_ertm_reassembly_sdu(sk, skb, control);
+ err = l2cap_ertm_reassembly_sdu(chan, skb, control);
if (err >= 0) {
- pi->buffer_seq = (pi->buffer_seq + 1) % 64;
+ chan->buffer_seq = (chan->buffer_seq + 1) % 64;
return err;
}
/* Busy Condition */
- BT_DBG("sk %p, Enter local busy", sk);
+ BT_DBG("chan %p, Enter local busy", chan);
- pi->conn_state |= L2CAP_CONN_LOCAL_BUSY;
+ chan->conn_state |= L2CAP_CONN_LOCAL_BUSY;
bt_cb(skb)->sar = control >> L2CAP_CTRL_SAR_SHIFT;
- __skb_queue_tail(BUSY_QUEUE(sk), skb);
+ __skb_queue_tail(&chan->busy_q, skb);
- sctrl = pi->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT;
+ sctrl = chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT;
sctrl |= L2CAP_SUPER_RCV_NOT_READY;
- l2cap_send_sframe(pi, sctrl);
+ l2cap_send_sframe(chan, sctrl);
- pi->conn_state |= L2CAP_CONN_RNR_SENT;
+ chan->conn_state |= L2CAP_CONN_RNR_SENT;
- del_timer(&pi->ack_timer);
+ del_timer(&chan->ack_timer);
- queue_work(_busy_wq, &pi->busy_work);
+ queue_work(_busy_wq, &chan->busy_work);
return err;
}
-static int l2cap_streaming_reassembly_sdu(struct sock *sk, struct sk_buff *skb, u16 control)
+static int l2cap_streaming_reassembly_sdu(struct l2cap_chan *chan, struct sk_buff *skb, u16 control)
{
- struct l2cap_pinfo *pi = l2cap_pi(sk);
struct sk_buff *_skb;
int err = -EINVAL;
@@ -3020,80 +3161,80 @@ static int l2cap_streaming_reassembly_sdu(struct sock *sk, struct sk_buff *skb,
switch (control & L2CAP_CTRL_SAR) {
case L2CAP_SDU_UNSEGMENTED:
- if (pi->conn_state & L2CAP_CONN_SAR_SDU) {
- kfree_skb(pi->sdu);
+ if (chan->conn_state & L2CAP_CONN_SAR_SDU) {
+ kfree_skb(chan->sdu);
break;
}
- err = sock_queue_rcv_skb(sk, skb);
+ err = sock_queue_rcv_skb(chan->sk, skb);
if (!err)
return 0;
break;
case L2CAP_SDU_START:
- if (pi->conn_state & L2CAP_CONN_SAR_SDU) {
- kfree_skb(pi->sdu);
+ if (chan->conn_state & L2CAP_CONN_SAR_SDU) {
+ kfree_skb(chan->sdu);
break;
}
- pi->sdu_len = get_unaligned_le16(skb->data);
+ chan->sdu_len = get_unaligned_le16(skb->data);
skb_pull(skb, 2);
- if (pi->sdu_len > pi->imtu) {
+ if (chan->sdu_len > chan->imtu) {
err = -EMSGSIZE;
break;
}
- pi->sdu = bt_skb_alloc(pi->sdu_len, GFP_ATOMIC);
- if (!pi->sdu) {
+ chan->sdu = bt_skb_alloc(chan->sdu_len, GFP_ATOMIC);
+ if (!chan->sdu) {
err = -ENOMEM;
break;
}
- memcpy(skb_put(pi->sdu, skb->len), skb->data, skb->len);
+ memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len);
- pi->conn_state |= L2CAP_CONN_SAR_SDU;
- pi->partial_sdu_len = skb->len;
+ chan->conn_state |= L2CAP_CONN_SAR_SDU;
+ chan->partial_sdu_len = skb->len;
err = 0;
break;
case L2CAP_SDU_CONTINUE:
- if (!(pi->conn_state & L2CAP_CONN_SAR_SDU))
+ if (!(chan->conn_state & L2CAP_CONN_SAR_SDU))
break;
- memcpy(skb_put(pi->sdu, skb->len), skb->data, skb->len);
+ memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len);
- pi->partial_sdu_len += skb->len;
- if (pi->partial_sdu_len > pi->sdu_len)
- kfree_skb(pi->sdu);
+ chan->partial_sdu_len += skb->len;
+ if (chan->partial_sdu_len > chan->sdu_len)
+ kfree_skb(chan->sdu);
else
err = 0;
break;
case L2CAP_SDU_END:
- if (!(pi->conn_state & L2CAP_CONN_SAR_SDU))
+ if (!(chan->conn_state & L2CAP_CONN_SAR_SDU))
break;
- memcpy(skb_put(pi->sdu, skb->len), skb->data, skb->len);
+ memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len);
- pi->conn_state &= ~L2CAP_CONN_SAR_SDU;
- pi->partial_sdu_len += skb->len;
+ chan->conn_state &= ~L2CAP_CONN_SAR_SDU;
+ chan->partial_sdu_len += skb->len;
- if (pi->partial_sdu_len > pi->imtu)
+ if (chan->partial_sdu_len > chan->imtu)
goto drop;
- if (pi->partial_sdu_len == pi->sdu_len) {
- _skb = skb_clone(pi->sdu, GFP_ATOMIC);
- err = sock_queue_rcv_skb(sk, _skb);
+ if (chan->partial_sdu_len == chan->sdu_len) {
+ _skb = skb_clone(chan->sdu, GFP_ATOMIC);
+ err = sock_queue_rcv_skb(chan->sk, _skb);
if (err < 0)
kfree_skb(_skb);
}
err = 0;
drop:
- kfree_skb(pi->sdu);
+ kfree_skb(chan->sdu);
break;
}
@@ -3101,31 +3242,30 @@ drop:
return err;
}
-static void l2cap_check_srej_gap(struct sock *sk, u8 tx_seq)
+static void l2cap_check_srej_gap(struct l2cap_chan *chan, u8 tx_seq)
{
struct sk_buff *skb;
u16 control;
- while ((skb = skb_peek(SREJ_QUEUE(sk)))) {
+ while ((skb = skb_peek(&chan->srej_q))) {
if (bt_cb(skb)->tx_seq != tx_seq)
break;
- skb = skb_dequeue(SREJ_QUEUE(sk));
+ skb = skb_dequeue(&chan->srej_q);
control = bt_cb(skb)->sar << L2CAP_CTRL_SAR_SHIFT;
- l2cap_ertm_reassembly_sdu(sk, skb, control);
- l2cap_pi(sk)->buffer_seq_srej =
- (l2cap_pi(sk)->buffer_seq_srej + 1) % 64;
+ l2cap_ertm_reassembly_sdu(chan, skb, control);
+ chan->buffer_seq_srej =
+ (chan->buffer_seq_srej + 1) % 64;
tx_seq = (tx_seq + 1) % 64;
}
}
-static void l2cap_resend_srejframe(struct sock *sk, u8 tx_seq)
+static void l2cap_resend_srejframe(struct l2cap_chan *chan, u8 tx_seq)
{
- struct l2cap_pinfo *pi = l2cap_pi(sk);
struct srej_list *l, *tmp;
u16 control;
- list_for_each_entry_safe(l, tmp, SREJ_LIST(sk), list) {
+ list_for_each_entry_safe(l, tmp, &chan->srej_l, list) {
if (l->tx_seq == tx_seq) {
list_del(&l->list);
kfree(l);
@@ -3133,107 +3273,105 @@ static void l2cap_resend_srejframe(struct sock *sk, u8 tx_seq)
}
control = L2CAP_SUPER_SELECT_REJECT;
control |= l->tx_seq << L2CAP_CTRL_REQSEQ_SHIFT;
- l2cap_send_sframe(pi, control);
+ l2cap_send_sframe(chan, control);
list_del(&l->list);
- list_add_tail(&l->list, SREJ_LIST(sk));
+ list_add_tail(&l->list, &chan->srej_l);
}
}
-static void l2cap_send_srejframe(struct sock *sk, u8 tx_seq)
+static void l2cap_send_srejframe(struct l2cap_chan *chan, u8 tx_seq)
{
- struct l2cap_pinfo *pi = l2cap_pi(sk);
struct srej_list *new;
u16 control;
- while (tx_seq != pi->expected_tx_seq) {
+ while (tx_seq != chan->expected_tx_seq) {
control = L2CAP_SUPER_SELECT_REJECT;
- control |= pi->expected_tx_seq << L2CAP_CTRL_REQSEQ_SHIFT;
- l2cap_send_sframe(pi, control);
+ control |= chan->expected_tx_seq << L2CAP_CTRL_REQSEQ_SHIFT;
+ l2cap_send_sframe(chan, control);
new = kzalloc(sizeof(struct srej_list), GFP_ATOMIC);
- new->tx_seq = pi->expected_tx_seq;
- pi->expected_tx_seq = (pi->expected_tx_seq + 1) % 64;
- list_add_tail(&new->list, SREJ_LIST(sk));
+ new->tx_seq = chan->expected_tx_seq;
+ chan->expected_tx_seq = (chan->expected_tx_seq + 1) % 64;
+ list_add_tail(&new->list, &chan->srej_l);
}
- pi->expected_tx_seq = (pi->expected_tx_seq + 1) % 64;
+ chan->expected_tx_seq = (chan->expected_tx_seq + 1) % 64;
}
-static inline int l2cap_data_channel_iframe(struct sock *sk, u16 rx_control, struct sk_buff *skb)
+static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_control, struct sk_buff *skb)
{
- struct l2cap_pinfo *pi = l2cap_pi(sk);
u8 tx_seq = __get_txseq(rx_control);
u8 req_seq = __get_reqseq(rx_control);
u8 sar = rx_control >> L2CAP_CTRL_SAR_SHIFT;
int tx_seq_offset, expected_tx_seq_offset;
- int num_to_ack = (pi->tx_win/6) + 1;
+ int num_to_ack = (chan->tx_win/6) + 1;
int err = 0;
- BT_DBG("sk %p len %d tx_seq %d rx_control 0x%4.4x", sk, skb->len, tx_seq,
- rx_control);
+ BT_DBG("chan %p len %d tx_seq %d rx_control 0x%4.4x", chan, skb->len,
+ tx_seq, rx_control);
if (L2CAP_CTRL_FINAL & rx_control &&
- l2cap_pi(sk)->conn_state & L2CAP_CONN_WAIT_F) {
- del_timer(&pi->monitor_timer);
- if (pi->unacked_frames > 0)
+ chan->conn_state & L2CAP_CONN_WAIT_F) {
+ del_timer(&chan->monitor_timer);
+ if (chan->unacked_frames > 0)
__mod_retrans_timer();
- pi->conn_state &= ~L2CAP_CONN_WAIT_F;
+ chan->conn_state &= ~L2CAP_CONN_WAIT_F;
}
- pi->expected_ack_seq = req_seq;
- l2cap_drop_acked_frames(sk);
+ chan->expected_ack_seq = req_seq;
+ l2cap_drop_acked_frames(chan);
- if (tx_seq == pi->expected_tx_seq)
+ if (tx_seq == chan->expected_tx_seq)
goto expected;
- tx_seq_offset = (tx_seq - pi->buffer_seq) % 64;
+ tx_seq_offset = (tx_seq - chan->buffer_seq) % 64;
if (tx_seq_offset < 0)
tx_seq_offset += 64;
/* invalid tx_seq */
- if (tx_seq_offset >= pi->tx_win) {
- l2cap_send_disconn_req(pi->conn, sk, ECONNRESET);
+ if (tx_seq_offset >= chan->tx_win) {
+ l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
goto drop;
}
- if (pi->conn_state == L2CAP_CONN_LOCAL_BUSY)
+ if (chan->conn_state == L2CAP_CONN_LOCAL_BUSY)
goto drop;
- if (pi->conn_state & L2CAP_CONN_SREJ_SENT) {
+ if (chan->conn_state & L2CAP_CONN_SREJ_SENT) {
struct srej_list *first;
- first = list_first_entry(SREJ_LIST(sk),
+ first = list_first_entry(&chan->srej_l,
struct srej_list, list);
if (tx_seq == first->tx_seq) {
- l2cap_add_to_srej_queue(sk, skb, tx_seq, sar);
- l2cap_check_srej_gap(sk, tx_seq);
+ l2cap_add_to_srej_queue(chan, skb, tx_seq, sar);
+ l2cap_check_srej_gap(chan, tx_seq);
list_del(&first->list);
kfree(first);
- if (list_empty(SREJ_LIST(sk))) {
- pi->buffer_seq = pi->buffer_seq_srej;
- pi->conn_state &= ~L2CAP_CONN_SREJ_SENT;
- l2cap_send_ack(pi);
- BT_DBG("sk %p, Exit SREJ_SENT", sk);
+ if (list_empty(&chan->srej_l)) {
+ chan->buffer_seq = chan->buffer_seq_srej;
+ chan->conn_state &= ~L2CAP_CONN_SREJ_SENT;
+ l2cap_send_ack(chan);
+ BT_DBG("chan %p, Exit SREJ_SENT", chan);
}
} else {
struct srej_list *l;
/* duplicated tx_seq */
- if (l2cap_add_to_srej_queue(sk, skb, tx_seq, sar) < 0)
+ if (l2cap_add_to_srej_queue(chan, skb, tx_seq, sar) < 0)
goto drop;
- list_for_each_entry(l, SREJ_LIST(sk), list) {
+ list_for_each_entry(l, &chan->srej_l, list) {
if (l->tx_seq == tx_seq) {
- l2cap_resend_srejframe(sk, tx_seq);
+ l2cap_resend_srejframe(chan, tx_seq);
return 0;
}
}
- l2cap_send_srejframe(sk, tx_seq);
+ l2cap_send_srejframe(chan, tx_seq);
}
} else {
expected_tx_seq_offset =
- (pi->expected_tx_seq - pi->buffer_seq) % 64;
+ (chan->expected_tx_seq - chan->buffer_seq) % 64;
if (expected_tx_seq_offset < 0)
expected_tx_seq_offset += 64;
@@ -3241,51 +3379,51 @@ static inline int l2cap_data_channel_iframe(struct sock *sk, u16 rx_control, str
if (tx_seq_offset < expected_tx_seq_offset)
goto drop;
- pi->conn_state |= L2CAP_CONN_SREJ_SENT;
+ chan->conn_state |= L2CAP_CONN_SREJ_SENT;
- BT_DBG("sk %p, Enter SREJ", sk);
+ BT_DBG("chan %p, Enter SREJ", chan);
- INIT_LIST_HEAD(SREJ_LIST(sk));
- pi->buffer_seq_srej = pi->buffer_seq;
+ INIT_LIST_HEAD(&chan->srej_l);
+ chan->buffer_seq_srej = chan->buffer_seq;
- __skb_queue_head_init(SREJ_QUEUE(sk));
- __skb_queue_head_init(BUSY_QUEUE(sk));
- l2cap_add_to_srej_queue(sk, skb, tx_seq, sar);
+ __skb_queue_head_init(&chan->srej_q);
+ __skb_queue_head_init(&chan->busy_q);
+ l2cap_add_to_srej_queue(chan, skb, tx_seq, sar);
- pi->conn_state |= L2CAP_CONN_SEND_PBIT;
+ chan->conn_state |= L2CAP_CONN_SEND_PBIT;
- l2cap_send_srejframe(sk, tx_seq);
+ l2cap_send_srejframe(chan, tx_seq);
- del_timer(&pi->ack_timer);
+ del_timer(&chan->ack_timer);
}
return 0;
expected:
- pi->expected_tx_seq = (pi->expected_tx_seq + 1) % 64;
+ chan->expected_tx_seq = (chan->expected_tx_seq + 1) % 64;
- if (pi->conn_state & L2CAP_CONN_SREJ_SENT) {
+ if (chan->conn_state & L2CAP_CONN_SREJ_SENT) {
bt_cb(skb)->tx_seq = tx_seq;
bt_cb(skb)->sar = sar;
- __skb_queue_tail(SREJ_QUEUE(sk), skb);
+ __skb_queue_tail(&chan->srej_q, skb);
return 0;
}
- err = l2cap_push_rx_skb(sk, skb, rx_control);
+ err = l2cap_push_rx_skb(chan, skb, rx_control);
if (err < 0)
return 0;
if (rx_control & L2CAP_CTRL_FINAL) {
- if (pi->conn_state & L2CAP_CONN_REJ_ACT)
- pi->conn_state &= ~L2CAP_CONN_REJ_ACT;
+ if (chan->conn_state & L2CAP_CONN_REJ_ACT)
+ chan->conn_state &= ~L2CAP_CONN_REJ_ACT;
else
- l2cap_retransmit_frames(sk);
+ l2cap_retransmit_frames(chan);
}
__mod_ack_timer();
- pi->num_acked = (pi->num_acked + 1) % num_to_ack;
- if (pi->num_acked == num_to_ack - 1)
- l2cap_send_ack(pi);
+ chan->num_acked = (chan->num_acked + 1) % num_to_ack;
+ if (chan->num_acked == num_to_ack - 1)
+ l2cap_send_ack(chan);
return 0;
@@ -3294,165 +3432,160 @@ drop:
return 0;
}
-static inline void l2cap_data_channel_rrframe(struct sock *sk, u16 rx_control)
+static inline void l2cap_data_channel_rrframe(struct l2cap_chan *chan, u16 rx_control)
{
- struct l2cap_pinfo *pi = l2cap_pi(sk);
-
- BT_DBG("sk %p, req_seq %d ctrl 0x%4.4x", sk, __get_reqseq(rx_control),
+ BT_DBG("chan %p, req_seq %d ctrl 0x%4.4x", chan, __get_reqseq(rx_control),
rx_control);
- pi->expected_ack_seq = __get_reqseq(rx_control);
- l2cap_drop_acked_frames(sk);
+ chan->expected_ack_seq = __get_reqseq(rx_control);
+ l2cap_drop_acked_frames(chan);
if (rx_control & L2CAP_CTRL_POLL) {
- pi->conn_state |= L2CAP_CONN_SEND_FBIT;
- if (pi->conn_state & L2CAP_CONN_SREJ_SENT) {
- if ((pi->conn_state & L2CAP_CONN_REMOTE_BUSY) &&
- (pi->unacked_frames > 0))
+ chan->conn_state |= L2CAP_CONN_SEND_FBIT;
+ if (chan->conn_state & L2CAP_CONN_SREJ_SENT) {
+ if ((chan->conn_state & L2CAP_CONN_REMOTE_BUSY) &&
+ (chan->unacked_frames > 0))
__mod_retrans_timer();
- pi->conn_state &= ~L2CAP_CONN_REMOTE_BUSY;
- l2cap_send_srejtail(sk);
+ chan->conn_state &= ~L2CAP_CONN_REMOTE_BUSY;
+ l2cap_send_srejtail(chan);
} else {
- l2cap_send_i_or_rr_or_rnr(sk);
+ l2cap_send_i_or_rr_or_rnr(chan);
}
} else if (rx_control & L2CAP_CTRL_FINAL) {
- pi->conn_state &= ~L2CAP_CONN_REMOTE_BUSY;
+ chan->conn_state &= ~L2CAP_CONN_REMOTE_BUSY;
- if (pi->conn_state & L2CAP_CONN_REJ_ACT)
- pi->conn_state &= ~L2CAP_CONN_REJ_ACT;
+ if (chan->conn_state & L2CAP_CONN_REJ_ACT)
+ chan->conn_state &= ~L2CAP_CONN_REJ_ACT;
else
- l2cap_retransmit_frames(sk);
+ l2cap_retransmit_frames(chan);
} else {
- if ((pi->conn_state & L2CAP_CONN_REMOTE_BUSY) &&
- (pi->unacked_frames > 0))
+ if ((chan->conn_state & L2CAP_CONN_REMOTE_BUSY) &&
+ (chan->unacked_frames > 0))
__mod_retrans_timer();
- pi->conn_state &= ~L2CAP_CONN_REMOTE_BUSY;
- if (pi->conn_state & L2CAP_CONN_SREJ_SENT)
- l2cap_send_ack(pi);
+ chan->conn_state &= ~L2CAP_CONN_REMOTE_BUSY;
+ if (chan->conn_state & L2CAP_CONN_SREJ_SENT)
+ l2cap_send_ack(chan);
else
- l2cap_ertm_send(sk);
+ l2cap_ertm_send(chan);
}
}
-static inline void l2cap_data_channel_rejframe(struct sock *sk, u16 rx_control)
+static inline void l2cap_data_channel_rejframe(struct l2cap_chan *chan, u16 rx_control)
{
- struct l2cap_pinfo *pi = l2cap_pi(sk);
u8 tx_seq = __get_reqseq(rx_control);
- BT_DBG("sk %p, req_seq %d ctrl 0x%4.4x", sk, tx_seq, rx_control);
+ BT_DBG("chan %p, req_seq %d ctrl 0x%4.4x", chan, tx_seq, rx_control);
- pi->conn_state &= ~L2CAP_CONN_REMOTE_BUSY;
+ chan->conn_state &= ~L2CAP_CONN_REMOTE_BUSY;
- pi->expected_ack_seq = tx_seq;
- l2cap_drop_acked_frames(sk);
+ chan->expected_ack_seq = tx_seq;
+ l2cap_drop_acked_frames(chan);
if (rx_control & L2CAP_CTRL_FINAL) {
- if (pi->conn_state & L2CAP_CONN_REJ_ACT)
- pi->conn_state &= ~L2CAP_CONN_REJ_ACT;
+ if (chan->conn_state & L2CAP_CONN_REJ_ACT)
+ chan->conn_state &= ~L2CAP_CONN_REJ_ACT;
else
- l2cap_retransmit_frames(sk);
+ l2cap_retransmit_frames(chan);
} else {
- l2cap_retransmit_frames(sk);
+ l2cap_retransmit_frames(chan);
- if (pi->conn_state & L2CAP_CONN_WAIT_F)
- pi->conn_state |= L2CAP_CONN_REJ_ACT;
+ if (chan->conn_state & L2CAP_CONN_WAIT_F)
+ chan->conn_state |= L2CAP_CONN_REJ_ACT;
}
}
-static inline void l2cap_data_channel_srejframe(struct sock *sk, u16 rx_control)
+static inline void l2cap_data_channel_srejframe(struct l2cap_chan *chan, u16 rx_control)
{
- struct l2cap_pinfo *pi = l2cap_pi(sk);
u8 tx_seq = __get_reqseq(rx_control);
- BT_DBG("sk %p, req_seq %d ctrl 0x%4.4x", sk, tx_seq, rx_control);
+ BT_DBG("chan %p, req_seq %d ctrl 0x%4.4x", chan, tx_seq, rx_control);
- pi->conn_state &= ~L2CAP_CONN_REMOTE_BUSY;
+ chan->conn_state &= ~L2CAP_CONN_REMOTE_BUSY;
if (rx_control & L2CAP_CTRL_POLL) {
- pi->expected_ack_seq = tx_seq;
- l2cap_drop_acked_frames(sk);
+ chan->expected_ack_seq = tx_seq;
+ l2cap_drop_acked_frames(chan);
- pi->conn_state |= L2CAP_CONN_SEND_FBIT;
- l2cap_retransmit_one_frame(sk, tx_seq);
+ chan->conn_state |= L2CAP_CONN_SEND_FBIT;
+ l2cap_retransmit_one_frame(chan, tx_seq);
- l2cap_ertm_send(sk);
+ l2cap_ertm_send(chan);
- if (pi->conn_state & L2CAP_CONN_WAIT_F) {
- pi->srej_save_reqseq = tx_seq;
- pi->conn_state |= L2CAP_CONN_SREJ_ACT;
+ if (chan->conn_state & L2CAP_CONN_WAIT_F) {
+ chan->srej_save_reqseq = tx_seq;
+ chan->conn_state |= L2CAP_CONN_SREJ_ACT;
}
} else if (rx_control & L2CAP_CTRL_FINAL) {
- if ((pi->conn_state & L2CAP_CONN_SREJ_ACT) &&
- pi->srej_save_reqseq == tx_seq)
- pi->conn_state &= ~L2CAP_CONN_SREJ_ACT;
+ if ((chan->conn_state & L2CAP_CONN_SREJ_ACT) &&
+ chan->srej_save_reqseq == tx_seq)
+ chan->conn_state &= ~L2CAP_CONN_SREJ_ACT;
else
- l2cap_retransmit_one_frame(sk, tx_seq);
+ l2cap_retransmit_one_frame(chan, tx_seq);
} else {
- l2cap_retransmit_one_frame(sk, tx_seq);
- if (pi->conn_state & L2CAP_CONN_WAIT_F) {
- pi->srej_save_reqseq = tx_seq;
- pi->conn_state |= L2CAP_CONN_SREJ_ACT;
+ l2cap_retransmit_one_frame(chan, tx_seq);
+ if (chan->conn_state & L2CAP_CONN_WAIT_F) {
+ chan->srej_save_reqseq = tx_seq;
+ chan->conn_state |= L2CAP_CONN_SREJ_ACT;
}
}
}
-static inline void l2cap_data_channel_rnrframe(struct sock *sk, u16 rx_control)
+static inline void l2cap_data_channel_rnrframe(struct l2cap_chan *chan, u16 rx_control)
{
- struct l2cap_pinfo *pi = l2cap_pi(sk);
u8 tx_seq = __get_reqseq(rx_control);
- BT_DBG("sk %p, req_seq %d ctrl 0x%4.4x", sk, tx_seq, rx_control);
+ BT_DBG("chan %p, req_seq %d ctrl 0x%4.4x", chan, tx_seq, rx_control);
- pi->conn_state |= L2CAP_CONN_REMOTE_BUSY;
- pi->expected_ack_seq = tx_seq;
- l2cap_drop_acked_frames(sk);
+ chan->conn_state |= L2CAP_CONN_REMOTE_BUSY;
+ chan->expected_ack_seq = tx_seq;
+ l2cap_drop_acked_frames(chan);
if (rx_control & L2CAP_CTRL_POLL)
- pi->conn_state |= L2CAP_CONN_SEND_FBIT;
+ chan->conn_state |= L2CAP_CONN_SEND_FBIT;
- if (!(pi->conn_state & L2CAP_CONN_SREJ_SENT)) {
- del_timer(&pi->retrans_timer);
+ if (!(chan->conn_state & L2CAP_CONN_SREJ_SENT)) {
+ del_timer(&chan->retrans_timer);
if (rx_control & L2CAP_CTRL_POLL)
- l2cap_send_rr_or_rnr(pi, L2CAP_CTRL_FINAL);
+ l2cap_send_rr_or_rnr(chan, L2CAP_CTRL_FINAL);
return;
}
if (rx_control & L2CAP_CTRL_POLL)
- l2cap_send_srejtail(sk);
+ l2cap_send_srejtail(chan);
else
- l2cap_send_sframe(pi, L2CAP_SUPER_RCV_READY);
+ l2cap_send_sframe(chan, L2CAP_SUPER_RCV_READY);
}
-static inline int l2cap_data_channel_sframe(struct sock *sk, u16 rx_control, struct sk_buff *skb)
+static inline int l2cap_data_channel_sframe(struct l2cap_chan *chan, u16 rx_control, struct sk_buff *skb)
{
- BT_DBG("sk %p rx_control 0x%4.4x len %d", sk, rx_control, skb->len);
+ BT_DBG("chan %p rx_control 0x%4.4x len %d", chan, rx_control, skb->len);
if (L2CAP_CTRL_FINAL & rx_control &&
- l2cap_pi(sk)->conn_state & L2CAP_CONN_WAIT_F) {
- del_timer(&l2cap_pi(sk)->monitor_timer);
- if (l2cap_pi(sk)->unacked_frames > 0)
+ chan->conn_state & L2CAP_CONN_WAIT_F) {
+ del_timer(&chan->monitor_timer);
+ if (chan->unacked_frames > 0)
__mod_retrans_timer();
- l2cap_pi(sk)->conn_state &= ~L2CAP_CONN_WAIT_F;
+ chan->conn_state &= ~L2CAP_CONN_WAIT_F;
}
switch (rx_control & L2CAP_CTRL_SUPERVISE) {
case L2CAP_SUPER_RCV_READY:
- l2cap_data_channel_rrframe(sk, rx_control);
+ l2cap_data_channel_rrframe(chan, rx_control);
break;
case L2CAP_SUPER_REJECT:
- l2cap_data_channel_rejframe(sk, rx_control);
+ l2cap_data_channel_rejframe(chan, rx_control);
break;
case L2CAP_SUPER_SELECT_REJECT:
- l2cap_data_channel_srejframe(sk, rx_control);
+ l2cap_data_channel_srejframe(chan, rx_control);
break;
case L2CAP_SUPER_RCV_NOT_READY:
- l2cap_data_channel_rnrframe(sk, rx_control);
+ l2cap_data_channel_rnrframe(chan, rx_control);
break;
}
@@ -3462,7 +3595,7 @@ static inline int l2cap_data_channel_sframe(struct sock *sk, u16 rx_control, str
static int l2cap_ertm_data_rcv(struct sock *sk, struct sk_buff *skb)
{
- struct l2cap_pinfo *pi = l2cap_pi(sk);
+ struct l2cap_chan *chan = l2cap_pi(sk)->chan;
u16 control;
u8 req_seq;
int len, next_tx_seq_offset, req_seq_offset;
@@ -3476,51 +3609,51 @@ static int l2cap_ertm_data_rcv(struct sock *sk, struct sk_buff *skb)
* Receiver will miss it and start proper recovery
* procedures and ask retransmission.
*/
- if (l2cap_check_fcs(pi, skb))
+ if (l2cap_check_fcs(chan, skb))
goto drop;
if (__is_sar_start(control) && __is_iframe(control))
len -= 2;
- if (pi->fcs == L2CAP_FCS_CRC16)
+ if (chan->fcs == L2CAP_FCS_CRC16)
len -= 2;
- if (len > pi->mps) {
- l2cap_send_disconn_req(pi->conn, sk, ECONNRESET);
+ if (len > chan->mps) {
+ l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
goto drop;
}
req_seq = __get_reqseq(control);
- req_seq_offset = (req_seq - pi->expected_ack_seq) % 64;
+ req_seq_offset = (req_seq - chan->expected_ack_seq) % 64;
if (req_seq_offset < 0)
req_seq_offset += 64;
next_tx_seq_offset =
- (pi->next_tx_seq - pi->expected_ack_seq) % 64;
+ (chan->next_tx_seq - chan->expected_ack_seq) % 64;
if (next_tx_seq_offset < 0)
next_tx_seq_offset += 64;
/* check for invalid req-seq */
if (req_seq_offset > next_tx_seq_offset) {
- l2cap_send_disconn_req(pi->conn, sk, ECONNRESET);
+ l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
goto drop;
}
if (__is_iframe(control)) {
if (len < 0) {
- l2cap_send_disconn_req(pi->conn, sk, ECONNRESET);
+ l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
goto drop;
}
- l2cap_data_channel_iframe(sk, control, skb);
+ l2cap_data_channel_iframe(chan, control, skb);
} else {
if (len != 0) {
BT_ERR("%d", len);
- l2cap_send_disconn_req(pi->conn, sk, ECONNRESET);
+ l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
goto drop;
}
- l2cap_data_channel_sframe(sk, control, skb);
+ l2cap_data_channel_sframe(chan, control, skb);
}
return 0;
@@ -3532,33 +3665,35 @@ drop:
static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk_buff *skb)
{
- struct sock *sk;
+ struct l2cap_chan *chan;
+ struct sock *sk = NULL;
struct l2cap_pinfo *pi;
u16 control;
u8 tx_seq;
int len;
- sk = l2cap_get_chan_by_scid(&conn->chan_list, cid);
- if (!sk) {
+ chan = l2cap_get_chan_by_scid(conn, cid);
+ if (!chan) {
BT_DBG("unknown cid 0x%4.4x", cid);
goto drop;
}
+ sk = chan->sk;
pi = l2cap_pi(sk);
- BT_DBG("sk %p, len %d", sk, skb->len);
+ BT_DBG("chan %p, len %d", chan, skb->len);
if (sk->sk_state != BT_CONNECTED)
goto drop;
- switch (pi->mode) {
+ switch (chan->mode) {
case L2CAP_MODE_BASIC:
/* If socket recv buffers overflows we drop data here
* which is *bad* because L2CAP has to be reliable.
* But we don't have any other choice. L2CAP doesn't
* provide flow control mechanism. */
- if (pi->imtu < skb->len)
+ if (chan->imtu < skb->len)
goto drop;
if (!sock_queue_rcv_skb(sk, skb))
@@ -3580,31 +3715,31 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk
skb_pull(skb, 2);
len = skb->len;
- if (l2cap_check_fcs(pi, skb))
+ if (l2cap_check_fcs(chan, skb))
goto drop;
if (__is_sar_start(control))
len -= 2;
- if (pi->fcs == L2CAP_FCS_CRC16)
+ if (chan->fcs == L2CAP_FCS_CRC16)
len -= 2;
- if (len > pi->mps || len < 0 || __is_sframe(control))
+ if (len > chan->mps || len < 0 || __is_sframe(control))
goto drop;
tx_seq = __get_txseq(control);
- if (pi->expected_tx_seq == tx_seq)
- pi->expected_tx_seq = (pi->expected_tx_seq + 1) % 64;
+ if (chan->expected_tx_seq == tx_seq)
+ chan->expected_tx_seq = (chan->expected_tx_seq + 1) % 64;
else
- pi->expected_tx_seq = (tx_seq + 1) % 64;
+ chan->expected_tx_seq = (tx_seq + 1) % 64;
- l2cap_streaming_reassembly_sdu(sk, skb, control);
+ l2cap_streaming_reassembly_sdu(chan, skb, control);
goto done;
default:
- BT_DBG("sk %p: bad mode 0x%2.2x", sk, pi->mode);
+ BT_DBG("chan %p: bad mode 0x%2.2x", chan, chan->mode);
break;
}
@@ -3620,12 +3755,48 @@ done:
static inline int l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm, struct sk_buff *skb)
{
- struct sock *sk;
+ struct sock *sk = NULL;
+ struct l2cap_chan *chan;
- sk = l2cap_get_sock_by_psm(0, psm, conn->src);
- if (!sk)
+ chan = l2cap_global_chan_by_psm(0, psm, conn->src);
+ if (!chan)
+ goto drop;
+
+ sk = chan->sk;
+
+ bh_lock_sock(sk);
+
+ BT_DBG("sk %p, len %d", sk, skb->len);
+
+ if (sk->sk_state != BT_BOUND && sk->sk_state != BT_CONNECTED)
+ goto drop;
+
+ if (l2cap_pi(sk)->chan->imtu < skb->len)
+ goto drop;
+
+ if (!sock_queue_rcv_skb(sk, skb))
+ goto done;
+
+drop:
+ kfree_skb(skb);
+
+done:
+ if (sk)
+ bh_unlock_sock(sk);
+ return 0;
+}
+
+static inline int l2cap_att_channel(struct l2cap_conn *conn, __le16 cid, struct sk_buff *skb)
+{
+ struct sock *sk = NULL;
+ struct l2cap_chan *chan;
+
+ chan = l2cap_global_chan_by_scid(0, cid, conn->src);
+ if (!chan)
goto drop;
+ sk = chan->sk;
+
bh_lock_sock(sk);
BT_DBG("sk %p, len %d", sk, skb->len);
@@ -3633,7 +3804,7 @@ static inline int l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm, str
if (sk->sk_state != BT_BOUND && sk->sk_state != BT_CONNECTED)
goto drop;
- if (l2cap_pi(sk)->imtu < skb->len)
+ if (l2cap_pi(sk)->chan->imtu < skb->len)
goto drop;
if (!sock_queue_rcv_skb(sk, skb))
@@ -3677,6 +3848,10 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
l2cap_conless_channel(conn, psm, skb);
break;
+ case L2CAP_CID_LE_DATA:
+ l2cap_att_channel(conn, cid, skb);
+ break;
+
default:
l2cap_data_channel(conn, cid, skb);
break;
@@ -3688,8 +3863,7 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
static int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
{
int exact = 0, lm1 = 0, lm2 = 0;
- register struct sock *sk;
- struct hlist_node *node;
+ struct l2cap_chan *c;
if (type != ACL_LINK)
return -EINVAL;
@@ -3697,23 +3871,25 @@ static int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
BT_DBG("hdev %s, bdaddr %s", hdev->name, batostr(bdaddr));
/* Find listening sockets and check their link_mode */
- read_lock(&l2cap_sk_list.lock);
- sk_for_each(sk, node, &l2cap_sk_list.head) {
+ read_lock(&chan_list_lock);
+ list_for_each_entry(c, &chan_list, global_l) {
+ struct sock *sk = c->sk;
+
if (sk->sk_state != BT_LISTEN)
continue;
if (!bacmp(&bt_sk(sk)->src, &hdev->bdaddr)) {
lm1 |= HCI_LM_ACCEPT;
- if (l2cap_pi(sk)->role_switch)
+ if (c->role_switch)
lm1 |= HCI_LM_MASTER;
exact++;
} else if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY)) {
lm2 |= HCI_LM_ACCEPT;
- if (l2cap_pi(sk)->role_switch)
+ if (c->role_switch)
lm2 |= HCI_LM_MASTER;
}
}
- read_unlock(&l2cap_sk_list.lock);
+ read_unlock(&chan_list_lock);
return exact ? lm1 : lm2;
}
@@ -3761,49 +3937,50 @@ static int l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason)
return 0;
}
-static inline void l2cap_check_encryption(struct sock *sk, u8 encrypt)
+static inline void l2cap_check_encryption(struct l2cap_chan *chan, u8 encrypt)
{
+ struct sock *sk = chan->sk;
+
if (sk->sk_type != SOCK_SEQPACKET && sk->sk_type != SOCK_STREAM)
return;
if (encrypt == 0x00) {
- if (l2cap_pi(sk)->sec_level == BT_SECURITY_MEDIUM) {
+ if (chan->sec_level == BT_SECURITY_MEDIUM) {
l2cap_sock_clear_timer(sk);
l2cap_sock_set_timer(sk, HZ * 5);
- } else if (l2cap_pi(sk)->sec_level == BT_SECURITY_HIGH)
+ } else if (chan->sec_level == BT_SECURITY_HIGH)
__l2cap_sock_close(sk, ECONNREFUSED);
} else {
- if (l2cap_pi(sk)->sec_level == BT_SECURITY_MEDIUM)
+ if (chan->sec_level == BT_SECURITY_MEDIUM)
l2cap_sock_clear_timer(sk);
}
}
static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
{
- struct l2cap_chan_list *l;
struct l2cap_conn *conn = hcon->l2cap_data;
- struct sock *sk;
+ struct l2cap_chan *chan;
if (!conn)
return 0;
- l = &conn->chan_list;
-
BT_DBG("conn %p", conn);
- read_lock(&l->lock);
+ read_lock(&conn->chan_lock);
+
+ list_for_each_entry(chan, &conn->chan_l, list) {
+ struct sock *sk = chan->sk;
- for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
bh_lock_sock(sk);
- if (l2cap_pi(sk)->conf_state & L2CAP_CONF_CONNECT_PEND) {
+ if (chan->conf_state & L2CAP_CONF_CONNECT_PEND) {
bh_unlock_sock(sk);
continue;
}
if (!status && (sk->sk_state == BT_CONNECTED ||
sk->sk_state == BT_CONFIG)) {
- l2cap_check_encryption(sk, encrypt);
+ l2cap_check_encryption(chan, encrypt);
bh_unlock_sock(sk);
continue;
}
@@ -3811,13 +3988,13 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
if (sk->sk_state == BT_CONNECT) {
if (!status) {
struct l2cap_conn_req req;
- req.scid = cpu_to_le16(l2cap_pi(sk)->scid);
- req.psm = l2cap_pi(sk)->psm;
+ req.scid = cpu_to_le16(chan->scid);
+ req.psm = chan->psm;
- l2cap_pi(sk)->ident = l2cap_get_ident(conn);
- l2cap_pi(sk)->conf_state |= L2CAP_CONF_CONNECT_PEND;
+ chan->ident = l2cap_get_ident(conn);
+ chan->conf_state |= L2CAP_CONF_CONNECT_PEND;
- l2cap_send_cmd(conn, l2cap_pi(sk)->ident,
+ l2cap_send_cmd(conn, chan->ident,
L2CAP_CONN_REQ, sizeof(req), &req);
} else {
l2cap_sock_clear_timer(sk);
@@ -3836,18 +4013,18 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
result = L2CAP_CR_SEC_BLOCK;
}
- rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid);
- rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid);
+ rsp.scid = cpu_to_le16(chan->dcid);
+ rsp.dcid = cpu_to_le16(chan->scid);
rsp.result = cpu_to_le16(result);
rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
- l2cap_send_cmd(conn, l2cap_pi(sk)->ident,
- L2CAP_CONN_RSP, sizeof(rsp), &rsp);
+ l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP,
+ sizeof(rsp), &rsp);
}
bh_unlock_sock(sk);
}
- read_unlock(&l->lock);
+ read_unlock(&conn->chan_lock);
return 0;
}
@@ -3866,7 +4043,7 @@ static int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 fl
if (!(flags & ACL_CONT)) {
struct l2cap_hdr *hdr;
- struct sock *sk;
+ struct l2cap_chan *chan;
u16 cid;
int len;
@@ -3904,18 +4081,21 @@ static int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 fl
goto drop;
}
- sk = l2cap_get_chan_by_scid(&conn->chan_list, cid);
+ chan = l2cap_get_chan_by_scid(conn, cid);
- if (sk && l2cap_pi(sk)->imtu < len - L2CAP_HDR_SIZE) {
- BT_ERR("Frame exceeding recv MTU (len %d, MTU %d)",
- len, l2cap_pi(sk)->imtu);
- bh_unlock_sock(sk);
- l2cap_conn_unreliable(conn, ECOMM);
- goto drop;
- }
+ if (chan && chan->sk) {
+ struct sock *sk = chan->sk;
- if (sk)
+ if (chan->imtu < len - L2CAP_HDR_SIZE) {
+ BT_ERR("Frame exceeding recv MTU (len %d, "
+ "MTU %d)", len,
+ chan->imtu);
+ bh_unlock_sock(sk);
+ l2cap_conn_unreliable(conn, ECOMM);
+ goto drop;
+ }
bh_unlock_sock(sk);
+ }
/* Allocate skb for the complete frame (with header) */
conn->rx_skb = bt_skb_alloc(len, GFP_ATOMIC);
@@ -3962,24 +4142,22 @@ drop:
static int l2cap_debugfs_show(struct seq_file *f, void *p)
{
- struct sock *sk;
- struct hlist_node *node;
+ struct l2cap_chan *c;
- read_lock_bh(&l2cap_sk_list.lock);
+ read_lock_bh(&chan_list_lock);
- sk_for_each(sk, node, &l2cap_sk_list.head) {
- struct l2cap_pinfo *pi = l2cap_pi(sk);
+ list_for_each_entry(c, &chan_list, global_l) {
+ struct sock *sk = c->sk;
seq_printf(f, "%s %s %d %d 0x%4.4x 0x%4.4x %d %d %d %d\n",
batostr(&bt_sk(sk)->src),
batostr(&bt_sk(sk)->dst),
- sk->sk_state, __le16_to_cpu(pi->psm),
- pi->scid, pi->dcid,
- pi->imtu, pi->omtu, pi->sec_level,
- pi->mode);
+ sk->sk_state, __le16_to_cpu(c->psm),
+ c->scid, c->dcid, c->imtu, c->omtu,
+ c->sec_level, c->mode);
}
- read_unlock_bh(&l2cap_sk_list.lock);
+ read_unlock_bh(&chan_list_lock);
return 0;
}
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index 299fe56a966..18dc9888d8c 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -30,6 +30,8 @@
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/l2cap.h>
+static const struct proto_ops l2cap_sock_ops;
+
/* ---- L2CAP timers ---- */
static void l2cap_sock_timeout(unsigned long arg)
{
@@ -51,7 +53,7 @@ static void l2cap_sock_timeout(unsigned long arg)
if (sk->sk_state == BT_CONNECTED || sk->sk_state == BT_CONFIG)
reason = ECONNREFUSED;
else if (sk->sk_state == BT_CONNECT &&
- l2cap_pi(sk)->sec_level != BT_SECURITY_SDP)
+ l2cap_pi(sk)->chan->sec_level != BT_SECURITY_SDP)
reason = ECONNREFUSED;
else
reason = ETIMEDOUT;
@@ -76,21 +78,10 @@ void l2cap_sock_clear_timer(struct sock *sk)
sk_stop_timer(sk, &sk->sk_timer);
}
-static struct sock *__l2cap_get_sock_by_addr(__le16 psm, bdaddr_t *src)
-{
- struct sock *sk;
- struct hlist_node *node;
- sk_for_each(sk, node, &l2cap_sk_list.head)
- if (l2cap_pi(sk)->sport == psm && !bacmp(&bt_sk(sk)->src, src))
- goto found;
- sk = NULL;
-found:
- return sk;
-}
-
static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
{
struct sock *sk = sock->sk;
+ struct l2cap_chan *chan = l2cap_pi(sk)->chan;
struct sockaddr_l2 la;
int len, err = 0;
@@ -129,26 +120,20 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
}
}
- write_lock_bh(&l2cap_sk_list.lock);
+ if (la.l2_cid)
+ err = l2cap_add_scid(chan, la.l2_cid);
+ else
+ err = l2cap_add_psm(chan, &la.l2_bdaddr, la.l2_psm);
- if (la.l2_psm && __l2cap_get_sock_by_addr(la.l2_psm, &la.l2_bdaddr)) {
- err = -EADDRINUSE;
- } else {
- /* Save source address */
- bacpy(&bt_sk(sk)->src, &la.l2_bdaddr);
- l2cap_pi(sk)->psm = la.l2_psm;
- l2cap_pi(sk)->sport = la.l2_psm;
- sk->sk_state = BT_BOUND;
-
- if (__le16_to_cpu(la.l2_psm) == 0x0001 ||
- __le16_to_cpu(la.l2_psm) == 0x0003)
- l2cap_pi(sk)->sec_level = BT_SECURITY_SDP;
- }
+ if (err < 0)
+ goto done;
- if (la.l2_cid)
- l2cap_pi(sk)->scid = la.l2_cid;
+ if (__le16_to_cpu(la.l2_psm) == 0x0001 ||
+ __le16_to_cpu(la.l2_psm) == 0x0003)
+ chan->sec_level = BT_SECURITY_SDP;
- write_unlock_bh(&l2cap_sk_list.lock);
+ bacpy(&bt_sk(sk)->src, &la.l2_bdaddr);
+ sk->sk_state = BT_BOUND;
done:
release_sock(sk);
@@ -158,6 +143,7 @@ done:
static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags)
{
struct sock *sk = sock->sk;
+ struct l2cap_chan *chan = l2cap_pi(sk)->chan;
struct sockaddr_l2 la;
int len, err = 0;
@@ -182,7 +168,7 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al
goto done;
}
- switch (l2cap_pi(sk)->mode) {
+ switch (chan->mode) {
case L2CAP_MODE_BASIC:
break;
case L2CAP_MODE_ERTM:
@@ -226,10 +212,10 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al
/* Set destination address and psm */
bacpy(&bt_sk(sk)->dst, &la.l2_bdaddr);
- l2cap_pi(sk)->psm = la.l2_psm;
- l2cap_pi(sk)->dcid = la.l2_cid;
+ chan->psm = la.l2_psm;
+ chan->dcid = la.l2_cid;
- err = l2cap_do_connect(sk);
+ err = l2cap_chan_connect(l2cap_pi(sk)->chan);
if (err)
goto done;
@@ -244,6 +230,7 @@ done:
static int l2cap_sock_listen(struct socket *sock, int backlog)
{
struct sock *sk = sock->sk;
+ struct l2cap_chan *chan = l2cap_pi(sk)->chan;
int err = 0;
BT_DBG("sk %p backlog %d", sk, backlog);
@@ -256,7 +243,7 @@ static int l2cap_sock_listen(struct socket *sock, int backlog)
goto done;
}
- switch (l2cap_pi(sk)->mode) {
+ switch (chan->mode) {
case L2CAP_MODE_BASIC:
break;
case L2CAP_MODE_ERTM:
@@ -269,28 +256,6 @@ static int l2cap_sock_listen(struct socket *sock, int backlog)
goto done;
}
- if (!l2cap_pi(sk)->psm && !l2cap_pi(sk)->dcid) {
- bdaddr_t *src = &bt_sk(sk)->src;
- u16 psm;
-
- err = -EINVAL;
-
- write_lock_bh(&l2cap_sk_list.lock);
-
- for (psm = 0x1001; psm < 0x1100; psm += 2)
- if (!__l2cap_get_sock_by_addr(cpu_to_le16(psm), src)) {
- l2cap_pi(sk)->psm = cpu_to_le16(psm);
- l2cap_pi(sk)->sport = cpu_to_le16(psm);
- err = 0;
- break;
- }
-
- write_unlock_bh(&l2cap_sk_list.lock);
-
- if (err < 0)
- goto done;
- }
-
sk->sk_max_ack_backlog = backlog;
sk->sk_ack_backlog = 0;
sk->sk_state = BT_LISTEN;
@@ -360,6 +325,7 @@ static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *l
{
struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr;
struct sock *sk = sock->sk;
+ struct l2cap_chan *chan = l2cap_pi(sk)->chan;
BT_DBG("sock %p, sk %p", sock, sk);
@@ -367,13 +333,13 @@ static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *l
*len = sizeof(struct sockaddr_l2);
if (peer) {
- la->l2_psm = l2cap_pi(sk)->psm;
+ la->l2_psm = chan->psm;
bacpy(&la->l2_bdaddr, &bt_sk(sk)->dst);
- la->l2_cid = cpu_to_le16(l2cap_pi(sk)->dcid);
+ la->l2_cid = cpu_to_le16(chan->dcid);
} else {
- la->l2_psm = l2cap_pi(sk)->sport;
+ la->l2_psm = chan->sport;
bacpy(&la->l2_bdaddr, &bt_sk(sk)->src);
- la->l2_cid = cpu_to_le16(l2cap_pi(sk)->scid);
+ la->l2_cid = cpu_to_le16(chan->scid);
}
return 0;
@@ -382,6 +348,7 @@ static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *l
static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __user *optval, int __user *optlen)
{
struct sock *sk = sock->sk;
+ struct l2cap_chan *chan = l2cap_pi(sk)->chan;
struct l2cap_options opts;
struct l2cap_conninfo cinfo;
int len, err = 0;
@@ -397,13 +364,13 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __us
switch (optname) {
case L2CAP_OPTIONS:
memset(&opts, 0, sizeof(opts));
- opts.imtu = l2cap_pi(sk)->imtu;
- opts.omtu = l2cap_pi(sk)->omtu;
- opts.flush_to = l2cap_pi(sk)->flush_to;
- opts.mode = l2cap_pi(sk)->mode;
- opts.fcs = l2cap_pi(sk)->fcs;
- opts.max_tx = l2cap_pi(sk)->max_tx;
- opts.txwin_size = (__u16)l2cap_pi(sk)->tx_win;
+ opts.imtu = chan->imtu;
+ opts.omtu = chan->omtu;
+ opts.flush_to = chan->flush_to;
+ opts.mode = chan->mode;
+ opts.fcs = chan->fcs;
+ opts.max_tx = chan->max_tx;
+ opts.txwin_size = (__u16)chan->tx_win;
len = min_t(unsigned int, len, sizeof(opts));
if (copy_to_user(optval, (char *) &opts, len))
@@ -412,7 +379,7 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __us
break;
case L2CAP_LM:
- switch (l2cap_pi(sk)->sec_level) {
+ switch (chan->sec_level) {
case BT_SECURITY_LOW:
opt = L2CAP_LM_AUTH;
break;
@@ -428,10 +395,10 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __us
break;
}
- if (l2cap_pi(sk)->role_switch)
+ if (chan->role_switch)
opt |= L2CAP_LM_MASTER;
- if (l2cap_pi(sk)->force_reliable)
+ if (chan->force_reliable)
opt |= L2CAP_LM_RELIABLE;
if (put_user(opt, (u32 __user *) optval))
@@ -446,8 +413,8 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __us
break;
}
- cinfo.hci_handle = l2cap_pi(sk)->conn->hcon->handle;
- memcpy(cinfo.dev_class, l2cap_pi(sk)->conn->hcon->dev_class, 3);
+ cinfo.hci_handle = chan->conn->hcon->handle;
+ memcpy(cinfo.dev_class, chan->conn->hcon->dev_class, 3);
len = min_t(unsigned int, len, sizeof(cinfo));
if (copy_to_user(optval, (char *) &cinfo, len))
@@ -467,6 +434,7 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __us
static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen)
{
struct sock *sk = sock->sk;
+ struct l2cap_chan *chan = l2cap_pi(sk)->chan;
struct bt_security sec;
int len, err = 0;
@@ -491,7 +459,7 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch
break;
}
- sec.level = l2cap_pi(sk)->sec_level;
+ sec.level = chan->sec_level;
len = min_t(unsigned int, len, sizeof(sec));
if (copy_to_user(optval, (char *) &sec, len))
@@ -511,7 +479,7 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch
break;
case BT_FLUSHABLE:
- if (put_user(l2cap_pi(sk)->flushable, (u32 __user *) optval))
+ if (put_user(chan->flushable, (u32 __user *) optval))
err = -EFAULT;
break;
@@ -528,6 +496,7 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch
static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __user *optval, unsigned int optlen)
{
struct sock *sk = sock->sk;
+ struct l2cap_chan *chan = l2cap_pi(sk)->chan;
struct l2cap_options opts;
int len, err = 0;
u32 opt;
@@ -543,13 +512,13 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us
break;
}
- opts.imtu = l2cap_pi(sk)->imtu;
- opts.omtu = l2cap_pi(sk)->omtu;
- opts.flush_to = l2cap_pi(sk)->flush_to;
- opts.mode = l2cap_pi(sk)->mode;
- opts.fcs = l2cap_pi(sk)->fcs;
- opts.max_tx = l2cap_pi(sk)->max_tx;
- opts.txwin_size = (__u16)l2cap_pi(sk)->tx_win;
+ opts.imtu = chan->imtu;
+ opts.omtu = chan->omtu;
+ opts.flush_to = chan->flush_to;
+ opts.mode = chan->mode;
+ opts.fcs = chan->fcs;
+ opts.max_tx = chan->max_tx;
+ opts.txwin_size = (__u16)chan->tx_win;
len = min_t(unsigned int, sizeof(opts), optlen);
if (copy_from_user((char *) &opts, optval, len)) {
@@ -562,10 +531,10 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us
break;
}
- l2cap_pi(sk)->mode = opts.mode;
- switch (l2cap_pi(sk)->mode) {
+ chan->mode = opts.mode;
+ switch (chan->mode) {
case L2CAP_MODE_BASIC:
- l2cap_pi(sk)->conf_state &= ~L2CAP_CONF_STATE2_DEVICE;
+ chan->conf_state &= ~L2CAP_CONF_STATE2_DEVICE;
break;
case L2CAP_MODE_ERTM:
case L2CAP_MODE_STREAMING:
@@ -577,11 +546,11 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us
break;
}
- l2cap_pi(sk)->imtu = opts.imtu;
- l2cap_pi(sk)->omtu = opts.omtu;
- l2cap_pi(sk)->fcs = opts.fcs;
- l2cap_pi(sk)->max_tx = opts.max_tx;
- l2cap_pi(sk)->tx_win = (__u8)opts.txwin_size;
+ chan->imtu = opts.imtu;
+ chan->omtu = opts.omtu;
+ chan->fcs = opts.fcs;
+ chan->max_tx = opts.max_tx;
+ chan->tx_win = (__u8)opts.txwin_size;
break;
case L2CAP_LM:
@@ -591,14 +560,14 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us
}
if (opt & L2CAP_LM_AUTH)
- l2cap_pi(sk)->sec_level = BT_SECURITY_LOW;
+ chan->sec_level = BT_SECURITY_LOW;
if (opt & L2CAP_LM_ENCRYPT)
- l2cap_pi(sk)->sec_level = BT_SECURITY_MEDIUM;
+ chan->sec_level = BT_SECURITY_MEDIUM;
if (opt & L2CAP_LM_SECURE)
- l2cap_pi(sk)->sec_level = BT_SECURITY_HIGH;
+ chan->sec_level = BT_SECURITY_HIGH;
- l2cap_pi(sk)->role_switch = (opt & L2CAP_LM_MASTER);
- l2cap_pi(sk)->force_reliable = (opt & L2CAP_LM_RELIABLE);
+ chan->role_switch = (opt & L2CAP_LM_MASTER);
+ chan->force_reliable = (opt & L2CAP_LM_RELIABLE);
break;
default:
@@ -613,6 +582,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us
static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen)
{
struct sock *sk = sock->sk;
+ struct l2cap_chan *chan = l2cap_pi(sk)->chan;
struct bt_security sec;
int len, err = 0;
u32 opt;
@@ -649,7 +619,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
break;
}
- l2cap_pi(sk)->sec_level = sec.level;
+ chan->sec_level = sec.level;
break;
case BT_DEFER_SETUP:
@@ -678,7 +648,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
}
if (opt == BT_FLUSHABLE_OFF) {
- struct l2cap_conn *conn = l2cap_pi(sk)->conn;
+ struct l2cap_conn *conn = chan->conn;
/* proceed further only when we have l2cap_conn and
No Flush support in the LM */
if (!conn || !lmp_no_flush_capable(conn->hcon->hdev)) {
@@ -687,7 +657,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
}
}
- l2cap_pi(sk)->flushable = opt;
+ chan->flushable = opt;
break;
default:
@@ -702,7 +672,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len)
{
struct sock *sk = sock->sk;
- struct l2cap_pinfo *pi = l2cap_pi(sk);
+ struct l2cap_chan *chan = l2cap_pi(sk)->chan;
struct sk_buff *skb;
u16 control;
int err;
@@ -725,74 +695,77 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct ms
/* Connectionless channel */
if (sk->sk_type == SOCK_DGRAM) {
- skb = l2cap_create_connless_pdu(sk, msg, len);
+ skb = l2cap_create_connless_pdu(chan, msg, len);
if (IS_ERR(skb)) {
err = PTR_ERR(skb);
} else {
- l2cap_do_send(sk, skb);
+ l2cap_do_send(chan, skb);
err = len;
}
goto done;
}
- switch (pi->mode) {
+ switch (chan->mode) {
case L2CAP_MODE_BASIC:
/* Check outgoing MTU */
- if (len > pi->omtu) {
+ if (len > chan->omtu) {
err = -EMSGSIZE;
goto done;
}
/* Create a basic PDU */
- skb = l2cap_create_basic_pdu(sk, msg, len);
+ skb = l2cap_create_basic_pdu(chan, msg, len);
if (IS_ERR(skb)) {
err = PTR_ERR(skb);
goto done;
}
- l2cap_do_send(sk, skb);
+ l2cap_do_send(chan, skb);
err = len;
break;
case L2CAP_MODE_ERTM:
case L2CAP_MODE_STREAMING:
/* Entire SDU fits into one PDU */
- if (len <= pi->remote_mps) {
+ if (len <= chan->remote_mps) {
control = L2CAP_SDU_UNSEGMENTED;
- skb = l2cap_create_iframe_pdu(sk, msg, len, control, 0);
+ skb = l2cap_create_iframe_pdu(chan, msg, len, control,
+ 0);
if (IS_ERR(skb)) {
err = PTR_ERR(skb);
goto done;
}
- __skb_queue_tail(TX_QUEUE(sk), skb);
+ __skb_queue_tail(&chan->tx_q, skb);
- if (sk->sk_send_head == NULL)
- sk->sk_send_head = skb;
+ if (chan->tx_send_head == NULL)
+ chan->tx_send_head = skb;
} else {
/* Segment SDU into multiples PDUs */
- err = l2cap_sar_segment_sdu(sk, msg, len);
+ err = l2cap_sar_segment_sdu(chan, msg, len);
if (err < 0)
goto done;
}
- if (pi->mode == L2CAP_MODE_STREAMING) {
- l2cap_streaming_send(sk);
- } else {
- if ((pi->conn_state & L2CAP_CONN_REMOTE_BUSY) &&
- (pi->conn_state & L2CAP_CONN_WAIT_F)) {
- err = len;
- break;
- }
- err = l2cap_ertm_send(sk);
+ if (chan->mode == L2CAP_MODE_STREAMING) {
+ l2cap_streaming_send(chan);
+ err = len;
+ break;
+ }
+
+ if ((chan->conn_state & L2CAP_CONN_REMOTE_BUSY) &&
+ (chan->conn_state & L2CAP_CONN_WAIT_F)) {
+ err = len;
+ break;
}
+ err = l2cap_ertm_send(chan);
if (err >= 0)
err = len;
break;
default:
- BT_DBG("bad state %1.1x", pi->mode);
+ BT_DBG("bad state %1.1x", chan->mode);
err = -EBADFD;
}
@@ -808,29 +781,9 @@ static int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct ms
lock_sock(sk);
if (sk->sk_state == BT_CONNECT2 && bt_sk(sk)->defer_setup) {
- struct l2cap_conn_rsp rsp;
- struct l2cap_conn *conn = l2cap_pi(sk)->conn;
- u8 buf[128];
-
sk->sk_state = BT_CONFIG;
- rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid);
- rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid);
- rsp.result = cpu_to_le16(L2CAP_CR_SUCCESS);
- rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
- l2cap_send_cmd(l2cap_pi(sk)->conn, l2cap_pi(sk)->ident,
- L2CAP_CONN_RSP, sizeof(rsp), &rsp);
-
- if (l2cap_pi(sk)->conf_state & L2CAP_CONF_REQ_SENT) {
- release_sock(sk);
- return 0;
- }
-
- l2cap_pi(sk)->conf_state |= L2CAP_CONF_REQ_SENT;
- l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
- l2cap_build_conf_req(sk, buf), buf);
- l2cap_pi(sk)->num_conf_req++;
-
+ __l2cap_connect_rsp_defer(l2cap_pi(sk)->chan);
release_sock(sk);
return 0;
}
@@ -854,7 +807,8 @@ void l2cap_sock_kill(struct sock *sk)
BT_DBG("sk %p state %d", sk, sk->sk_state);
/* Kill poor orphan */
- bt_sock_unlink(&l2cap_sk_list, sk);
+
+ l2cap_chan_destroy(l2cap_pi(sk)->chan);
sock_set_flag(sk, SOCK_DEAD);
sock_put(sk);
}
@@ -885,7 +839,8 @@ static void l2cap_sock_cleanup_listen(struct sock *parent)
void __l2cap_sock_close(struct sock *sk, int reason)
{
- struct l2cap_conn *conn = l2cap_pi(sk)->conn;
+ struct l2cap_chan *chan = l2cap_pi(sk)->chan;
+ struct l2cap_conn *conn = chan->conn;
BT_DBG("sk %p state %d socket %p", sk, sk->sk_state, sk->sk_socket);
@@ -900,9 +855,9 @@ void __l2cap_sock_close(struct sock *sk, int reason)
sk->sk_type == SOCK_STREAM) &&
conn->hcon->type == ACL_LINK) {
l2cap_sock_set_timer(sk, sk->sk_sndtimeo);
- l2cap_send_disconn_req(conn, sk, reason);
+ l2cap_send_disconn_req(conn, chan, reason);
} else
- l2cap_chan_del(sk, reason);
+ l2cap_chan_del(chan, reason);
break;
case BT_CONNECT2:
@@ -917,20 +872,20 @@ void __l2cap_sock_close(struct sock *sk, int reason)
else
result = L2CAP_CR_BAD_PSM;
- rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid);
- rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid);
+ rsp.scid = cpu_to_le16(chan->dcid);
+ rsp.dcid = cpu_to_le16(chan->scid);
rsp.result = cpu_to_le16(result);
rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
- l2cap_send_cmd(conn, l2cap_pi(sk)->ident,
- L2CAP_CONN_RSP, sizeof(rsp), &rsp);
+ l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP,
+ sizeof(rsp), &rsp);
}
- l2cap_chan_del(sk, reason);
+ l2cap_chan_del(chan, reason);
break;
case BT_CONNECT:
case BT_DISCONN:
- l2cap_chan_del(sk, reason);
+ l2cap_chan_del(chan, reason);
break;
default:
@@ -942,6 +897,7 @@ void __l2cap_sock_close(struct sock *sk, int reason)
static int l2cap_sock_shutdown(struct socket *sock, int how)
{
struct sock *sk = sock->sk;
+ struct l2cap_chan *chan = l2cap_pi(sk)->chan;
int err = 0;
BT_DBG("sock %p, sk %p", sock, sk);
@@ -951,7 +907,7 @@ static int l2cap_sock_shutdown(struct socket *sock, int how)
lock_sock(sk);
if (!sk->sk_shutdown) {
- if (l2cap_pi(sk)->mode == L2CAP_MODE_ERTM)
+ if (chan->mode == L2CAP_MODE_ERTM)
err = __l2cap_wait_ack(sk);
sk->sk_shutdown = SHUTDOWN_MASK;
@@ -998,49 +954,47 @@ static void l2cap_sock_destruct(struct sock *sk)
void l2cap_sock_init(struct sock *sk, struct sock *parent)
{
struct l2cap_pinfo *pi = l2cap_pi(sk);
+ struct l2cap_chan *chan = pi->chan;
BT_DBG("sk %p", sk);
if (parent) {
+ struct l2cap_chan *pchan = l2cap_pi(parent)->chan;
+
sk->sk_type = parent->sk_type;
bt_sk(sk)->defer_setup = bt_sk(parent)->defer_setup;
- pi->imtu = l2cap_pi(parent)->imtu;
- pi->omtu = l2cap_pi(parent)->omtu;
- pi->conf_state = l2cap_pi(parent)->conf_state;
- pi->mode = l2cap_pi(parent)->mode;
- pi->fcs = l2cap_pi(parent)->fcs;
- pi->max_tx = l2cap_pi(parent)->max_tx;
- pi->tx_win = l2cap_pi(parent)->tx_win;
- pi->sec_level = l2cap_pi(parent)->sec_level;
- pi->role_switch = l2cap_pi(parent)->role_switch;
- pi->force_reliable = l2cap_pi(parent)->force_reliable;
- pi->flushable = l2cap_pi(parent)->flushable;
+ chan->imtu = pchan->imtu;
+ chan->omtu = pchan->omtu;
+ chan->conf_state = pchan->conf_state;
+ chan->mode = pchan->mode;
+ chan->fcs = pchan->fcs;
+ chan->max_tx = pchan->max_tx;
+ chan->tx_win = pchan->tx_win;
+ chan->sec_level = pchan->sec_level;
+ chan->role_switch = pchan->role_switch;
+ chan->force_reliable = pchan->force_reliable;
+ chan->flushable = pchan->flushable;
} else {
- pi->imtu = L2CAP_DEFAULT_MTU;
- pi->omtu = 0;
+ chan->imtu = L2CAP_DEFAULT_MTU;
+ chan->omtu = 0;
if (!disable_ertm && sk->sk_type == SOCK_STREAM) {
- pi->mode = L2CAP_MODE_ERTM;
- pi->conf_state |= L2CAP_CONF_STATE2_DEVICE;
+ chan->mode = L2CAP_MODE_ERTM;
+ chan->conf_state |= L2CAP_CONF_STATE2_DEVICE;
} else {
- pi->mode = L2CAP_MODE_BASIC;
+ chan->mode = L2CAP_MODE_BASIC;
}
- pi->max_tx = L2CAP_DEFAULT_MAX_TX;
- pi->fcs = L2CAP_FCS_CRC16;
- pi->tx_win = L2CAP_DEFAULT_TX_WINDOW;
- pi->sec_level = BT_SECURITY_LOW;
- pi->role_switch = 0;
- pi->force_reliable = 0;
- pi->flushable = BT_FLUSHABLE_OFF;
+ chan->max_tx = L2CAP_DEFAULT_MAX_TX;
+ chan->fcs = L2CAP_FCS_CRC16;
+ chan->tx_win = L2CAP_DEFAULT_TX_WINDOW;
+ chan->sec_level = BT_SECURITY_LOW;
+ chan->role_switch = 0;
+ chan->force_reliable = 0;
+ chan->flushable = BT_FLUSHABLE_OFF;
}
/* Default config options */
- pi->conf_len = 0;
- pi->flush_to = L2CAP_DEFAULT_FLUSH_TO;
- skb_queue_head_init(TX_QUEUE(sk));
- skb_queue_head_init(SREJ_QUEUE(sk));
- skb_queue_head_init(BUSY_QUEUE(sk));
- INIT_LIST_HEAD(SREJ_LIST(sk));
+ chan->flush_to = L2CAP_DEFAULT_FLUSH_TO;
}
static struct proto l2cap_proto = {
@@ -1070,7 +1024,6 @@ struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, g
setup_timer(&sk->sk_timer, l2cap_sock_timeout, (unsigned long) sk);
- bt_sock_link(&l2cap_sk_list, sk);
return sk;
}
@@ -1078,6 +1031,7 @@ static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol,
int kern)
{
struct sock *sk;
+ struct l2cap_chan *chan;
BT_DBG("sock %p", sock);
@@ -1096,11 +1050,19 @@ static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol,
if (!sk)
return -ENOMEM;
+ chan = l2cap_chan_create(sk);
+ if (!chan) {
+ l2cap_sock_kill(sk);
+ return -ENOMEM;
+ }
+
+ l2cap_pi(sk)->chan = chan;
+
l2cap_sock_init(sk, NULL);
return 0;
}
-const struct proto_ops l2cap_sock_ops = {
+static const struct proto_ops l2cap_sock_ops = {
.family = PF_BLUETOOTH,
.owner = THIS_MODULE,
.release = l2cap_sock_release,
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 4476d8e3c0f..dae382ce702 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -36,7 +36,7 @@ struct pending_cmd {
struct list_head list;
__u16 opcode;
int index;
- void *cmd;
+ void *param;
struct sock *sk;
void *user_data;
};
@@ -179,10 +179,12 @@ static int read_controller_info(struct sock *sk, u16 index)
hci_del_off_timer(hdev);
- hci_dev_lock_bh(hdev);
+ hci_dev_lock(hdev);
set_bit(HCI_MGMT, &hdev->flags);
+ memset(&rp, 0, sizeof(rp));
+
rp.type = hdev->dev_type;
rp.powered = test_bit(HCI_UP, &hdev->flags);
@@ -204,7 +206,9 @@ static int read_controller_info(struct sock *sk, u16 index)
rp.hci_ver = hdev->hci_ver;
put_unaligned_le16(hdev->hci_rev, &rp.hci_rev);
- hci_dev_unlock_bh(hdev);
+ memcpy(rp.name, hdev->dev_name, sizeof(hdev->dev_name));
+
+ hci_dev_unlock(hdev);
hci_dev_put(hdev);
return cmd_complete(sk, index, MGMT_OP_READ_INFO, &rp, sizeof(rp));
@@ -213,7 +217,7 @@ static int read_controller_info(struct sock *sk, u16 index)
static void mgmt_pending_free(struct pending_cmd *cmd)
{
sock_put(cmd->sk);
- kfree(cmd->cmd);
+ kfree(cmd->param);
kfree(cmd);
}
@@ -229,13 +233,14 @@ static struct pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode,
cmd->opcode = opcode;
cmd->index = index;
- cmd->cmd = kmalloc(len, GFP_ATOMIC);
- if (!cmd->cmd) {
+ cmd->param = kmalloc(len, GFP_ATOMIC);
+ if (!cmd->param) {
kfree(cmd);
return NULL;
}
- memcpy(cmd->cmd, data, len);
+ if (data)
+ memcpy(cmd->param, data, len);
cmd->sk = sk;
sock_hold(sk);
@@ -311,7 +316,7 @@ static int set_powered(struct sock *sk, u16 index, unsigned char *data, u16 len)
if (!hdev)
return cmd_status(sk, index, MGMT_OP_SET_POWERED, ENODEV);
- hci_dev_lock_bh(hdev);
+ hci_dev_lock(hdev);
up = test_bit(HCI_UP, &hdev->flags);
if ((cp->val && up) || (!cp->val && !up)) {
@@ -338,7 +343,7 @@ static int set_powered(struct sock *sk, u16 index, unsigned char *data, u16 len)
err = 0;
failed:
- hci_dev_unlock_bh(hdev);
+ hci_dev_unlock(hdev);
hci_dev_put(hdev);
return err;
}
@@ -363,7 +368,7 @@ static int set_discoverable(struct sock *sk, u16 index, unsigned char *data,
if (!hdev)
return cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE, ENODEV);
- hci_dev_lock_bh(hdev);
+ hci_dev_lock(hdev);
if (!test_bit(HCI_UP, &hdev->flags)) {
err = cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE, ENETDOWN);
@@ -398,7 +403,7 @@ static int set_discoverable(struct sock *sk, u16 index, unsigned char *data,
mgmt_pending_remove(cmd);
failed:
- hci_dev_unlock_bh(hdev);
+ hci_dev_unlock(hdev);
hci_dev_put(hdev);
return err;
@@ -424,7 +429,7 @@ static int set_connectable(struct sock *sk, u16 index, unsigned char *data,
if (!hdev)
return cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE, ENODEV);
- hci_dev_lock_bh(hdev);
+ hci_dev_lock(hdev);
if (!test_bit(HCI_UP, &hdev->flags)) {
err = cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE, ENETDOWN);
@@ -458,7 +463,7 @@ static int set_connectable(struct sock *sk, u16 index, unsigned char *data,
mgmt_pending_remove(cmd);
failed:
- hci_dev_unlock_bh(hdev);
+ hci_dev_unlock(hdev);
hci_dev_put(hdev);
return err;
@@ -517,7 +522,7 @@ static int set_pairable(struct sock *sk, u16 index, unsigned char *data,
if (!hdev)
return cmd_status(sk, index, MGMT_OP_SET_PAIRABLE, ENODEV);
- hci_dev_lock_bh(hdev);
+ hci_dev_lock(hdev);
if (cp->val)
set_bit(HCI_PAIRABLE, &hdev->flags);
@@ -533,12 +538,156 @@ static int set_pairable(struct sock *sk, u16 index, unsigned char *data,
err = mgmt_event(MGMT_EV_PAIRABLE, index, &ev, sizeof(ev), sk);
failed:
- hci_dev_unlock_bh(hdev);
+ hci_dev_unlock(hdev);
hci_dev_put(hdev);
return err;
}
+#define EIR_FLAGS 0x01 /* flags */
+#define EIR_UUID16_SOME 0x02 /* 16-bit UUID, more available */
+#define EIR_UUID16_ALL 0x03 /* 16-bit UUID, all listed */
+#define EIR_UUID32_SOME 0x04 /* 32-bit UUID, more available */
+#define EIR_UUID32_ALL 0x05 /* 32-bit UUID, all listed */
+#define EIR_UUID128_SOME 0x06 /* 128-bit UUID, more available */
+#define EIR_UUID128_ALL 0x07 /* 128-bit UUID, all listed */
+#define EIR_NAME_SHORT 0x08 /* shortened local name */
+#define EIR_NAME_COMPLETE 0x09 /* complete local name */
+#define EIR_TX_POWER 0x0A /* transmit power level */
+#define EIR_DEVICE_ID 0x10 /* device ID */
+
+#define PNP_INFO_SVCLASS_ID 0x1200
+
+static u8 bluetooth_base_uuid[] = {
+ 0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00, 0x00, 0x80,
+ 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static u16 get_uuid16(u8 *uuid128)
+{
+ u32 val;
+ int i;
+
+ for (i = 0; i < 12; i++) {
+ if (bluetooth_base_uuid[i] != uuid128[i])
+ return 0;
+ }
+
+ memcpy(&val, &uuid128[12], 4);
+
+ val = le32_to_cpu(val);
+ if (val > 0xffff)
+ return 0;
+
+ return (u16) val;
+}
+
+static void create_eir(struct hci_dev *hdev, u8 *data)
+{
+ u8 *ptr = data;
+ u16 eir_len = 0;
+ u16 uuid16_list[HCI_MAX_EIR_LENGTH / sizeof(u16)];
+ int i, truncated = 0;
+ struct list_head *p;
+ size_t name_len;
+
+ name_len = strlen(hdev->dev_name);
+
+ if (name_len > 0) {
+ /* EIR Data type */
+ if (name_len > 48) {
+ name_len = 48;
+ ptr[1] = EIR_NAME_SHORT;
+ } else
+ ptr[1] = EIR_NAME_COMPLETE;
+
+ /* EIR Data length */
+ ptr[0] = name_len + 1;
+
+ memcpy(ptr + 2, hdev->dev_name, name_len);
+
+ eir_len += (name_len + 2);
+ ptr += (name_len + 2);
+ }
+
+ memset(uuid16_list, 0, sizeof(uuid16_list));
+
+ /* Group all UUID16 types */
+ list_for_each(p, &hdev->uuids) {
+ struct bt_uuid *uuid = list_entry(p, struct bt_uuid, list);
+ u16 uuid16;
+
+ uuid16 = get_uuid16(uuid->uuid);
+ if (uuid16 == 0)
+ return;
+
+ if (uuid16 < 0x1100)
+ continue;
+
+ if (uuid16 == PNP_INFO_SVCLASS_ID)
+ continue;
+
+ /* Stop if not enough space to put next UUID */
+ if (eir_len + 2 + sizeof(u16) > HCI_MAX_EIR_LENGTH) {
+ truncated = 1;
+ break;
+ }
+
+ /* Check for duplicates */
+ for (i = 0; uuid16_list[i] != 0; i++)
+ if (uuid16_list[i] == uuid16)
+ break;
+
+ if (uuid16_list[i] == 0) {
+ uuid16_list[i] = uuid16;
+ eir_len += sizeof(u16);
+ }
+ }
+
+ if (uuid16_list[0] != 0) {
+ u8 *length = ptr;
+
+ /* EIR Data type */
+ ptr[1] = truncated ? EIR_UUID16_SOME : EIR_UUID16_ALL;
+
+ ptr += 2;
+ eir_len += 2;
+
+ for (i = 0; uuid16_list[i] != 0; i++) {
+ *ptr++ = (uuid16_list[i] & 0x00ff);
+ *ptr++ = (uuid16_list[i] & 0xff00) >> 8;
+ }
+
+ /* EIR Data length */
+ *length = (i * sizeof(u16)) + 1;
+ }
+}
+
+static int update_eir(struct hci_dev *hdev)
+{
+ struct hci_cp_write_eir cp;
+
+ if (!(hdev->features[6] & LMP_EXT_INQ))
+ return 0;
+
+ if (hdev->ssp_mode == 0)
+ return 0;
+
+ if (test_bit(HCI_SERVICE_CACHE, &hdev->flags))
+ return 0;
+
+ memset(&cp, 0, sizeof(cp));
+
+ create_eir(hdev, cp.data);
+
+ if (memcmp(cp.data, hdev->eir, sizeof(cp.data)) == 0)
+ return 0;
+
+ memcpy(hdev->eir, cp.data, sizeof(cp.data));
+
+ return hci_send_cmd(hdev, HCI_OP_WRITE_EIR, sizeof(cp), &cp);
+}
+
static u8 get_service_classes(struct hci_dev *hdev)
{
struct list_head *p;
@@ -590,7 +739,7 @@ static int add_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len)
if (!hdev)
return cmd_status(sk, index, MGMT_OP_ADD_UUID, ENODEV);
- hci_dev_lock_bh(hdev);
+ hci_dev_lock(hdev);
uuid = kmalloc(sizeof(*uuid), GFP_ATOMIC);
if (!uuid) {
@@ -607,10 +756,14 @@ static int add_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len)
if (err < 0)
goto failed;
+ err = update_eir(hdev);
+ if (err < 0)
+ goto failed;
+
err = cmd_complete(sk, index, MGMT_OP_ADD_UUID, NULL, 0);
failed:
- hci_dev_unlock_bh(hdev);
+ hci_dev_unlock(hdev);
hci_dev_put(hdev);
return err;
@@ -635,7 +788,7 @@ static int remove_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len)
if (!hdev)
return cmd_status(sk, index, MGMT_OP_REMOVE_UUID, ENODEV);
- hci_dev_lock_bh(hdev);
+ hci_dev_lock(hdev);
if (memcmp(cp->uuid, bt_uuid_any, 16) == 0) {
err = hci_uuids_clear(hdev);
@@ -663,10 +816,14 @@ static int remove_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len)
if (err < 0)
goto unlock;
+ err = update_eir(hdev);
+ if (err < 0)
+ goto unlock;
+
err = cmd_complete(sk, index, MGMT_OP_REMOVE_UUID, NULL, 0);
unlock:
- hci_dev_unlock_bh(hdev);
+ hci_dev_unlock(hdev);
hci_dev_put(hdev);
return err;
@@ -690,7 +847,7 @@ static int set_dev_class(struct sock *sk, u16 index, unsigned char *data,
if (!hdev)
return cmd_status(sk, index, MGMT_OP_SET_DEV_CLASS, ENODEV);
- hci_dev_lock_bh(hdev);
+ hci_dev_lock(hdev);
hdev->major_class = cp->major;
hdev->minor_class = cp->minor;
@@ -700,7 +857,7 @@ static int set_dev_class(struct sock *sk, u16 index, unsigned char *data,
if (err == 0)
err = cmd_complete(sk, index, MGMT_OP_SET_DEV_CLASS, NULL, 0);
- hci_dev_unlock_bh(hdev);
+ hci_dev_unlock(hdev);
hci_dev_put(hdev);
return err;
@@ -722,7 +879,7 @@ static int set_service_cache(struct sock *sk, u16 index, unsigned char *data,
if (!hdev)
return cmd_status(sk, index, MGMT_OP_SET_SERVICE_CACHE, ENODEV);
- hci_dev_lock_bh(hdev);
+ hci_dev_lock(hdev);
BT_DBG("hci%u enable %d", index, cp->enable);
@@ -732,13 +889,15 @@ static int set_service_cache(struct sock *sk, u16 index, unsigned char *data,
} else {
clear_bit(HCI_SERVICE_CACHE, &hdev->flags);
err = update_class(hdev);
+ if (err == 0)
+ err = update_eir(hdev);
}
if (err == 0)
err = cmd_complete(sk, index, MGMT_OP_SET_SERVICE_CACHE, NULL,
0);
- hci_dev_unlock_bh(hdev);
+ hci_dev_unlock(hdev);
hci_dev_put(hdev);
return err;
@@ -772,7 +931,7 @@ static int load_keys(struct sock *sk, u16 index, unsigned char *data, u16 len)
BT_DBG("hci%u debug_keys %u key_count %u", index, cp->debug_keys,
key_count);
- hci_dev_lock_bh(hdev);
+ hci_dev_lock(hdev);
hci_link_keys_clear(hdev);
@@ -786,11 +945,11 @@ static int load_keys(struct sock *sk, u16 index, unsigned char *data, u16 len)
for (i = 0; i < key_count; i++) {
struct mgmt_key_info *key = &cp->keys[i];
- hci_add_link_key(hdev, 0, &key->bdaddr, key->val, key->type,
+ hci_add_link_key(hdev, NULL, 0, &key->bdaddr, key->val, key->type,
key->pin_len);
}
- hci_dev_unlock_bh(hdev);
+ hci_dev_unlock(hdev);
hci_dev_put(hdev);
return 0;
@@ -812,7 +971,7 @@ static int remove_key(struct sock *sk, u16 index, unsigned char *data, u16 len)
if (!hdev)
return cmd_status(sk, index, MGMT_OP_REMOVE_KEY, ENODEV);
- hci_dev_lock_bh(hdev);
+ hci_dev_lock(hdev);
err = hci_remove_link_key(hdev, &cp->bdaddr);
if (err < 0) {
@@ -835,7 +994,7 @@ static int remove_key(struct sock *sk, u16 index, unsigned char *data, u16 len)
}
unlock:
- hci_dev_unlock_bh(hdev);
+ hci_dev_unlock(hdev);
hci_dev_put(hdev);
return err;
@@ -861,7 +1020,7 @@ static int disconnect(struct sock *sk, u16 index, unsigned char *data, u16 len)
if (!hdev)
return cmd_status(sk, index, MGMT_OP_DISCONNECT, ENODEV);
- hci_dev_lock_bh(hdev);
+ hci_dev_lock(hdev);
if (!test_bit(HCI_UP, &hdev->flags)) {
err = cmd_status(sk, index, MGMT_OP_DISCONNECT, ENETDOWN);
@@ -874,6 +1033,9 @@ static int disconnect(struct sock *sk, u16 index, unsigned char *data, u16 len)
}
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr);
+ if (!conn)
+ conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->bdaddr);
+
if (!conn) {
err = cmd_status(sk, index, MGMT_OP_DISCONNECT, ENOTCONN);
goto failed;
@@ -893,7 +1055,7 @@ static int disconnect(struct sock *sk, u16 index, unsigned char *data, u16 len)
mgmt_pending_remove(cmd);
failed:
- hci_dev_unlock_bh(hdev);
+ hci_dev_unlock(hdev);
hci_dev_put(hdev);
return err;
@@ -914,7 +1076,7 @@ static int get_connections(struct sock *sk, u16 index)
if (!hdev)
return cmd_status(sk, index, MGMT_OP_GET_CONNECTIONS, ENODEV);
- hci_dev_lock_bh(hdev);
+ hci_dev_lock(hdev);
count = 0;
list_for_each(p, &hdev->conn_hash.list) {
@@ -945,7 +1107,7 @@ static int get_connections(struct sock *sk, u16 index)
unlock:
kfree(rp);
- hci_dev_unlock_bh(hdev);
+ hci_dev_unlock(hdev);
hci_dev_put(hdev);
return err;
}
@@ -970,7 +1132,7 @@ static int pin_code_reply(struct sock *sk, u16 index, unsigned char *data,
if (!hdev)
return cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY, ENODEV);
- hci_dev_lock_bh(hdev);
+ hci_dev_lock(hdev);
if (!test_bit(HCI_UP, &hdev->flags)) {
err = cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY, ENETDOWN);
@@ -992,7 +1154,7 @@ static int pin_code_reply(struct sock *sk, u16 index, unsigned char *data,
mgmt_pending_remove(cmd);
failed:
- hci_dev_unlock_bh(hdev);
+ hci_dev_unlock(hdev);
hci_dev_put(hdev);
return err;
@@ -1019,7 +1181,7 @@ static int pin_code_neg_reply(struct sock *sk, u16 index, unsigned char *data,
return cmd_status(sk, index, MGMT_OP_PIN_CODE_NEG_REPLY,
ENODEV);
- hci_dev_lock_bh(hdev);
+ hci_dev_lock(hdev);
if (!test_bit(HCI_UP, &hdev->flags)) {
err = cmd_status(sk, index, MGMT_OP_PIN_CODE_NEG_REPLY,
@@ -1040,7 +1202,7 @@ static int pin_code_neg_reply(struct sock *sk, u16 index, unsigned char *data,
mgmt_pending_remove(cmd);
failed:
- hci_dev_unlock_bh(hdev);
+ hci_dev_unlock(hdev);
hci_dev_put(hdev);
return err;
@@ -1063,14 +1225,14 @@ static int set_io_capability(struct sock *sk, u16 index, unsigned char *data,
if (!hdev)
return cmd_status(sk, index, MGMT_OP_SET_IO_CAPABILITY, ENODEV);
- hci_dev_lock_bh(hdev);
+ hci_dev_lock(hdev);
hdev->io_capability = cp->io_capability;
BT_DBG("%s IO capability set to 0x%02x", hdev->name,
hdev->io_capability);
- hci_dev_unlock_bh(hdev);
+ hci_dev_unlock(hdev);
hci_dev_put(hdev);
return cmd_complete(sk, index, MGMT_OP_SET_IO_CAPABILITY, NULL, 0);
@@ -1156,7 +1318,7 @@ static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len)
if (!hdev)
return cmd_status(sk, index, MGMT_OP_PAIR_DEVICE, ENODEV);
- hci_dev_lock_bh(hdev);
+ hci_dev_lock(hdev);
if (cp->io_cap == 0x03) {
sec_level = BT_SECURITY_MEDIUM;
@@ -1198,7 +1360,7 @@ static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len)
err = 0;
unlock:
- hci_dev_unlock_bh(hdev);
+ hci_dev_unlock(hdev);
hci_dev_put(hdev);
return err;
@@ -1230,7 +1392,7 @@ static int user_confirm_reply(struct sock *sk, u16 index, unsigned char *data,
if (!hdev)
return cmd_status(sk, index, mgmt_op, ENODEV);
- hci_dev_lock_bh(hdev);
+ hci_dev_lock(hdev);
if (!test_bit(HCI_UP, &hdev->flags)) {
err = cmd_status(sk, index, mgmt_op, ENETDOWN);
@@ -1248,6 +1410,231 @@ static int user_confirm_reply(struct sock *sk, u16 index, unsigned char *data,
mgmt_pending_remove(cmd);
failed:
+ hci_dev_unlock(hdev);
+ hci_dev_put(hdev);
+
+ return err;
+}
+
+static int set_local_name(struct sock *sk, u16 index, unsigned char *data,
+ u16 len)
+{
+ struct mgmt_cp_set_local_name *mgmt_cp = (void *) data;
+ struct hci_cp_write_local_name hci_cp;
+ struct hci_dev *hdev;
+ struct pending_cmd *cmd;
+ int err;
+
+ BT_DBG("");
+
+ if (len != sizeof(*mgmt_cp))
+ return cmd_status(sk, index, MGMT_OP_SET_LOCAL_NAME, EINVAL);
+
+ hdev = hci_dev_get(index);
+ if (!hdev)
+ return cmd_status(sk, index, MGMT_OP_SET_LOCAL_NAME, ENODEV);
+
+ hci_dev_lock(hdev);
+
+ cmd = mgmt_pending_add(sk, MGMT_OP_SET_LOCAL_NAME, index, data, len);
+ if (!cmd) {
+ err = -ENOMEM;
+ goto failed;
+ }
+
+ memcpy(hci_cp.name, mgmt_cp->name, sizeof(hci_cp.name));
+ err = hci_send_cmd(hdev, HCI_OP_WRITE_LOCAL_NAME, sizeof(hci_cp),
+ &hci_cp);
+ if (err < 0)
+ mgmt_pending_remove(cmd);
+
+failed:
+ hci_dev_unlock(hdev);
+ hci_dev_put(hdev);
+
+ return err;
+}
+
+static int read_local_oob_data(struct sock *sk, u16 index)
+{
+ struct hci_dev *hdev;
+ struct pending_cmd *cmd;
+ int err;
+
+ BT_DBG("hci%u", index);
+
+ hdev = hci_dev_get(index);
+ if (!hdev)
+ return cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
+ ENODEV);
+
+ hci_dev_lock(hdev);
+
+ if (!test_bit(HCI_UP, &hdev->flags)) {
+ err = cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
+ ENETDOWN);
+ goto unlock;
+ }
+
+ if (!(hdev->features[6] & LMP_SIMPLE_PAIR)) {
+ err = cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
+ EOPNOTSUPP);
+ goto unlock;
+ }
+
+ if (mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, index)) {
+ err = cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA, EBUSY);
+ goto unlock;
+ }
+
+ cmd = mgmt_pending_add(sk, MGMT_OP_READ_LOCAL_OOB_DATA, index, NULL, 0);
+ if (!cmd) {
+ err = -ENOMEM;
+ goto unlock;
+ }
+
+ err = hci_send_cmd(hdev, HCI_OP_READ_LOCAL_OOB_DATA, 0, NULL);
+ if (err < 0)
+ mgmt_pending_remove(cmd);
+
+unlock:
+ hci_dev_unlock(hdev);
+ hci_dev_put(hdev);
+
+ return err;
+}
+
+static int add_remote_oob_data(struct sock *sk, u16 index, unsigned char *data,
+ u16 len)
+{
+ struct hci_dev *hdev;
+ struct mgmt_cp_add_remote_oob_data *cp = (void *) data;
+ int err;
+
+ BT_DBG("hci%u ", index);
+
+ if (len != sizeof(*cp))
+ return cmd_status(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA,
+ EINVAL);
+
+ hdev = hci_dev_get(index);
+ if (!hdev)
+ return cmd_status(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA,
+ ENODEV);
+
+ hci_dev_lock(hdev);
+
+ err = hci_add_remote_oob_data(hdev, &cp->bdaddr, cp->hash,
+ cp->randomizer);
+ if (err < 0)
+ err = cmd_status(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA, -err);
+ else
+ err = cmd_complete(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA, NULL,
+ 0);
+
+ hci_dev_unlock(hdev);
+ hci_dev_put(hdev);
+
+ return err;
+}
+
+static int remove_remote_oob_data(struct sock *sk, u16 index,
+ unsigned char *data, u16 len)
+{
+ struct hci_dev *hdev;
+ struct mgmt_cp_remove_remote_oob_data *cp = (void *) data;
+ int err;
+
+ BT_DBG("hci%u ", index);
+
+ if (len != sizeof(*cp))
+ return cmd_status(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
+ EINVAL);
+
+ hdev = hci_dev_get(index);
+ if (!hdev)
+ return cmd_status(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
+ ENODEV);
+
+ hci_dev_lock(hdev);
+
+ err = hci_remove_remote_oob_data(hdev, &cp->bdaddr);
+ if (err < 0)
+ err = cmd_status(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
+ -err);
+ else
+ err = cmd_complete(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
+ NULL, 0);
+
+ hci_dev_unlock(hdev);
+ hci_dev_put(hdev);
+
+ return err;
+}
+
+static int start_discovery(struct sock *sk, u16 index)
+{
+ u8 lap[3] = { 0x33, 0x8b, 0x9e };
+ struct hci_cp_inquiry cp;
+ struct pending_cmd *cmd;
+ struct hci_dev *hdev;
+ int err;
+
+ BT_DBG("hci%u", index);
+
+ hdev = hci_dev_get(index);
+ if (!hdev)
+ return cmd_status(sk, index, MGMT_OP_START_DISCOVERY, ENODEV);
+
+ hci_dev_lock_bh(hdev);
+
+ cmd = mgmt_pending_add(sk, MGMT_OP_START_DISCOVERY, index, NULL, 0);
+ if (!cmd) {
+ err = -ENOMEM;
+ goto failed;
+ }
+
+ memset(&cp, 0, sizeof(cp));
+ memcpy(&cp.lap, lap, 3);
+ cp.length = 0x08;
+ cp.num_rsp = 0x00;
+
+ err = hci_send_cmd(hdev, HCI_OP_INQUIRY, sizeof(cp), &cp);
+ if (err < 0)
+ mgmt_pending_remove(cmd);
+
+failed:
+ hci_dev_unlock_bh(hdev);
+ hci_dev_put(hdev);
+
+ return err;
+}
+
+static int stop_discovery(struct sock *sk, u16 index)
+{
+ struct hci_dev *hdev;
+ struct pending_cmd *cmd;
+ int err;
+
+ BT_DBG("hci%u", index);
+
+ hdev = hci_dev_get(index);
+ if (!hdev)
+ return cmd_status(sk, index, MGMT_OP_STOP_DISCOVERY, ENODEV);
+
+ hci_dev_lock_bh(hdev);
+
+ cmd = mgmt_pending_add(sk, MGMT_OP_STOP_DISCOVERY, index, NULL, 0);
+ if (!cmd) {
+ err = -ENOMEM;
+ goto failed;
+ }
+
+ err = hci_send_cmd(hdev, HCI_OP_INQUIRY_CANCEL, 0, NULL);
+ if (err < 0)
+ mgmt_pending_remove(cmd);
+
+failed:
hci_dev_unlock_bh(hdev);
hci_dev_put(hdev);
@@ -1266,7 +1653,7 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
if (msglen < sizeof(*hdr))
return -EINVAL;
- buf = kmalloc(msglen, GFP_ATOMIC);
+ buf = kmalloc(msglen, GFP_KERNEL);
if (!buf)
return -ENOMEM;
@@ -1349,6 +1736,25 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
case MGMT_OP_USER_CONFIRM_NEG_REPLY:
err = user_confirm_reply(sk, index, buf + sizeof(*hdr), len, 0);
break;
+ case MGMT_OP_SET_LOCAL_NAME:
+ err = set_local_name(sk, index, buf + sizeof(*hdr), len);
+ break;
+ case MGMT_OP_READ_LOCAL_OOB_DATA:
+ err = read_local_oob_data(sk, index);
+ break;
+ case MGMT_OP_ADD_REMOTE_OOB_DATA:
+ err = add_remote_oob_data(sk, index, buf + sizeof(*hdr), len);
+ break;
+ case MGMT_OP_REMOVE_REMOTE_OOB_DATA:
+ err = remove_remote_oob_data(sk, index, buf + sizeof(*hdr),
+ len);
+ break;
+ case MGMT_OP_START_DISCOVERY:
+ err = start_discovery(sk, index);
+ break;
+ case MGMT_OP_STOP_DISCOVERY:
+ err = stop_discovery(sk, index);
+ break;
default:
BT_DBG("Unknown op %u", opcode);
err = cmd_status(sk, index, opcode, 0x01);
@@ -1382,7 +1788,7 @@ struct cmd_lookup {
static void mode_rsp(struct pending_cmd *cmd, void *data)
{
- struct mgmt_mode *cp = cmd->cmd;
+ struct mgmt_mode *cp = cmd->param;
struct cmd_lookup *match = data;
if (cp->val != match->val)
@@ -1455,17 +1861,17 @@ int mgmt_connectable(u16 index, u8 connectable)
return ret;
}
-int mgmt_new_key(u16 index, struct link_key *key, u8 old_key_type)
+int mgmt_new_key(u16 index, struct link_key *key, u8 persistent)
{
struct mgmt_ev_new_key ev;
memset(&ev, 0, sizeof(ev));
+ ev.store_hint = persistent;
bacpy(&ev.key.bdaddr, &key->bdaddr);
ev.key.type = key->type;
memcpy(ev.key.val, key->val, 16);
ev.key.pin_len = key->pin_len;
- ev.old_key_type = old_key_type;
return mgmt_event(MGMT_EV_NEW_KEY, index, &ev, sizeof(ev), NULL);
}
@@ -1481,7 +1887,7 @@ int mgmt_connected(u16 index, bdaddr_t *bdaddr)
static void disconnect_rsp(struct pending_cmd *cmd, void *data)
{
- struct mgmt_cp_disconnect *cp = cmd->cmd;
+ struct mgmt_cp_disconnect *cp = cmd->param;
struct sock **sk = data;
struct mgmt_rp_disconnect rp;
@@ -1539,11 +1945,12 @@ int mgmt_connect_failed(u16 index, bdaddr_t *bdaddr, u8 status)
return mgmt_event(MGMT_EV_CONNECT_FAILED, index, &ev, sizeof(ev), NULL);
}
-int mgmt_pin_code_request(u16 index, bdaddr_t *bdaddr)
+int mgmt_pin_code_request(u16 index, bdaddr_t *bdaddr, u8 secure)
{
struct mgmt_ev_pin_code_request ev;
bacpy(&ev.bdaddr, bdaddr);
+ ev.secure = secure;
return mgmt_event(MGMT_EV_PIN_CODE_REQUEST, index, &ev, sizeof(ev),
NULL);
@@ -1591,13 +1998,15 @@ int mgmt_pin_code_neg_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status)
return err;
}
-int mgmt_user_confirm_request(u16 index, bdaddr_t *bdaddr, __le32 value)
+int mgmt_user_confirm_request(u16 index, bdaddr_t *bdaddr, __le32 value,
+ u8 confirm_hint)
{
struct mgmt_ev_user_confirm_request ev;
BT_DBG("hci%u", index);
bacpy(&ev.bdaddr, bdaddr);
+ ev.confirm_hint = confirm_hint;
put_unaligned_le32(value, &ev.value);
return mgmt_event(MGMT_EV_USER_CONFIRM_REQUEST, index, &ev, sizeof(ev),
@@ -1645,3 +2054,110 @@ int mgmt_auth_failed(u16 index, bdaddr_t *bdaddr, u8 status)
return mgmt_event(MGMT_EV_AUTH_FAILED, index, &ev, sizeof(ev), NULL);
}
+
+int mgmt_set_local_name_complete(u16 index, u8 *name, u8 status)
+{
+ struct pending_cmd *cmd;
+ struct hci_dev *hdev;
+ struct mgmt_cp_set_local_name ev;
+ int err;
+
+ memset(&ev, 0, sizeof(ev));
+ memcpy(ev.name, name, HCI_MAX_NAME_LENGTH);
+
+ cmd = mgmt_pending_find(MGMT_OP_SET_LOCAL_NAME, index);
+ if (!cmd)
+ goto send_event;
+
+ if (status) {
+ err = cmd_status(cmd->sk, index, MGMT_OP_SET_LOCAL_NAME, EIO);
+ goto failed;
+ }
+
+ hdev = hci_dev_get(index);
+ if (hdev) {
+ hci_dev_lock_bh(hdev);
+ update_eir(hdev);
+ hci_dev_unlock_bh(hdev);
+ hci_dev_put(hdev);
+ }
+
+ err = cmd_complete(cmd->sk, index, MGMT_OP_SET_LOCAL_NAME, &ev,
+ sizeof(ev));
+ if (err < 0)
+ goto failed;
+
+send_event:
+ err = mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, index, &ev, sizeof(ev),
+ cmd ? cmd->sk : NULL);
+
+failed:
+ if (cmd)
+ mgmt_pending_remove(cmd);
+ return err;
+}
+
+int mgmt_read_local_oob_data_reply_complete(u16 index, u8 *hash, u8 *randomizer,
+ u8 status)
+{
+ struct pending_cmd *cmd;
+ int err;
+
+ BT_DBG("hci%u status %u", index, status);
+
+ cmd = mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, index);
+ if (!cmd)
+ return -ENOENT;
+
+ if (status) {
+ err = cmd_status(cmd->sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
+ EIO);
+ } else {
+ struct mgmt_rp_read_local_oob_data rp;
+
+ memcpy(rp.hash, hash, sizeof(rp.hash));
+ memcpy(rp.randomizer, randomizer, sizeof(rp.randomizer));
+
+ err = cmd_complete(cmd->sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
+ &rp, sizeof(rp));
+ }
+
+ mgmt_pending_remove(cmd);
+
+ return err;
+}
+
+int mgmt_device_found(u16 index, bdaddr_t *bdaddr, u8 *dev_class, s8 rssi,
+ u8 *eir)
+{
+ struct mgmt_ev_device_found ev;
+
+ memset(&ev, 0, sizeof(ev));
+
+ bacpy(&ev.bdaddr, bdaddr);
+ memcpy(ev.dev_class, dev_class, sizeof(ev.dev_class));
+ ev.rssi = rssi;
+
+ if (eir)
+ memcpy(ev.eir, eir, sizeof(ev.eir));
+
+ return mgmt_event(MGMT_EV_DEVICE_FOUND, index, &ev, sizeof(ev), NULL);
+}
+
+int mgmt_remote_name(u16 index, bdaddr_t *bdaddr, u8 *name)
+{
+ struct mgmt_ev_remote_name ev;
+
+ memset(&ev, 0, sizeof(ev));
+
+ bacpy(&ev.bdaddr, bdaddr);
+ memcpy(ev.name, name, HCI_MAX_NAME_LENGTH);
+
+ return mgmt_event(MGMT_EV_REMOTE_NAME, index, &ev, sizeof(ev), NULL);
+}
+
+int mgmt_discovering(u16 index, u8 discovering)
+{
+ return mgmt_event(MGMT_EV_DISCOVERING, index, &discovering,
+ sizeof(discovering), NULL);
+}
diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c
index c9973932456..5759bb7054f 100644
--- a/net/bluetooth/rfcomm/core.c
+++ b/net/bluetooth/rfcomm/core.c
@@ -232,6 +232,8 @@ static int rfcomm_l2sock_create(struct socket **sock)
static inline int rfcomm_check_security(struct rfcomm_dlc *d)
{
struct sock *sk = d->session->sock->sk;
+ struct l2cap_conn *conn = l2cap_pi(sk)->chan->conn;
+
__u8 auth_type;
switch (d->sec_level) {
@@ -246,8 +248,7 @@ static inline int rfcomm_check_security(struct rfcomm_dlc *d)
break;
}
- return hci_conn_security(l2cap_pi(sk)->conn->hcon, d->sec_level,
- auth_type);
+ return hci_conn_security(conn->hcon, d->sec_level, auth_type);
}
static void rfcomm_session_timeout(unsigned long arg)
@@ -710,10 +711,10 @@ static struct rfcomm_session *rfcomm_session_create(bdaddr_t *src,
/* Set L2CAP options */
sk = sock->sk;
lock_sock(sk);
- l2cap_pi(sk)->imtu = l2cap_mtu;
- l2cap_pi(sk)->sec_level = sec_level;
+ l2cap_pi(sk)->chan->imtu = l2cap_mtu;
+ l2cap_pi(sk)->chan->sec_level = sec_level;
if (l2cap_ertm)
- l2cap_pi(sk)->mode = L2CAP_MODE_ERTM;
+ l2cap_pi(sk)->chan->mode = L2CAP_MODE_ERTM;
release_sock(sk);
s = rfcomm_session_add(sock, BT_BOUND);
@@ -1241,6 +1242,7 @@ static int rfcomm_recv_disc(struct rfcomm_session *s, u8 dlci)
void rfcomm_dlc_accept(struct rfcomm_dlc *d)
{
struct sock *sk = d->session->sock->sk;
+ struct l2cap_conn *conn = l2cap_pi(sk)->chan->conn;
BT_DBG("dlc %p", d);
@@ -1254,7 +1256,7 @@ void rfcomm_dlc_accept(struct rfcomm_dlc *d)
rfcomm_dlc_unlock(d);
if (d->role_switch)
- hci_conn_switch_role(l2cap_pi(sk)->conn->hcon, 0x00);
+ hci_conn_switch_role(conn->hcon, 0x00);
rfcomm_send_msc(d->session, 1, d->dlci, d->v24_sig);
}
@@ -1890,7 +1892,8 @@ static inline void rfcomm_accept_connection(struct rfcomm_session *s)
/* We should adjust MTU on incoming sessions.
* L2CAP MTU minus UIH header and FCS. */
- s->mtu = min(l2cap_pi(nsock->sk)->omtu, l2cap_pi(nsock->sk)->imtu) - 5;
+ s->mtu = min(l2cap_pi(nsock->sk)->chan->omtu,
+ l2cap_pi(nsock->sk)->chan->imtu) - 5;
rfcomm_schedule();
} else
@@ -1909,7 +1912,7 @@ static inline void rfcomm_check_connection(struct rfcomm_session *s)
/* We can adjust MTU on outgoing sessions.
* L2CAP MTU minus UIH header and FCS. */
- s->mtu = min(l2cap_pi(sk)->omtu, l2cap_pi(sk)->imtu) - 5;
+ s->mtu = min(l2cap_pi(sk)->chan->omtu, l2cap_pi(sk)->chan->imtu) - 5;
rfcomm_send_sabm(s, 0);
break;
@@ -1992,7 +1995,7 @@ static int rfcomm_add_listener(bdaddr_t *ba)
/* Set L2CAP options */
sk = sock->sk;
lock_sock(sk);
- l2cap_pi(sk)->imtu = l2cap_mtu;
+ l2cap_pi(sk)->chan->imtu = l2cap_mtu;
release_sock(sk);
/* Start listening on the socket */
@@ -2093,7 +2096,7 @@ static void rfcomm_security_cfm(struct hci_conn *conn, u8 status, u8 encrypt)
if (!test_and_clear_bit(RFCOMM_AUTH_PENDING, &d->flags))
continue;
- if (!status)
+ if (!status && hci_conn_check_secure(conn, d->sec_level))
set_bit(RFCOMM_AUTH_ACCEPT, &d->flags);
else
set_bit(RFCOMM_AUTH_REJECT, &d->flags);
diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c
index 66cc1f0c3df..386cfaffd4b 100644
--- a/net/bluetooth/rfcomm/sock.c
+++ b/net/bluetooth/rfcomm/sock.c
@@ -743,6 +743,7 @@ static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname, char __u
struct sock *sk = sock->sk;
struct sock *l2cap_sk;
struct rfcomm_conninfo cinfo;
+ struct l2cap_conn *conn = l2cap_pi(sk)->chan->conn;
int len, err = 0;
u32 opt;
@@ -787,8 +788,8 @@ static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname, char __u
l2cap_sk = rfcomm_pi(sk)->dlc->session->sock->sk;
- cinfo.hci_handle = l2cap_pi(l2cap_sk)->conn->hcon->handle;
- memcpy(cinfo.dev_class, l2cap_pi(l2cap_sk)->conn->hcon->dev_class, 3);
+ cinfo.hci_handle = conn->hcon->handle;
+ memcpy(cinfo.dev_class, conn->hcon->dev_class, 3);
len = min_t(unsigned int, len, sizeof(cinfo));
if (copy_to_user(optval, (char *) &cinfo, len))
diff --git a/net/bridge/br.c b/net/bridge/br.c
index 84bbb82599b..f20c4fd915a 100644
--- a/net/bridge/br.c
+++ b/net/bridge/br.c
@@ -104,3 +104,4 @@ module_init(br_init)
module_exit(br_deinit)
MODULE_LICENSE("GPL");
MODULE_VERSION(BR_VERSION);
+MODULE_ALIAS_RTNL_LINK("bridge");
diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
index 21e5901186e..a6b2f86378c 100644
--- a/net/bridge/br_device.c
+++ b/net/bridge/br_device.c
@@ -74,13 +74,23 @@ out:
return NETDEV_TX_OK;
}
+static int br_dev_init(struct net_device *dev)
+{
+ struct net_bridge *br = netdev_priv(dev);
+
+ br->stats = alloc_percpu(struct br_cpu_netstats);
+ if (!br->stats)
+ return -ENOMEM;
+
+ return 0;
+}
+
static int br_dev_open(struct net_device *dev)
{
struct net_bridge *br = netdev_priv(dev);
netif_carrier_off(dev);
-
- br_features_recompute(br);
+ netdev_update_features(dev);
netif_start_queue(dev);
br_stp_enable_bridge(br);
br_multicast_open(br);
@@ -177,48 +187,11 @@ static void br_getinfo(struct net_device *dev, struct ethtool_drvinfo *info)
strcpy(info->bus_info, "N/A");
}
-static int br_set_sg(struct net_device *dev, u32 data)
-{
- struct net_bridge *br = netdev_priv(dev);
-
- if (data)
- br->feature_mask |= NETIF_F_SG;
- else
- br->feature_mask &= ~NETIF_F_SG;
-
- br_features_recompute(br);
- return 0;
-}
-
-static int br_set_tso(struct net_device *dev, u32 data)
-{
- struct net_bridge *br = netdev_priv(dev);
-
- if (data)
- br->feature_mask |= NETIF_F_TSO;
- else
- br->feature_mask &= ~NETIF_F_TSO;
-
- br_features_recompute(br);
- return 0;
-}
-
-static int br_set_tx_csum(struct net_device *dev, u32 data)
+static u32 br_fix_features(struct net_device *dev, u32 features)
{
struct net_bridge *br = netdev_priv(dev);
- if (data)
- br->feature_mask |= NETIF_F_NO_CSUM;
- else
- br->feature_mask &= ~NETIF_F_ALL_CSUM;
-
- br_features_recompute(br);
- return 0;
-}
-
-static int br_set_flags(struct net_device *netdev, u32 data)
-{
- return ethtool_op_set_flags(netdev, data, ETH_FLAG_TXVLAN);
+ return br_features_recompute(br, features);
}
#ifdef CONFIG_NET_POLL_CONTROLLER
@@ -319,21 +292,12 @@ static int br_del_slave(struct net_device *dev, struct net_device *slave_dev)
static const struct ethtool_ops br_ethtool_ops = {
.get_drvinfo = br_getinfo,
.get_link = ethtool_op_get_link,
- .get_tx_csum = ethtool_op_get_tx_csum,
- .set_tx_csum = br_set_tx_csum,
- .get_sg = ethtool_op_get_sg,
- .set_sg = br_set_sg,
- .get_tso = ethtool_op_get_tso,
- .set_tso = br_set_tso,
- .get_ufo = ethtool_op_get_ufo,
- .set_ufo = ethtool_op_set_ufo,
- .get_flags = ethtool_op_get_flags,
- .set_flags = br_set_flags,
};
static const struct net_device_ops br_netdev_ops = {
.ndo_open = br_dev_open,
.ndo_stop = br_dev_stop,
+ .ndo_init = br_dev_init,
.ndo_start_xmit = br_dev_xmit,
.ndo_get_stats64 = br_get_stats64,
.ndo_set_mac_address = br_set_mac_address,
@@ -347,6 +311,7 @@ static const struct net_device_ops br_netdev_ops = {
#endif
.ndo_add_slave = br_add_slave,
.ndo_del_slave = br_del_slave,
+ .ndo_fix_features = br_fix_features,
};
static void br_dev_free(struct net_device *dev)
@@ -357,18 +322,49 @@ static void br_dev_free(struct net_device *dev)
free_netdev(dev);
}
+static struct device_type br_type = {
+ .name = "bridge",
+};
+
void br_dev_setup(struct net_device *dev)
{
+ struct net_bridge *br = netdev_priv(dev);
+
random_ether_addr(dev->dev_addr);
ether_setup(dev);
dev->netdev_ops = &br_netdev_ops;
dev->destructor = br_dev_free;
SET_ETHTOOL_OPS(dev, &br_ethtool_ops);
+ SET_NETDEV_DEVTYPE(dev, &br_type);
dev->tx_queue_len = 0;
dev->priv_flags = IFF_EBRIDGE;
dev->features = NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_HIGHDMA |
NETIF_F_GSO_MASK | NETIF_F_NO_CSUM | NETIF_F_LLTX |
- NETIF_F_NETNS_LOCAL | NETIF_F_GSO | NETIF_F_HW_VLAN_TX;
+ NETIF_F_NETNS_LOCAL | NETIF_F_HW_VLAN_TX;
+ dev->hw_features = NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_HIGHDMA |
+ NETIF_F_GSO_MASK | NETIF_F_NO_CSUM |
+ NETIF_F_HW_VLAN_TX;
+
+ br->dev = dev;
+ spin_lock_init(&br->lock);
+ INIT_LIST_HEAD(&br->port_list);
+ spin_lock_init(&br->hash_lock);
+
+ br->bridge_id.prio[0] = 0x80;
+ br->bridge_id.prio[1] = 0x00;
+
+ memcpy(br->group_addr, br_group_address, ETH_ALEN);
+
+ br->stp_enabled = BR_NO_STP;
+ br->designated_root = br->bridge_id;
+ br->bridge_max_age = br->max_age = 20 * HZ;
+ br->bridge_hello_time = br->hello_time = 2 * HZ;
+ br->bridge_forward_delay = br->forward_delay = 15 * HZ;
+ br->ageing_time = 300 * HZ;
+
+ br_netfilter_rtable_init(br);
+ br_stp_timer_init(br);
+ br_multicast_init(br);
}
diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
index cc4d3c5ab1c..e0dfbc151dd 100644
--- a/net/bridge/br_fdb.c
+++ b/net/bridge/br_fdb.c
@@ -28,6 +28,7 @@
static struct kmem_cache *br_fdb_cache __read_mostly;
static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
const unsigned char *addr);
+static void fdb_notify(const struct net_bridge_fdb_entry *, int);
static u32 fdb_salt __read_mostly;
@@ -62,7 +63,7 @@ static inline int has_expired(const struct net_bridge *br,
const struct net_bridge_fdb_entry *fdb)
{
return !fdb->is_static &&
- time_before_eq(fdb->ageing_timer + hold_time(br), jiffies);
+ time_before_eq(fdb->updated + hold_time(br), jiffies);
}
static inline int br_mac_hash(const unsigned char *mac)
@@ -81,6 +82,7 @@ static void fdb_rcu_free(struct rcu_head *head)
static inline void fdb_delete(struct net_bridge_fdb_entry *f)
{
+ fdb_notify(f, RTM_DELNEIGH);
hlist_del_rcu(&f->hlist);
call_rcu(&f->rcu, fdb_rcu_free);
}
@@ -140,7 +142,7 @@ void br_fdb_cleanup(unsigned long _data)
unsigned long this_timer;
if (f->is_static)
continue;
- this_timer = f->ageing_timer + delay;
+ this_timer = f->updated + delay;
if (time_before_eq(this_timer, jiffies))
fdb_delete(f);
else if (time_before(this_timer, next_timer))
@@ -293,7 +295,7 @@ int br_fdb_fillbuf(struct net_bridge *br, void *buf,
fe->is_local = f->is_local;
if (!f->is_static)
- fe->ageing_timer_value = jiffies_to_clock_t(jiffies - f->ageing_timer);
+ fe->ageing_timer_value = jiffies_to_clock_t(jiffies - f->updated);
++fe;
++num;
}
@@ -305,8 +307,21 @@ int br_fdb_fillbuf(struct net_bridge *br, void *buf,
return num;
}
-static inline struct net_bridge_fdb_entry *fdb_find(struct hlist_head *head,
- const unsigned char *addr)
+static struct net_bridge_fdb_entry *fdb_find(struct hlist_head *head,
+ const unsigned char *addr)
+{
+ struct hlist_node *h;
+ struct net_bridge_fdb_entry *fdb;
+
+ hlist_for_each_entry(fdb, h, head, hlist) {
+ if (!compare_ether_addr(fdb->addr.addr, addr))
+ return fdb;
+ }
+ return NULL;
+}
+
+static struct net_bridge_fdb_entry *fdb_find_rcu(struct hlist_head *head,
+ const unsigned char *addr)
{
struct hlist_node *h;
struct net_bridge_fdb_entry *fdb;
@@ -320,8 +335,7 @@ static inline struct net_bridge_fdb_entry *fdb_find(struct hlist_head *head,
static struct net_bridge_fdb_entry *fdb_create(struct hlist_head *head,
struct net_bridge_port *source,
- const unsigned char *addr,
- int is_local)
+ const unsigned char *addr)
{
struct net_bridge_fdb_entry *fdb;
@@ -329,11 +343,11 @@ static struct net_bridge_fdb_entry *fdb_create(struct hlist_head *head,
if (fdb) {
memcpy(fdb->addr.addr, addr, ETH_ALEN);
fdb->dst = source;
- fdb->is_local = is_local;
- fdb->is_static = is_local;
- fdb->ageing_timer = jiffies;
-
+ fdb->is_local = 0;
+ fdb->is_static = 0;
+ fdb->updated = fdb->used = jiffies;
hlist_add_head_rcu(&fdb->hlist, head);
+ fdb_notify(fdb, RTM_NEWNEIGH);
}
return fdb;
}
@@ -360,12 +374,15 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
fdb_delete(fdb);
}
- if (!fdb_create(head, source, addr, 1))
+ fdb = fdb_create(head, source, addr);
+ if (!fdb)
return -ENOMEM;
+ fdb->is_local = fdb->is_static = 1;
return 0;
}
+/* Add entry for local address of interface */
int br_fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
const unsigned char *addr)
{
@@ -392,7 +409,7 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source,
source->state == BR_STATE_FORWARDING))
return;
- fdb = fdb_find(head, addr);
+ fdb = fdb_find_rcu(head, addr);
if (likely(fdb)) {
/* attempt to update an entry for a local interface */
if (unlikely(fdb->is_local)) {
@@ -403,15 +420,277 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source,
} else {
/* fastpath: update of existing entry */
fdb->dst = source;
- fdb->ageing_timer = jiffies;
+ fdb->updated = jiffies;
}
} else {
spin_lock(&br->hash_lock);
- if (!fdb_find(head, addr))
- fdb_create(head, source, addr, 0);
+ if (likely(!fdb_find(head, addr)))
+ fdb_create(head, source, addr);
+
/* else we lose race and someone else inserts
* it first, don't bother updating
*/
spin_unlock(&br->hash_lock);
}
}
+
+static int fdb_to_nud(const struct net_bridge_fdb_entry *fdb)
+{
+ if (fdb->is_local)
+ return NUD_PERMANENT;
+ else if (fdb->is_static)
+ return NUD_NOARP;
+ else if (has_expired(fdb->dst->br, fdb))
+ return NUD_STALE;
+ else
+ return NUD_REACHABLE;
+}
+
+static int fdb_fill_info(struct sk_buff *skb,
+ const struct net_bridge_fdb_entry *fdb,
+ u32 pid, u32 seq, int type, unsigned int flags)
+{
+ unsigned long now = jiffies;
+ struct nda_cacheinfo ci;
+ struct nlmsghdr *nlh;
+ struct ndmsg *ndm;
+
+ nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ndm), flags);
+ if (nlh == NULL)
+ return -EMSGSIZE;
+
+
+ ndm = nlmsg_data(nlh);
+ ndm->ndm_family = AF_BRIDGE;
+ ndm->ndm_pad1 = 0;
+ ndm->ndm_pad2 = 0;
+ ndm->ndm_flags = 0;
+ ndm->ndm_type = 0;
+ ndm->ndm_ifindex = fdb->dst->dev->ifindex;
+ ndm->ndm_state = fdb_to_nud(fdb);
+
+ NLA_PUT(skb, NDA_LLADDR, ETH_ALEN, &fdb->addr);
+
+ ci.ndm_used = jiffies_to_clock_t(now - fdb->used);
+ ci.ndm_confirmed = 0;
+ ci.ndm_updated = jiffies_to_clock_t(now - fdb->updated);
+ ci.ndm_refcnt = 0;
+ NLA_PUT(skb, NDA_CACHEINFO, sizeof(ci), &ci);
+
+ return nlmsg_end(skb, nlh);
+
+nla_put_failure:
+ nlmsg_cancel(skb, nlh);
+ return -EMSGSIZE;
+}
+
+static inline size_t fdb_nlmsg_size(void)
+{
+ return NLMSG_ALIGN(sizeof(struct ndmsg))
+ + nla_total_size(ETH_ALEN) /* NDA_LLADDR */
+ + nla_total_size(sizeof(struct nda_cacheinfo));
+}
+
+static void fdb_notify(const struct net_bridge_fdb_entry *fdb, int type)
+{
+ struct net *net = dev_net(fdb->dst->dev);
+ struct sk_buff *skb;
+ int err = -ENOBUFS;
+
+ skb = nlmsg_new(fdb_nlmsg_size(), GFP_ATOMIC);
+ if (skb == NULL)
+ goto errout;
+
+ err = fdb_fill_info(skb, fdb, 0, 0, type, 0);
+ if (err < 0) {
+ /* -EMSGSIZE implies BUG in fdb_nlmsg_size() */
+ WARN_ON(err == -EMSGSIZE);
+ kfree_skb(skb);
+ goto errout;
+ }
+ rtnl_notify(skb, net, 0, RTNLGRP_NEIGH, NULL, GFP_ATOMIC);
+ return;
+errout:
+ if (err < 0)
+ rtnl_set_sk_err(net, RTNLGRP_NEIGH, err);
+}
+
+/* Dump information about entries, in response to GETNEIGH */
+int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ struct net *net = sock_net(skb->sk);
+ struct net_device *dev;
+ int idx = 0;
+
+ rcu_read_lock();
+ for_each_netdev_rcu(net, dev) {
+ struct net_bridge *br = netdev_priv(dev);
+ int i;
+
+ if (!(dev->priv_flags & IFF_EBRIDGE))
+ continue;
+
+ for (i = 0; i < BR_HASH_SIZE; i++) {
+ struct hlist_node *h;
+ struct net_bridge_fdb_entry *f;
+
+ hlist_for_each_entry_rcu(f, h, &br->hash[i], hlist) {
+ if (idx < cb->args[0])
+ goto skip;
+
+ if (fdb_fill_info(skb, f,
+ NETLINK_CB(cb->skb).pid,
+ cb->nlh->nlmsg_seq,
+ RTM_NEWNEIGH,
+ NLM_F_MULTI) < 0)
+ break;
+skip:
+ ++idx;
+ }
+ }
+ }
+ rcu_read_unlock();
+
+ cb->args[0] = idx;
+
+ return skb->len;
+}
+
+/* Create new static fdb entry */
+static int fdb_add_entry(struct net_bridge_port *source, const __u8 *addr,
+ __u16 state)
+{
+ struct net_bridge *br = source->br;
+ struct hlist_head *head = &br->hash[br_mac_hash(addr)];
+ struct net_bridge_fdb_entry *fdb;
+
+ fdb = fdb_find(head, addr);
+ if (fdb)
+ return -EEXIST;
+
+ fdb = fdb_create(head, source, addr);
+ if (!fdb)
+ return -ENOMEM;
+
+ if (state & NUD_PERMANENT)
+ fdb->is_local = fdb->is_static = 1;
+ else if (state & NUD_NOARP)
+ fdb->is_static = 1;
+ return 0;
+}
+
+/* Add new permanent fdb entry with RTM_NEWNEIGH */
+int br_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+{
+ struct net *net = sock_net(skb->sk);
+ struct ndmsg *ndm;
+ struct nlattr *tb[NDA_MAX+1];
+ struct net_device *dev;
+ struct net_bridge_port *p;
+ const __u8 *addr;
+ int err;
+
+ ASSERT_RTNL();
+ err = nlmsg_parse(nlh, sizeof(*ndm), tb, NDA_MAX, NULL);
+ if (err < 0)
+ return err;
+
+ ndm = nlmsg_data(nlh);
+ if (ndm->ndm_ifindex == 0) {
+ pr_info("bridge: RTM_NEWNEIGH with invalid ifindex\n");
+ return -EINVAL;
+ }
+
+ dev = __dev_get_by_index(net, ndm->ndm_ifindex);
+ if (dev == NULL) {
+ pr_info("bridge: RTM_NEWNEIGH with unknown ifindex\n");
+ return -ENODEV;
+ }
+
+ if (!tb[NDA_LLADDR] || nla_len(tb[NDA_LLADDR]) != ETH_ALEN) {
+ pr_info("bridge: RTM_NEWNEIGH with invalid address\n");
+ return -EINVAL;
+ }
+
+ addr = nla_data(tb[NDA_LLADDR]);
+ if (!is_valid_ether_addr(addr)) {
+ pr_info("bridge: RTM_NEWNEIGH with invalid ether address\n");
+ return -EINVAL;
+ }
+
+ p = br_port_get_rtnl(dev);
+ if (p == NULL) {
+ pr_info("bridge: RTM_NEWNEIGH %s not a bridge port\n",
+ dev->name);
+ return -EINVAL;
+ }
+
+ spin_lock_bh(&p->br->hash_lock);
+ err = fdb_add_entry(p, addr, ndm->ndm_state);
+ spin_unlock_bh(&p->br->hash_lock);
+
+ return err;
+}
+
+static int fdb_delete_by_addr(struct net_bridge_port *p, const u8 *addr)
+{
+ struct net_bridge *br = p->br;
+ struct hlist_head *head = &br->hash[br_mac_hash(addr)];
+ struct net_bridge_fdb_entry *fdb;
+
+ fdb = fdb_find(head, addr);
+ if (!fdb)
+ return -ENOENT;
+
+ fdb_delete(fdb);
+ return 0;
+}
+
+/* Remove neighbor entry with RTM_DELNEIGH */
+int br_fdb_delete(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+{
+ struct net *net = sock_net(skb->sk);
+ struct ndmsg *ndm;
+ struct net_bridge_port *p;
+ struct nlattr *llattr;
+ const __u8 *addr;
+ struct net_device *dev;
+ int err;
+
+ ASSERT_RTNL();
+ if (nlmsg_len(nlh) < sizeof(*ndm))
+ return -EINVAL;
+
+ ndm = nlmsg_data(nlh);
+ if (ndm->ndm_ifindex == 0) {
+ pr_info("bridge: RTM_DELNEIGH with invalid ifindex\n");
+ return -EINVAL;
+ }
+
+ dev = __dev_get_by_index(net, ndm->ndm_ifindex);
+ if (dev == NULL) {
+ pr_info("bridge: RTM_DELNEIGH with unknown ifindex\n");
+ return -ENODEV;
+ }
+
+ llattr = nlmsg_find_attr(nlh, sizeof(*ndm), NDA_LLADDR);
+ if (llattr == NULL || nla_len(llattr) != ETH_ALEN) {
+ pr_info("bridge: RTM_DELNEIGH with invalid address\n");
+ return -EINVAL;
+ }
+
+ addr = nla_data(llattr);
+
+ p = br_port_get_rtnl(dev);
+ if (p == NULL) {
+ pr_info("bridge: RTM_DELNEIGH %s not a bridge port\n",
+ dev->name);
+ return -EINVAL;
+ }
+
+ spin_lock_bh(&p->br->hash_lock);
+ err = fdb_delete_by_addr(p, addr);
+ spin_unlock_bh(&p->br->hash_lock);
+
+ return err;
+}
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index 718b60366df..5dbdfdfc3a3 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -36,8 +36,8 @@ static int port_cost(struct net_device *dev)
if (dev->ethtool_ops && dev->ethtool_ops->get_settings) {
struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET, };
- if (!dev->ethtool_ops->get_settings(dev, &ecmd)) {
- switch(ecmd.speed) {
+ if (!dev_ethtool_get_settings(dev, &ecmd)) {
+ switch (ethtool_cmd_speed(&ecmd)) {
case SPEED_10000:
return 2;
case SPEED_1000:
@@ -175,56 +175,6 @@ static void del_br(struct net_bridge *br, struct list_head *head)
unregister_netdevice_queue(br->dev, head);
}
-static struct net_device *new_bridge_dev(struct net *net, const char *name)
-{
- struct net_bridge *br;
- struct net_device *dev;
-
- dev = alloc_netdev(sizeof(struct net_bridge), name,
- br_dev_setup);
-
- if (!dev)
- return NULL;
- dev_net_set(dev, net);
-
- br = netdev_priv(dev);
- br->dev = dev;
-
- br->stats = alloc_percpu(struct br_cpu_netstats);
- if (!br->stats) {
- free_netdev(dev);
- return NULL;
- }
-
- spin_lock_init(&br->lock);
- INIT_LIST_HEAD(&br->port_list);
- spin_lock_init(&br->hash_lock);
-
- br->bridge_id.prio[0] = 0x80;
- br->bridge_id.prio[1] = 0x00;
-
- memcpy(br->group_addr, br_group_address, ETH_ALEN);
-
- br->feature_mask = dev->features;
- br->stp_enabled = BR_NO_STP;
- br->designated_root = br->bridge_id;
- br->root_path_cost = 0;
- br->root_port = 0;
- br->bridge_max_age = br->max_age = 20 * HZ;
- br->bridge_hello_time = br->hello_time = 2 * HZ;
- br->bridge_forward_delay = br->forward_delay = 15 * HZ;
- br->topology_change = 0;
- br->topology_change_detected = 0;
- br->ageing_time = 300 * HZ;
-
- br_netfilter_rtable_init(br);
-
- br_stp_timer_init(br);
- br_multicast_init(br);
-
- return dev;
-}
-
/* find an available port number */
static int find_portno(struct net_bridge *br)
{
@@ -277,42 +227,19 @@ static struct net_bridge_port *new_nbp(struct net_bridge *br,
return p;
}
-static struct device_type br_type = {
- .name = "bridge",
-};
-
int br_add_bridge(struct net *net, const char *name)
{
struct net_device *dev;
- int ret;
- dev = new_bridge_dev(net, name);
+ dev = alloc_netdev(sizeof(struct net_bridge), name,
+ br_dev_setup);
+
if (!dev)
return -ENOMEM;
- rtnl_lock();
- if (strchr(dev->name, '%')) {
- ret = dev_alloc_name(dev, dev->name);
- if (ret < 0)
- goto out_free;
- }
-
- SET_NETDEV_DEVTYPE(dev, &br_type);
-
- ret = register_netdevice(dev);
- if (ret)
- goto out_free;
-
- ret = br_sysfs_addbr(dev);
- if (ret)
- unregister_netdevice(dev);
- out:
- rtnl_unlock();
- return ret;
+ dev_net_set(dev, net);
-out_free:
- free_netdev(dev);
- goto out;
+ return register_netdev(dev);
}
int br_del_bridge(struct net *net, const char *name)
@@ -364,15 +291,15 @@ int br_min_mtu(const struct net_bridge *br)
/*
* Recomputes features using slave's features
*/
-void br_features_recompute(struct net_bridge *br)
+u32 br_features_recompute(struct net_bridge *br, u32 features)
{
struct net_bridge_port *p;
- u32 features, mask;
+ u32 mask;
- features = mask = br->feature_mask;
if (list_empty(&br->port_list))
- goto done;
+ return features;
+ mask = features;
features &= ~NETIF_F_ONE_FOR_ALL;
list_for_each_entry(p, &br->port_list, list) {
@@ -380,8 +307,7 @@ void br_features_recompute(struct net_bridge *br)
p->dev->features, mask);
}
-done:
- br->dev->features = netdev_fix_features(br->dev, features);
+ return features;
}
/* called with RTNL */
@@ -446,9 +372,10 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
list_add_rcu(&p->list, &br->port_list);
+ netdev_update_features(br->dev);
+
spin_lock_bh(&br->lock);
changed_addr = br_stp_recalculate_bridge_id(br);
- br_features_recompute(br);
if ((dev->flags & IFF_UP) && netif_carrier_ok(dev) &&
(br->dev->flags & IFF_UP))
@@ -496,9 +423,10 @@ int br_del_if(struct net_bridge *br, struct net_device *dev)
spin_lock_bh(&br->lock);
br_stp_recalculate_bridge_id(br);
- br_features_recompute(br);
spin_unlock_bh(&br->lock);
+ netdev_update_features(br->dev);
+
return 0;
}
diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c
index 0c7badad62a..f3ac1e858ee 100644
--- a/net/bridge/br_input.c
+++ b/net/bridge/br_input.c
@@ -98,9 +98,10 @@ int br_handle_frame_finish(struct sk_buff *skb)
}
if (skb) {
- if (dst)
+ if (dst) {
+ dst->used = jiffies;
br_forward(dst->dst, skb, skb2);
- else
+ } else
br_flood_forward(br, skb, skb2);
}
diff --git a/net/bridge/br_ioctl.c b/net/bridge/br_ioctl.c
index 3d9fca0e337..7222fe1d546 100644
--- a/net/bridge/br_ioctl.c
+++ b/net/bridge/br_ioctl.c
@@ -181,40 +181,19 @@ static int old_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
if (!capable(CAP_NET_ADMIN))
return -EPERM;
- spin_lock_bh(&br->lock);
- br->bridge_forward_delay = clock_t_to_jiffies(args[1]);
- if (br_is_root_bridge(br))
- br->forward_delay = br->bridge_forward_delay;
- spin_unlock_bh(&br->lock);
- return 0;
+ return br_set_forward_delay(br, args[1]);
case BRCTL_SET_BRIDGE_HELLO_TIME:
- {
- unsigned long t = clock_t_to_jiffies(args[1]);
if (!capable(CAP_NET_ADMIN))
return -EPERM;
- if (t < HZ)
- return -EINVAL;
-
- spin_lock_bh(&br->lock);
- br->bridge_hello_time = t;
- if (br_is_root_bridge(br))
- br->hello_time = br->bridge_hello_time;
- spin_unlock_bh(&br->lock);
- return 0;
- }
+ return br_set_hello_time(br, args[1]);
case BRCTL_SET_BRIDGE_MAX_AGE:
if (!capable(CAP_NET_ADMIN))
return -EPERM;
- spin_lock_bh(&br->lock);
- br->bridge_max_age = clock_t_to_jiffies(args[1]);
- if (br_is_root_bridge(br))
- br->max_age = br->bridge_max_age;
- spin_unlock_bh(&br->lock);
- return 0;
+ return br_set_max_age(br, args[1]);
case BRCTL_SET_AGEING_TIME:
if (!capable(CAP_NET_ADMIN))
@@ -275,19 +254,16 @@ static int old_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
case BRCTL_SET_PORT_PRIORITY:
{
struct net_bridge_port *p;
- int ret = 0;
+ int ret;
if (!capable(CAP_NET_ADMIN))
return -EPERM;
- if (args[2] >= (1<<(16-BR_PORT_BITS)))
- return -ERANGE;
-
spin_lock_bh(&br->lock);
if ((p = br_get_port(br, args[1])) == NULL)
ret = -EINVAL;
else
- br_stp_set_port_priority(p, args[2]);
+ ret = br_stp_set_port_priority(p, args[2]);
spin_unlock_bh(&br->lock);
return ret;
}
@@ -295,15 +271,17 @@ static int old_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
case BRCTL_SET_PATH_COST:
{
struct net_bridge_port *p;
- int ret = 0;
+ int ret;
if (!capable(CAP_NET_ADMIN))
return -EPERM;
+ spin_lock_bh(&br->lock);
if ((p = br_get_port(br, args[1])) == NULL)
ret = -EINVAL;
else
- br_stp_set_path_cost(p, args[2]);
+ ret = br_stp_set_path_cost(p, args[2]);
+ spin_unlock_bh(&br->lock);
return ret;
}
diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
index 59660c909a7..2f14eafdeea 100644
--- a/net/bridge/br_multicast.c
+++ b/net/bridge/br_multicast.c
@@ -413,7 +413,7 @@ out:
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge *br,
- struct in6_addr *group)
+ const struct in6_addr *group)
{
struct sk_buff *skb;
struct ipv6hdr *ip6h;
@@ -1115,7 +1115,7 @@ static int br_ip4_multicast_query(struct net_bridge *br,
struct net_bridge_port *port,
struct sk_buff *skb)
{
- struct iphdr *iph = ip_hdr(skb);
+ const struct iphdr *iph = ip_hdr(skb);
struct igmphdr *ih = igmp_hdr(skb);
struct net_bridge_mdb_entry *mp;
struct igmpv3_query *ih3;
@@ -1190,7 +1190,7 @@ static int br_ip6_multicast_query(struct net_bridge *br,
struct net_bridge_port *port,
struct sk_buff *skb)
{
- struct ipv6hdr *ip6h = ipv6_hdr(skb);
+ const struct ipv6hdr *ip6h = ipv6_hdr(skb);
struct mld_msg *mld = (struct mld_msg *) icmp6_hdr(skb);
struct net_bridge_mdb_entry *mp;
struct mld2_query *mld2q;
@@ -1198,7 +1198,7 @@ static int br_ip6_multicast_query(struct net_bridge *br,
struct net_bridge_port_group __rcu **pp;
unsigned long max_delay;
unsigned long now = jiffies;
- struct in6_addr *group = NULL;
+ const struct in6_addr *group = NULL;
int err = 0;
spin_lock(&br->multicast_lock);
@@ -1356,7 +1356,7 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br,
struct sk_buff *skb)
{
struct sk_buff *skb2 = skb;
- struct iphdr *iph;
+ const struct iphdr *iph;
struct igmphdr *ih;
unsigned len;
unsigned offset;
@@ -1452,7 +1452,7 @@ static int br_multicast_ipv6_rcv(struct net_bridge *br,
struct sk_buff *skb)
{
struct sk_buff *skb2;
- struct ipv6hdr *ip6h;
+ const struct ipv6hdr *ip6h;
struct icmp6hdr *icmp6h;
u8 nexthdr;
unsigned len;
diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c
index 74ef4d4846a..e1f5ec75e91 100644
--- a/net/bridge/br_netfilter.c
+++ b/net/bridge/br_netfilter.c
@@ -219,7 +219,7 @@ static inline void nf_bridge_update_protocol(struct sk_buff *skb)
static int br_parse_ip_options(struct sk_buff *skb)
{
struct ip_options *opt;
- struct iphdr *iph;
+ const struct iphdr *iph;
struct net_device *dev = skb->dev;
u32 len;
@@ -554,7 +554,7 @@ static unsigned int br_nf_pre_routing_ipv6(unsigned int hook,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
- struct ipv6hdr *hdr;
+ const struct ipv6hdr *hdr;
u32 pkt_len;
if (skb->len < sizeof(struct ipv6hdr))
diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
index f8bf4c7f842..ffb0dc4cc0e 100644
--- a/net/bridge/br_netlink.c
+++ b/net/bridge/br_netlink.c
@@ -12,9 +12,11 @@
#include <linux/kernel.h>
#include <linux/slab.h>
+#include <linux/etherdevice.h>
#include <net/rtnetlink.h>
#include <net/net_namespace.h>
#include <net/sock.h>
+
#include "br_private.h"
static inline size_t br_nlmsg_size(void)
@@ -118,8 +120,9 @@ static int br_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
int idx;
idx = 0;
- for_each_netdev(net, dev) {
- struct net_bridge_port *port = br_port_get_rtnl(dev);
+ rcu_read_lock();
+ for_each_netdev_rcu(net, dev) {
+ struct net_bridge_port *port = br_port_get_rcu(dev);
/* not a bridge port */
if (!port || idx < cb->args[0])
@@ -133,7 +136,7 @@ static int br_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
skip:
++idx;
}
-
+ rcu_read_unlock();
cb->args[0] = idx;
return skb->len;
@@ -188,20 +191,61 @@ static int br_rtm_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
return 0;
}
+static int br_validate(struct nlattr *tb[], struct nlattr *data[])
+{
+ if (tb[IFLA_ADDRESS]) {
+ if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN)
+ return -EINVAL;
+ if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS])))
+ return -EADDRNOTAVAIL;
+ }
+
+ return 0;
+}
+
+static struct rtnl_link_ops br_link_ops __read_mostly = {
+ .kind = "bridge",
+ .priv_size = sizeof(struct net_bridge),
+ .setup = br_dev_setup,
+ .validate = br_validate,
+};
int __init br_netlink_init(void)
{
- if (__rtnl_register(PF_BRIDGE, RTM_GETLINK, NULL, br_dump_ifinfo))
- return -ENOBUFS;
+ int err;
- /* Only the first call to __rtnl_register can fail */
- __rtnl_register(PF_BRIDGE, RTM_SETLINK, br_rtm_setlink, NULL);
+ err = rtnl_link_register(&br_link_ops);
+ if (err < 0)
+ goto err1;
+
+ err = __rtnl_register(PF_BRIDGE, RTM_GETLINK, NULL, br_dump_ifinfo);
+ if (err)
+ goto err2;
+ err = __rtnl_register(PF_BRIDGE, RTM_SETLINK, br_rtm_setlink, NULL);
+ if (err)
+ goto err3;
+ err = __rtnl_register(PF_BRIDGE, RTM_NEWNEIGH, br_fdb_add, NULL);
+ if (err)
+ goto err3;
+ err = __rtnl_register(PF_BRIDGE, RTM_DELNEIGH, br_fdb_delete, NULL);
+ if (err)
+ goto err3;
+ err = __rtnl_register(PF_BRIDGE, RTM_GETNEIGH, NULL, br_fdb_dump);
+ if (err)
+ goto err3;
return 0;
+
+err3:
+ rtnl_unregister_all(PF_BRIDGE);
+err2:
+ rtnl_link_unregister(&br_link_ops);
+err1:
+ return err;
}
void __exit br_netlink_fini(void)
{
+ rtnl_link_unregister(&br_link_ops);
rtnl_unregister_all(PF_BRIDGE);
}
-
diff --git a/net/bridge/br_notify.c b/net/bridge/br_notify.c
index 7d337c9b608..6545ee9591d 100644
--- a/net/bridge/br_notify.c
+++ b/net/bridge/br_notify.c
@@ -36,6 +36,12 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v
struct net_bridge *br;
int err;
+ /* register of bridge completed, add sysfs entries */
+ if ((dev->priv_flags & IFF_EBRIDGE) && event == NETDEV_REGISTER) {
+ br_sysfs_addbr(dev);
+ return NOTIFY_DONE;
+ }
+
/* not a port of a bridge */
p = br_port_get_rtnl(dev);
if (!p)
@@ -60,10 +66,7 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v
break;
case NETDEV_FEAT_CHANGE:
- spin_lock_bh(&br->lock);
- if (netif_running(br->dev))
- br_features_recompute(br);
- spin_unlock_bh(&br->lock);
+ netdev_update_features(br->dev);
break;
case NETDEV_DOWN:
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 387013d3374..54578f274d8 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -64,7 +64,8 @@ struct net_bridge_fdb_entry
struct net_bridge_port *dst;
struct rcu_head rcu;
- unsigned long ageing_timer;
+ unsigned long updated;
+ unsigned long used;
mac_addr addr;
unsigned char is_local;
unsigned char is_static;
@@ -182,7 +183,6 @@ struct net_bridge
struct br_cpu_netstats __percpu *stats;
spinlock_t hash_lock;
struct hlist_head hash[BR_HASH_SIZE];
- u32 feature_mask;
#ifdef CONFIG_BRIDGE_NETFILTER
struct rtable fake_rtable;
bool nf_call_iptables;
@@ -353,6 +353,9 @@ extern int br_fdb_insert(struct net_bridge *br,
extern void br_fdb_update(struct net_bridge *br,
struct net_bridge_port *source,
const unsigned char *addr);
+extern int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb);
+extern int br_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg);
+extern int br_fdb_delete(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg);
/* br_forward.c */
extern void br_deliver(const struct net_bridge_port *to,
@@ -375,7 +378,7 @@ extern int br_add_if(struct net_bridge *br,
extern int br_del_if(struct net_bridge *br,
struct net_device *dev);
extern int br_min_mtu(const struct net_bridge *br);
-extern void br_features_recompute(struct net_bridge *br);
+extern u32 br_features_recompute(struct net_bridge *br, u32 features);
/* br_input.c */
extern int br_handle_frame_finish(struct sk_buff *skb);
@@ -491,6 +494,11 @@ extern struct net_bridge_port *br_get_port(struct net_bridge *br,
extern void br_init_port(struct net_bridge_port *p);
extern void br_become_designated_port(struct net_bridge_port *p);
+extern int br_set_forward_delay(struct net_bridge *br, unsigned long x);
+extern int br_set_hello_time(struct net_bridge *br, unsigned long x);
+extern int br_set_max_age(struct net_bridge *br, unsigned long x);
+
+
/* br_stp_if.c */
extern void br_stp_enable_bridge(struct net_bridge *br);
extern void br_stp_disable_bridge(struct net_bridge *br);
@@ -501,10 +509,10 @@ extern bool br_stp_recalculate_bridge_id(struct net_bridge *br);
extern void br_stp_change_bridge_id(struct net_bridge *br, const unsigned char *a);
extern void br_stp_set_bridge_priority(struct net_bridge *br,
u16 newprio);
-extern void br_stp_set_port_priority(struct net_bridge_port *p,
- u8 newprio);
-extern void br_stp_set_path_cost(struct net_bridge_port *p,
- u32 path_cost);
+extern int br_stp_set_port_priority(struct net_bridge_port *p,
+ unsigned long newprio);
+extern int br_stp_set_path_cost(struct net_bridge_port *p,
+ unsigned long path_cost);
extern ssize_t br_show_bridge_id(char *buf, const struct bridge_id *id);
/* br_stp_bpdu.c */
diff --git a/net/bridge/br_private_stp.h b/net/bridge/br_private_stp.h
index 8b650f7fbfa..642ef47a867 100644
--- a/net/bridge/br_private_stp.h
+++ b/net/bridge/br_private_stp.h
@@ -16,6 +16,19 @@
#define BPDU_TYPE_CONFIG 0
#define BPDU_TYPE_TCN 0x80
+/* IEEE 802.1D-1998 timer values */
+#define BR_MIN_HELLO_TIME (1*HZ)
+#define BR_MAX_HELLO_TIME (10*HZ)
+
+#define BR_MIN_FORWARD_DELAY (2*HZ)
+#define BR_MAX_FORWARD_DELAY (30*HZ)
+
+#define BR_MIN_MAX_AGE (6*HZ)
+#define BR_MAX_MAX_AGE (40*HZ)
+
+#define BR_MIN_PATH_COST 1
+#define BR_MAX_PATH_COST 65535
+
struct br_config_bpdu
{
unsigned topology_change:1;
diff --git a/net/bridge/br_stp.c b/net/bridge/br_stp.c
index 7370d14f634..bb4383e84de 100644
--- a/net/bridge/br_stp.c
+++ b/net/bridge/br_stp.c
@@ -484,3 +484,51 @@ void br_received_tcn_bpdu(struct net_bridge_port *p)
br_topology_change_acknowledge(p);
}
}
+
+/* Change bridge STP parameter */
+int br_set_hello_time(struct net_bridge *br, unsigned long val)
+{
+ unsigned long t = clock_t_to_jiffies(val);
+
+ if (t < BR_MIN_HELLO_TIME || t > BR_MAX_HELLO_TIME)
+ return -ERANGE;
+
+ spin_lock_bh(&br->lock);
+ br->bridge_hello_time = t;
+ if (br_is_root_bridge(br))
+ br->hello_time = br->bridge_hello_time;
+ spin_unlock_bh(&br->lock);
+ return 0;
+}
+
+int br_set_max_age(struct net_bridge *br, unsigned long val)
+{
+ unsigned long t = clock_t_to_jiffies(val);
+
+ if (t < BR_MIN_MAX_AGE || t > BR_MAX_MAX_AGE)
+ return -ERANGE;
+
+ spin_lock_bh(&br->lock);
+ br->bridge_max_age = t;
+ if (br_is_root_bridge(br))
+ br->max_age = br->bridge_max_age;
+ spin_unlock_bh(&br->lock);
+ return 0;
+
+}
+
+int br_set_forward_delay(struct net_bridge *br, unsigned long val)
+{
+ unsigned long t = clock_t_to_jiffies(val);
+
+ if (br->stp_enabled != BR_NO_STP &&
+ (t < BR_MIN_FORWARD_DELAY || t > BR_MAX_FORWARD_DELAY))
+ return -ERANGE;
+
+ spin_lock_bh(&br->lock);
+ br->bridge_forward_delay = t;
+ if (br_is_root_bridge(br))
+ br->forward_delay = br->bridge_forward_delay;
+ spin_unlock_bh(&br->lock);
+ return 0;
+}
diff --git a/net/bridge/br_stp_if.c b/net/bridge/br_stp_if.c
index 9b61d09de9b..6f615b8192f 100644
--- a/net/bridge/br_stp_if.c
+++ b/net/bridge/br_stp_if.c
@@ -20,7 +20,7 @@
/* Port id is composed of priority and port number.
- * NB: least significant bits of priority are dropped to
+ * NB: some bits of priority are dropped to
* make room for more ports.
*/
static inline port_id br_make_port_id(__u8 priority, __u16 port_no)
@@ -29,6 +29,8 @@ static inline port_id br_make_port_id(__u8 priority, __u16 port_no)
| (port_no & ((1<<BR_PORT_BITS)-1));
}
+#define BR_MAX_PORT_PRIORITY ((u16)~0 >> BR_PORT_BITS)
+
/* called under bridge lock */
void br_init_port(struct net_bridge_port *p)
{
@@ -255,10 +257,14 @@ void br_stp_set_bridge_priority(struct net_bridge *br, u16 newprio)
}
/* called under bridge lock */
-void br_stp_set_port_priority(struct net_bridge_port *p, u8 newprio)
+int br_stp_set_port_priority(struct net_bridge_port *p, unsigned long newprio)
{
- port_id new_port_id = br_make_port_id(newprio, p->port_no);
+ port_id new_port_id;
+
+ if (newprio > BR_MAX_PORT_PRIORITY)
+ return -ERANGE;
+ new_port_id = br_make_port_id(newprio, p->port_no);
if (br_is_designated_port(p))
p->designated_port = new_port_id;
@@ -269,14 +275,21 @@ void br_stp_set_port_priority(struct net_bridge_port *p, u8 newprio)
br_become_designated_port(p);
br_port_state_selection(p->br);
}
+
+ return 0;
}
/* called under bridge lock */
-void br_stp_set_path_cost(struct net_bridge_port *p, u32 path_cost)
+int br_stp_set_path_cost(struct net_bridge_port *p, unsigned long path_cost)
{
+ if (path_cost < BR_MIN_PATH_COST ||
+ path_cost > BR_MAX_PATH_COST)
+ return -ERANGE;
+
p->path_cost = path_cost;
br_configuration_update(p->br);
br_port_state_selection(p->br);
+ return 0;
}
ssize_t br_show_bridge_id(char *buf, const struct bridge_id *id)
diff --git a/net/bridge/br_sysfs_br.c b/net/bridge/br_sysfs_br.c
index 5c1e5559ebb..68b893ea8c3 100644
--- a/net/bridge/br_sysfs_br.c
+++ b/net/bridge/br_sysfs_br.c
@@ -43,9 +43,7 @@ static ssize_t store_bridge_parm(struct device *d,
if (endp == buf)
return -EINVAL;
- spin_lock_bh(&br->lock);
err = (*set)(br, val);
- spin_unlock_bh(&br->lock);
return err ? err : len;
}
@@ -57,20 +55,11 @@ static ssize_t show_forward_delay(struct device *d,
return sprintf(buf, "%lu\n", jiffies_to_clock_t(br->forward_delay));
}
-static int set_forward_delay(struct net_bridge *br, unsigned long val)
-{
- unsigned long delay = clock_t_to_jiffies(val);
- br->forward_delay = delay;
- if (br_is_root_bridge(br))
- br->bridge_forward_delay = delay;
- return 0;
-}
-
static ssize_t store_forward_delay(struct device *d,
struct device_attribute *attr,
const char *buf, size_t len)
{
- return store_bridge_parm(d, buf, len, set_forward_delay);
+ return store_bridge_parm(d, buf, len, br_set_forward_delay);
}
static DEVICE_ATTR(forward_delay, S_IRUGO | S_IWUSR,
show_forward_delay, store_forward_delay);
@@ -82,24 +71,11 @@ static ssize_t show_hello_time(struct device *d, struct device_attribute *attr,
jiffies_to_clock_t(to_bridge(d)->hello_time));
}
-static int set_hello_time(struct net_bridge *br, unsigned long val)
-{
- unsigned long t = clock_t_to_jiffies(val);
-
- if (t < HZ)
- return -EINVAL;
-
- br->hello_time = t;
- if (br_is_root_bridge(br))
- br->bridge_hello_time = t;
- return 0;
-}
-
static ssize_t store_hello_time(struct device *d,
struct device_attribute *attr, const char *buf,
size_t len)
{
- return store_bridge_parm(d, buf, len, set_hello_time);
+ return store_bridge_parm(d, buf, len, br_set_hello_time);
}
static DEVICE_ATTR(hello_time, S_IRUGO | S_IWUSR, show_hello_time,
store_hello_time);
@@ -111,19 +87,10 @@ static ssize_t show_max_age(struct device *d, struct device_attribute *attr,
jiffies_to_clock_t(to_bridge(d)->max_age));
}
-static int set_max_age(struct net_bridge *br, unsigned long val)
-{
- unsigned long t = clock_t_to_jiffies(val);
- br->max_age = t;
- if (br_is_root_bridge(br))
- br->bridge_max_age = t;
- return 0;
-}
-
static ssize_t store_max_age(struct device *d, struct device_attribute *attr,
const char *buf, size_t len)
{
- return store_bridge_parm(d, buf, len, set_max_age);
+ return store_bridge_parm(d, buf, len, br_set_max_age);
}
static DEVICE_ATTR(max_age, S_IRUGO | S_IWUSR, show_max_age, store_max_age);
diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c
index fd5799c9bc8..6229b62749e 100644
--- a/net/bridge/br_sysfs_if.c
+++ b/net/bridge/br_sysfs_if.c
@@ -23,7 +23,7 @@
struct brport_attribute {
struct attribute attr;
ssize_t (*show)(struct net_bridge_port *, char *);
- ssize_t (*store)(struct net_bridge_port *, unsigned long);
+ int (*store)(struct net_bridge_port *, unsigned long);
};
#define BRPORT_ATTR(_name,_mode,_show,_store) \
@@ -38,27 +38,17 @@ static ssize_t show_path_cost(struct net_bridge_port *p, char *buf)
{
return sprintf(buf, "%d\n", p->path_cost);
}
-static ssize_t store_path_cost(struct net_bridge_port *p, unsigned long v)
-{
- br_stp_set_path_cost(p, v);
- return 0;
-}
+
static BRPORT_ATTR(path_cost, S_IRUGO | S_IWUSR,
- show_path_cost, store_path_cost);
+ show_path_cost, br_stp_set_path_cost);
static ssize_t show_priority(struct net_bridge_port *p, char *buf)
{
return sprintf(buf, "%d\n", p->priority);
}
-static ssize_t store_priority(struct net_bridge_port *p, unsigned long v)
-{
- if (v >= (1<<(16-BR_PORT_BITS)))
- return -ERANGE;
- br_stp_set_port_priority(p, v);
- return 0;
-}
+
static BRPORT_ATTR(priority, S_IRUGO | S_IWUSR,
- show_priority, store_priority);
+ show_priority, br_stp_set_port_priority);
static ssize_t show_designated_root(struct net_bridge_port *p, char *buf)
{
@@ -136,7 +126,7 @@ static ssize_t show_hold_timer(struct net_bridge_port *p,
}
static BRPORT_ATTR(hold_timer, S_IRUGO, show_hold_timer, NULL);
-static ssize_t store_flush(struct net_bridge_port *p, unsigned long v)
+static int store_flush(struct net_bridge_port *p, unsigned long v)
{
br_fdb_delete_by_port(p->br, p, 0); // Don't delete local entry
return 0;
@@ -148,7 +138,7 @@ static ssize_t show_hairpin_mode(struct net_bridge_port *p, char *buf)
int hairpin_mode = (p->flags & BR_HAIRPIN_MODE) ? 1 : 0;
return sprintf(buf, "%d\n", hairpin_mode);
}
-static ssize_t store_hairpin_mode(struct net_bridge_port *p, unsigned long v)
+static int store_hairpin_mode(struct net_bridge_port *p, unsigned long v)
{
if (v)
p->flags |= BR_HAIRPIN_MODE;
@@ -165,7 +155,7 @@ static ssize_t show_multicast_router(struct net_bridge_port *p, char *buf)
return sprintf(buf, "%d\n", p->multicast_router);
}
-static ssize_t store_multicast_router(struct net_bridge_port *p,
+static int store_multicast_router(struct net_bridge_port *p,
unsigned long v)
{
return br_multicast_set_port_router(p, v);
diff --git a/net/caif/Makefile b/net/caif/Makefile
index 9d38e406e4a..ebcd4e7e6f4 100644
--- a/net/caif/Makefile
+++ b/net/caif/Makefile
@@ -5,7 +5,7 @@ caif-y := caif_dev.o \
cffrml.o cfveil.o cfdbgl.o\
cfserl.o cfdgml.o \
cfrfml.o cfvidl.o cfutill.o \
- cfsrvl.o cfpkt_skbuff.o caif_config_util.o
+ cfsrvl.o cfpkt_skbuff.o
obj-$(CONFIG_CAIF) += caif.o
obj-$(CONFIG_CAIF_NETDEV) += chnl_net.o
diff --git a/net/caif/caif_config_util.c b/net/caif/caif_config_util.c
deleted file mode 100644
index d522d8c1703..00000000000
--- a/net/caif/caif_config_util.c
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (C) ST-Ericsson AB 2010
- * Author: Sjur Brendeland sjur.brandeland@stericsson.com
- * License terms: GNU General Public License (GPL) version 2
- */
-
-#include <linux/module.h>
-#include <linux/spinlock.h>
-#include <net/caif/cfctrl.h>
-#include <net/caif/cfcnfg.h>
-#include <net/caif/caif_dev.h>
-
-int connect_req_to_link_param(struct cfcnfg *cnfg,
- struct caif_connect_request *s,
- struct cfctrl_link_param *l)
-{
- struct dev_info *dev_info;
- enum cfcnfg_phy_preference pref;
- int res;
-
- memset(l, 0, sizeof(*l));
- /* In caif protocol low value is high priority */
- l->priority = CAIF_PRIO_MAX - s->priority + 1;
-
- if (s->ifindex != 0){
- res = cfcnfg_get_id_from_ifi(cnfg, s->ifindex);
- if (res < 0)
- return res;
- l->phyid = res;
- }
- else {
- switch (s->link_selector) {
- case CAIF_LINK_HIGH_BANDW:
- pref = CFPHYPREF_HIGH_BW;
- break;
- case CAIF_LINK_LOW_LATENCY:
- pref = CFPHYPREF_LOW_LAT;
- break;
- default:
- return -EINVAL;
- }
- dev_info = cfcnfg_get_phyid(cnfg, pref);
- if (dev_info == NULL)
- return -ENODEV;
- l->phyid = dev_info->id;
- }
- switch (s->protocol) {
- case CAIFPROTO_AT:
- l->linktype = CFCTRL_SRV_VEI;
- if (s->sockaddr.u.at.type == CAIF_ATTYPE_PLAIN)
- l->chtype = 0x02;
- else
- l->chtype = s->sockaddr.u.at.type;
- l->endpoint = 0x00;
- break;
- case CAIFPROTO_DATAGRAM:
- l->linktype = CFCTRL_SRV_DATAGRAM;
- l->chtype = 0x00;
- l->u.datagram.connid = s->sockaddr.u.dgm.connection_id;
- break;
- case CAIFPROTO_DATAGRAM_LOOP:
- l->linktype = CFCTRL_SRV_DATAGRAM;
- l->chtype = 0x03;
- l->endpoint = 0x00;
- l->u.datagram.connid = s->sockaddr.u.dgm.connection_id;
- break;
- case CAIFPROTO_RFM:
- l->linktype = CFCTRL_SRV_RFM;
- l->u.datagram.connid = s->sockaddr.u.rfm.connection_id;
- strncpy(l->u.rfm.volume, s->sockaddr.u.rfm.volume,
- sizeof(l->u.rfm.volume)-1);
- l->u.rfm.volume[sizeof(l->u.rfm.volume)-1] = 0;
- break;
- case CAIFPROTO_UTIL:
- l->linktype = CFCTRL_SRV_UTIL;
- l->endpoint = 0x00;
- l->chtype = 0x00;
- strncpy(l->u.utility.name, s->sockaddr.u.util.service,
- sizeof(l->u.utility.name)-1);
- l->u.utility.name[sizeof(l->u.utility.name)-1] = 0;
- caif_assert(sizeof(l->u.utility.name) > 10);
- l->u.utility.paramlen = s->param.size;
- if (l->u.utility.paramlen > sizeof(l->u.utility.params))
- l->u.utility.paramlen = sizeof(l->u.utility.params);
-
- memcpy(l->u.utility.params, s->param.data,
- l->u.utility.paramlen);
-
- break;
- case CAIFPROTO_DEBUG:
- l->linktype = CFCTRL_SRV_DBG;
- l->endpoint = s->sockaddr.u.dbg.service;
- l->chtype = s->sockaddr.u.dbg.type;
- break;
- default:
- return -EINVAL;
- }
- return 0;
-}
diff --git a/net/caif/caif_dev.c b/net/caif/caif_dev.c
index a42a408306e..366ca0fb7a2 100644
--- a/net/caif/caif_dev.c
+++ b/net/caif/caif_dev.c
@@ -12,49 +12,51 @@
#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__
#include <linux/version.h>
-#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/if_arp.h>
#include <linux/net.h>
#include <linux/netdevice.h>
-#include <linux/skbuff.h>
-#include <linux/sched.h>
-#include <linux/wait.h>
+#include <linux/mutex.h>
#include <net/netns/generic.h>
#include <net/net_namespace.h>
#include <net/pkt_sched.h>
#include <net/caif/caif_device.h>
-#include <net/caif/caif_dev.h>
#include <net/caif/caif_layer.h>
#include <net/caif/cfpkt.h>
#include <net/caif/cfcnfg.h>
MODULE_LICENSE("GPL");
-#define TIMEOUT (HZ*5)
/* Used for local tracking of the CAIF net devices */
struct caif_device_entry {
struct cflayer layer;
struct list_head list;
- atomic_t in_use;
- atomic_t state;
- u16 phyid;
struct net_device *netdev;
- wait_queue_head_t event;
+ int __percpu *pcpu_refcnt;
};
struct caif_device_entry_list {
struct list_head list;
/* Protects simulanous deletes in list */
- spinlock_t lock;
+ struct mutex lock;
};
struct caif_net {
+ struct cfcnfg *cfg;
struct caif_device_entry_list caifdevs;
};
static int caif_net_id;
-static struct cfcnfg *cfg;
+
+struct cfcnfg *get_cfcnfg(struct net *net)
+{
+ struct caif_net *caifn;
+ BUG_ON(!net);
+ caifn = net_generic(net, caif_net_id);
+ BUG_ON(!caifn);
+ return caifn->cfg;
+}
+EXPORT_SYMBOL(get_cfcnfg);
static struct caif_device_entry_list *caif_device_list(struct net *net)
{
@@ -65,19 +67,39 @@ static struct caif_device_entry_list *caif_device_list(struct net *net)
return &caifn->caifdevs;
}
+static void caifd_put(struct caif_device_entry *e)
+{
+ irqsafe_cpu_dec(*e->pcpu_refcnt);
+}
+
+static void caifd_hold(struct caif_device_entry *e)
+{
+ irqsafe_cpu_inc(*e->pcpu_refcnt);
+}
+
+static int caifd_refcnt_read(struct caif_device_entry *e)
+{
+ int i, refcnt = 0;
+ for_each_possible_cpu(i)
+ refcnt += *per_cpu_ptr(e->pcpu_refcnt, i);
+ return refcnt;
+}
+
/* Allocate new CAIF device. */
static struct caif_device_entry *caif_device_alloc(struct net_device *dev)
{
struct caif_device_entry_list *caifdevs;
struct caif_device_entry *caifd;
+
caifdevs = caif_device_list(dev_net(dev));
BUG_ON(!caifdevs);
+
caifd = kzalloc(sizeof(*caifd), GFP_ATOMIC);
if (!caifd)
return NULL;
+ caifd->pcpu_refcnt = alloc_percpu(int);
caifd->netdev = dev;
- list_add(&caifd->list, &caifdevs->list);
- init_waitqueue_head(&caifd->event);
+ dev_hold(dev);
return caifd;
}
@@ -87,98 +109,60 @@ static struct caif_device_entry *caif_get(struct net_device *dev)
caif_device_list(dev_net(dev));
struct caif_device_entry *caifd;
BUG_ON(!caifdevs);
- list_for_each_entry(caifd, &caifdevs->list, list) {
+ list_for_each_entry_rcu(caifd, &caifdevs->list, list) {
if (caifd->netdev == dev)
return caifd;
}
return NULL;
}
-static void caif_device_destroy(struct net_device *dev)
-{
- struct caif_device_entry_list *caifdevs =
- caif_device_list(dev_net(dev));
- struct caif_device_entry *caifd;
- ASSERT_RTNL();
- if (dev->type != ARPHRD_CAIF)
- return;
-
- spin_lock_bh(&caifdevs->lock);
- caifd = caif_get(dev);
- if (caifd == NULL) {
- spin_unlock_bh(&caifdevs->lock);
- return;
- }
-
- list_del(&caifd->list);
- spin_unlock_bh(&caifdevs->lock);
-
- kfree(caifd);
-}
-
static int transmit(struct cflayer *layer, struct cfpkt *pkt)
{
+ int err;
struct caif_device_entry *caifd =
container_of(layer, struct caif_device_entry, layer);
- struct sk_buff *skb, *skb2;
- int ret = -EINVAL;
+ struct sk_buff *skb;
+
skb = cfpkt_tonative(pkt);
skb->dev = caifd->netdev;
- /*
- * Don't allow SKB to be destroyed upon error, but signal resend
- * notification to clients. We can't rely on the return value as
- * congestion (NET_XMIT_CN) sometimes drops the packet, sometimes don't.
- */
- if (netif_queue_stopped(caifd->netdev))
- return -EAGAIN;
- skb2 = skb_get(skb);
-
- ret = dev_queue_xmit(skb2);
-
- if (!ret)
- kfree_skb(skb);
- else
- return -EAGAIN;
- return 0;
-}
+ err = dev_queue_xmit(skb);
+ if (err > 0)
+ err = -EIO;
-static int modemcmd(struct cflayer *layr, enum caif_modemcmd ctrl)
-{
- struct caif_device_entry *caifd;
- struct caif_dev_common *caifdev;
- caifd = container_of(layr, struct caif_device_entry, layer);
- caifdev = netdev_priv(caifd->netdev);
- if (ctrl == _CAIF_MODEMCMD_PHYIF_USEFULL) {
- atomic_set(&caifd->in_use, 1);
- wake_up_interruptible(&caifd->event);
-
- } else if (ctrl == _CAIF_MODEMCMD_PHYIF_USELESS) {
- atomic_set(&caifd->in_use, 0);
- wake_up_interruptible(&caifd->event);
- }
- return 0;
+ return err;
}
/*
- * Stuff received packets to associated sockets.
+ * Stuff received packets into the CAIF stack.
* On error, returns non-zero and releases the skb.
*/
static int receive(struct sk_buff *skb, struct net_device *dev,
struct packet_type *pkttype, struct net_device *orig_dev)
{
- struct net *net;
struct cfpkt *pkt;
struct caif_device_entry *caifd;
- net = dev_net(dev);
+
pkt = cfpkt_fromnative(CAIF_DIR_IN, skb);
+
+ rcu_read_lock();
caifd = caif_get(dev);
- if (!caifd || !caifd->layer.up || !caifd->layer.up->receive)
- return NET_RX_DROP;
- if (caifd->layer.up->receive(caifd->layer.up, pkt))
+ if (!caifd || !caifd->layer.up || !caifd->layer.up->receive ||
+ !netif_oper_up(caifd->netdev)) {
+ rcu_read_unlock();
+ kfree_skb(skb);
return NET_RX_DROP;
+ }
+
+ /* Hold reference to netdevice while using CAIF stack */
+ caifd_hold(caifd);
+ rcu_read_unlock();
+
+ caifd->layer.up->receive(caifd->layer.up, pkt);
+ /* Release reference to stack upwards */
+ caifd_put(caifd);
return 0;
}
@@ -189,15 +173,25 @@ static struct packet_type caif_packet_type __read_mostly = {
static void dev_flowctrl(struct net_device *dev, int on)
{
- struct caif_device_entry *caifd = caif_get(dev);
- if (!caifd || !caifd->layer.up || !caifd->layer.up->ctrlcmd)
+ struct caif_device_entry *caifd;
+
+ rcu_read_lock();
+
+ caifd = caif_get(dev);
+ if (!caifd || !caifd->layer.up || !caifd->layer.up->ctrlcmd) {
+ rcu_read_unlock();
return;
+ }
+
+ caifd_hold(caifd);
+ rcu_read_unlock();
caifd->layer.up->ctrlcmd(caifd->layer.up,
on ?
_CAIF_CTRLCMD_PHYIF_FLOW_ON_IND :
_CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND,
caifd->layer.id);
+ caifd_put(caifd);
}
/* notify Caif of device events */
@@ -208,37 +202,28 @@ static int caif_device_notify(struct notifier_block *me, unsigned long what,
struct caif_device_entry *caifd = NULL;
struct caif_dev_common *caifdev;
enum cfcnfg_phy_preference pref;
- int res = -EINVAL;
enum cfcnfg_phy_type phy_type;
+ struct cfcnfg *cfg;
+ struct caif_device_entry_list *caifdevs =
+ caif_device_list(dev_net(dev));
if (dev->type != ARPHRD_CAIF)
return 0;
+ cfg = get_cfcnfg(dev_net(dev));
+ if (cfg == NULL)
+ return 0;
+
switch (what) {
case NETDEV_REGISTER:
- netdev_info(dev, "register\n");
caifd = caif_device_alloc(dev);
- if (caifd == NULL)
- break;
+ if (!caifd)
+ return 0;
+
caifdev = netdev_priv(dev);
caifdev->flowctrl = dev_flowctrl;
- atomic_set(&caifd->state, what);
- res = 0;
- break;
- case NETDEV_UP:
- netdev_info(dev, "up\n");
- caifd = caif_get(dev);
- if (caifd == NULL)
- break;
- caifdev = netdev_priv(dev);
- if (atomic_read(&caifd->state) == NETDEV_UP) {
- netdev_info(dev, "already up\n");
- break;
- }
- atomic_set(&caifd->state, what);
caifd->layer.transmit = transmit;
- caifd->layer.modemcmd = modemcmd;
if (caifdev->use_frag)
phy_type = CFPHYTYPE_FRAG;
@@ -256,62 +241,94 @@ static int caif_device_notify(struct notifier_block *me, unsigned long what,
pref = CFPHYPREF_HIGH_BW;
break;
}
- dev_hold(dev);
- cfcnfg_add_phy_layer(get_caif_conf(),
+ strncpy(caifd->layer.name, dev->name,
+ sizeof(caifd->layer.name) - 1);
+ caifd->layer.name[sizeof(caifd->layer.name) - 1] = 0;
+
+ mutex_lock(&caifdevs->lock);
+ list_add_rcu(&caifd->list, &caifdevs->list);
+
+ cfcnfg_add_phy_layer(cfg,
phy_type,
dev,
&caifd->layer,
- &caifd->phyid,
pref,
caifdev->use_fcs,
caifdev->use_stx);
- strncpy(caifd->layer.name, dev->name,
- sizeof(caifd->layer.name) - 1);
- caifd->layer.name[sizeof(caifd->layer.name) - 1] = 0;
+ mutex_unlock(&caifdevs->lock);
break;
- case NETDEV_GOING_DOWN:
+ case NETDEV_UP:
+ rcu_read_lock();
+
caifd = caif_get(dev);
- if (caifd == NULL)
+ if (caifd == NULL) {
+ rcu_read_unlock();
break;
- netdev_info(dev, "going down\n");
+ }
- if (atomic_read(&caifd->state) == NETDEV_GOING_DOWN ||
- atomic_read(&caifd->state) == NETDEV_DOWN)
- break;
+ cfcnfg_set_phy_state(cfg, &caifd->layer, true);
+ rcu_read_unlock();
- atomic_set(&caifd->state, what);
- if (!caifd || !caifd->layer.up || !caifd->layer.up->ctrlcmd)
- return -EINVAL;
- caifd->layer.up->ctrlcmd(caifd->layer.up,
- _CAIF_CTRLCMD_PHYIF_DOWN_IND,
- caifd->layer.id);
- might_sleep();
- res = wait_event_interruptible_timeout(caifd->event,
- atomic_read(&caifd->in_use) == 0,
- TIMEOUT);
break;
case NETDEV_DOWN:
+ rcu_read_lock();
+
caifd = caif_get(dev);
- if (caifd == NULL)
- break;
- netdev_info(dev, "down\n");
- if (atomic_read(&caifd->in_use))
- netdev_warn(dev,
- "Unregistering an active CAIF device\n");
- cfcnfg_del_phy_layer(get_caif_conf(), &caifd->layer);
- dev_put(dev);
- atomic_set(&caifd->state, what);
+ if (!caifd || !caifd->layer.up || !caifd->layer.up->ctrlcmd) {
+ rcu_read_unlock();
+ return -EINVAL;
+ }
+
+ cfcnfg_set_phy_state(cfg, &caifd->layer, false);
+ caifd_hold(caifd);
+ rcu_read_unlock();
+
+ caifd->layer.up->ctrlcmd(caifd->layer.up,
+ _CAIF_CTRLCMD_PHYIF_DOWN_IND,
+ caifd->layer.id);
+ caifd_put(caifd);
break;
case NETDEV_UNREGISTER:
+ mutex_lock(&caifdevs->lock);
+
caifd = caif_get(dev);
- if (caifd == NULL)
+ if (caifd == NULL) {
+ mutex_unlock(&caifdevs->lock);
+ break;
+ }
+ list_del_rcu(&caifd->list);
+
+ /*
+ * NETDEV_UNREGISTER is called repeatedly until all reference
+ * counts for the net-device are released. If references to
+ * caifd is taken, simply ignore NETDEV_UNREGISTER and wait for
+ * the next call to NETDEV_UNREGISTER.
+ *
+ * If any packets are in flight down the CAIF Stack,
+ * cfcnfg_del_phy_layer will return nonzero.
+ * If no packets are in flight, the CAIF Stack associated
+ * with the net-device un-registering is freed.
+ */
+
+ if (caifd_refcnt_read(caifd) != 0 ||
+ cfcnfg_del_phy_layer(cfg, &caifd->layer) != 0) {
+
+ pr_info("Wait for device inuse\n");
+ /* Enrole device if CAIF Stack is still in use */
+ list_add_rcu(&caifd->list, &caifdevs->list);
+ mutex_unlock(&caifdevs->lock);
break;
- netdev_info(dev, "unregister\n");
- atomic_set(&caifd->state, what);
- caif_device_destroy(dev);
+ }
+
+ synchronize_rcu();
+ dev_put(caifd->netdev);
+ free_percpu(caifd->pcpu_refcnt);
+ kfree(caifd);
+
+ mutex_unlock(&caifdevs->lock);
break;
}
return 0;
@@ -322,61 +339,60 @@ static struct notifier_block caif_device_notifier = {
.priority = 0,
};
-
-struct cfcnfg *get_caif_conf(void)
-{
- return cfg;
-}
-EXPORT_SYMBOL(get_caif_conf);
-
-int caif_connect_client(struct caif_connect_request *conn_req,
- struct cflayer *client_layer, int *ifindex,
- int *headroom, int *tailroom)
-{
- struct cfctrl_link_param param;
- int ret;
- ret = connect_req_to_link_param(get_caif_conf(), conn_req, &param);
- if (ret)
- return ret;
- /* Hook up the adaptation layer. */
- return cfcnfg_add_adaptation_layer(get_caif_conf(), &param,
- client_layer, ifindex,
- headroom, tailroom);
-}
-EXPORT_SYMBOL(caif_connect_client);
-
-int caif_disconnect_client(struct cflayer *adap_layer)
-{
- return cfcnfg_disconn_adapt_layer(get_caif_conf(), adap_layer);
-}
-EXPORT_SYMBOL(caif_disconnect_client);
-
-void caif_release_client(struct cflayer *adap_layer)
-{
- cfcnfg_release_adap_layer(adap_layer);
-}
-EXPORT_SYMBOL(caif_release_client);
-
/* Per-namespace Caif devices handling */
static int caif_init_net(struct net *net)
{
struct caif_net *caifn = net_generic(net, caif_net_id);
+ BUG_ON(!caifn);
INIT_LIST_HEAD(&caifn->caifdevs.list);
- spin_lock_init(&caifn->caifdevs.lock);
+ mutex_init(&caifn->caifdevs.lock);
+
+ caifn->cfg = cfcnfg_create();
+ if (!caifn->cfg) {
+ pr_warn("can't create cfcnfg\n");
+ return -ENOMEM;
+ }
+
return 0;
}
static void caif_exit_net(struct net *net)
{
- struct net_device *dev;
- int res;
+ struct caif_device_entry *caifd, *tmp;
+ struct caif_device_entry_list *caifdevs =
+ caif_device_list(net);
+ struct cfcnfg *cfg;
+
rtnl_lock();
- for_each_netdev(net, dev) {
- if (dev->type != ARPHRD_CAIF)
- continue;
- res = dev_close(dev);
- caif_device_destroy(dev);
+ mutex_lock(&caifdevs->lock);
+
+ cfg = get_cfcnfg(net);
+ if (cfg == NULL) {
+ mutex_unlock(&caifdevs->lock);
+ return;
}
+
+ list_for_each_entry_safe(caifd, tmp, &caifdevs->list, list) {
+ int i = 0;
+ list_del_rcu(&caifd->list);
+ cfcnfg_set_phy_state(cfg, &caifd->layer, false);
+
+ while (i < 10 &&
+ (caifd_refcnt_read(caifd) != 0 ||
+ cfcnfg_del_phy_layer(cfg, &caifd->layer) != 0)) {
+
+ pr_info("Wait for device inuse\n");
+ msleep(250);
+ i++;
+ }
+ synchronize_rcu();
+ dev_put(caifd->netdev);
+ free_percpu(caifd->pcpu_refcnt);
+ kfree(caifd);
+ }
+ cfcnfg_remove(cfg);
+
+ mutex_unlock(&caifdevs->lock);
rtnl_unlock();
}
@@ -391,32 +407,23 @@ static struct pernet_operations caif_net_ops = {
static int __init caif_device_init(void)
{
int result;
- cfg = cfcnfg_create();
- if (!cfg) {
- pr_warn("can't create cfcnfg\n");
- goto err_cfcnfg_create_failed;
- }
+
result = register_pernet_device(&caif_net_ops);
- if (result) {
- kfree(cfg);
- cfg = NULL;
+ if (result)
return result;
- }
- dev_add_pack(&caif_packet_type);
+
register_netdevice_notifier(&caif_device_notifier);
+ dev_add_pack(&caif_packet_type);
return result;
-err_cfcnfg_create_failed:
- return -ENODEV;
}
static void __exit caif_device_exit(void)
{
- dev_remove_pack(&caif_packet_type);
unregister_pernet_device(&caif_net_ops);
unregister_netdevice_notifier(&caif_device_notifier);
- cfcnfg_remove(cfg);
+ dev_remove_pack(&caif_packet_type);
}
module_init(caif_device_init);
diff --git a/net/caif/caif_socket.c b/net/caif/caif_socket.c
index 37a4034dfc2..b840395ced1 100644
--- a/net/caif/caif_socket.c
+++ b/net/caif/caif_socket.c
@@ -48,6 +48,7 @@ static struct dentry *debugfsdir;
#ifdef CONFIG_DEBUG_FS
struct debug_fs_counter {
atomic_t caif_nr_socks;
+ atomic_t caif_sock_create;
atomic_t num_connect_req;
atomic_t num_connect_resp;
atomic_t num_connect_fail_resp;
@@ -59,11 +60,11 @@ struct debug_fs_counter {
atomic_t num_rx_flow_on;
};
static struct debug_fs_counter cnt;
-#define dbfs_atomic_inc(v) atomic_inc(v)
-#define dbfs_atomic_dec(v) atomic_dec(v)
+#define dbfs_atomic_inc(v) atomic_inc_return(v)
+#define dbfs_atomic_dec(v) atomic_dec_return(v)
#else
-#define dbfs_atomic_inc(v)
-#define dbfs_atomic_dec(v)
+#define dbfs_atomic_inc(v) 0
+#define dbfs_atomic_dec(v) 0
#endif
struct caifsock {
@@ -155,9 +156,10 @@ static int caif_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
if (atomic_read(&sk->sk_rmem_alloc) + skb->truesize >=
(unsigned)sk->sk_rcvbuf && rx_flow_is_on(cf_sk)) {
- pr_debug("sending flow OFF (queue len = %d %d)\n",
- atomic_read(&cf_sk->sk.sk_rmem_alloc),
- sk_rcvbuf_lowwater(cf_sk));
+ if (net_ratelimit())
+ pr_debug("sending flow OFF (queue len = %d %d)\n",
+ atomic_read(&cf_sk->sk.sk_rmem_alloc),
+ sk_rcvbuf_lowwater(cf_sk));
set_rx_flow_off(cf_sk);
dbfs_atomic_inc(&cnt.num_rx_flow_off);
caif_flow_ctrl(sk, CAIF_MODEMCMD_FLOW_OFF_REQ);
@@ -168,7 +170,8 @@ static int caif_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
return err;
if (!sk_rmem_schedule(sk, skb->truesize) && rx_flow_is_on(cf_sk)) {
set_rx_flow_off(cf_sk);
- pr_debug("sending flow OFF due to rmem_schedule\n");
+ if (net_ratelimit())
+ pr_debug("sending flow OFF due to rmem_schedule\n");
dbfs_atomic_inc(&cnt.num_rx_flow_off);
caif_flow_ctrl(sk, CAIF_MODEMCMD_FLOW_OFF_REQ);
}
@@ -202,13 +205,25 @@ static int caif_sktrecv_cb(struct cflayer *layr, struct cfpkt *pkt)
skb = cfpkt_tonative(pkt);
if (unlikely(cf_sk->sk.sk_state != CAIF_CONNECTED)) {
- cfpkt_destroy(pkt);
+ kfree_skb(skb);
return 0;
}
caif_queue_rcv_skb(&cf_sk->sk, skb);
return 0;
}
+static void cfsk_hold(struct cflayer *layr)
+{
+ struct caifsock *cf_sk = container_of(layr, struct caifsock, layer);
+ sock_hold(&cf_sk->sk);
+}
+
+static void cfsk_put(struct cflayer *layr)
+{
+ struct caifsock *cf_sk = container_of(layr, struct caifsock, layer);
+ sock_put(&cf_sk->sk);
+}
+
/* Packet Control Callback function called from CAIF */
static void caif_ctrl_cb(struct cflayer *layr,
enum caif_ctrlcmd flow,
@@ -232,6 +247,8 @@ static void caif_ctrl_cb(struct cflayer *layr,
case CAIF_CTRLCMD_INIT_RSP:
/* We're now connected */
+ caif_client_register_refcnt(&cf_sk->layer,
+ cfsk_hold, cfsk_put);
dbfs_atomic_inc(&cnt.num_connect_resp);
cf_sk->sk.sk_state = CAIF_CONNECTED;
set_tx_flow_on(cf_sk);
@@ -242,7 +259,6 @@ static void caif_ctrl_cb(struct cflayer *layr,
/* We're now disconnected */
cf_sk->sk.sk_state = CAIF_DISCONNECTED;
cf_sk->sk.sk_state_change(&cf_sk->sk);
- cfcnfg_release_adap_layer(&cf_sk->layer);
break;
case CAIF_CTRLCMD_INIT_FAIL_RSP:
@@ -519,43 +535,14 @@ static int transmit_skb(struct sk_buff *skb, struct caifsock *cf_sk,
int noblock, long timeo)
{
struct cfpkt *pkt;
- int ret, loopcnt = 0;
pkt = cfpkt_fromnative(CAIF_DIR_OUT, skb);
- memset(cfpkt_info(pkt), 0, sizeof(struct caif_payload_info));
- do {
+ memset(skb->cb, 0, sizeof(struct caif_payload_info));
- ret = -ETIMEDOUT;
+ if (cf_sk->layer.dn == NULL)
+ return -EINVAL;
- /* Slight paranoia, probably not needed. */
- if (unlikely(loopcnt++ > 1000)) {
- pr_warn("transmit retries failed, error = %d\n", ret);
- break;
- }
-
- if (cf_sk->layer.dn != NULL)
- ret = cf_sk->layer.dn->transmit(cf_sk->layer.dn, pkt);
- if (likely(ret >= 0))
- break;
- /* if transmit return -EAGAIN, then retry */
- if (noblock && ret == -EAGAIN)
- break;
- timeo = caif_wait_for_flow_on(cf_sk, 0, timeo, &ret);
- if (signal_pending(current)) {
- ret = sock_intr_errno(timeo);
- break;
- }
- if (ret)
- break;
- if (cf_sk->sk.sk_state != CAIF_CONNECTED ||
- sock_flag(&cf_sk->sk, SOCK_DEAD) ||
- (cf_sk->sk.sk_shutdown & RCV_SHUTDOWN)) {
- ret = -EPIPE;
- cf_sk->sk.sk_err = EPIPE;
- break;
- }
- } while (ret == -EAGAIN);
- return ret;
+ return cf_sk->layer.dn->transmit(cf_sk->layer.dn, pkt);
}
/* Copied from af_unix:unix_dgram_sendmsg, and adapted to CAIF */
@@ -620,7 +607,9 @@ static int caif_seqpkt_sendmsg(struct kiocb *kiocb, struct socket *sock,
goto err;
ret = transmit_skb(skb, cf_sk, noblock, timeo);
if (ret < 0)
- goto err;
+ /* skb is already freed */
+ return ret;
+
return len;
err:
kfree_skb(skb);
@@ -826,7 +815,7 @@ static int caif_connect(struct socket *sock, struct sockaddr *uaddr,
sk->sk_state == CAIF_DISCONNECTED);
if (sk->sk_shutdown & SHUTDOWN_MASK) {
/* Allow re-connect after SHUTDOWN_IND */
- caif_disconnect_client(&cf_sk->layer);
+ caif_disconnect_client(sock_net(sk), &cf_sk->layer);
break;
}
/* No reconnect on a seqpacket socket */
@@ -852,7 +841,7 @@ static int caif_connect(struct socket *sock, struct sockaddr *uaddr,
sock->state = SS_CONNECTING;
sk->sk_state = CAIF_CONNECTING;
- /* Check priority value coming from socket */
+ /* Check priority value comming from socket */
/* if priority value is out of range it will be ajusted */
if (cf_sk->sk.sk_priority > CAIF_PRIO_MAX)
cf_sk->conn_req.priority = CAIF_PRIO_MAX;
@@ -866,8 +855,10 @@ static int caif_connect(struct socket *sock, struct sockaddr *uaddr,
dbfs_atomic_inc(&cnt.num_connect_req);
cf_sk->layer.receive = caif_sktrecv_cb;
- err = caif_connect_client(&cf_sk->conn_req,
+
+ err = caif_connect_client(sock_net(sk), &cf_sk->conn_req,
&cf_sk->layer, &ifindex, &headroom, &tailroom);
+
if (err < 0) {
cf_sk->sk.sk_socket->state = SS_UNCONNECTED;
cf_sk->sk.sk_state = CAIF_DISCONNECTED;
@@ -947,13 +938,14 @@ static int caif_release(struct socket *sock)
* caif_queue_rcv_skb checks SOCK_DEAD holding the queue lock,
* this ensures no packets when sock is dead.
*/
- spin_lock(&sk->sk_receive_queue.lock);
+ spin_lock_bh(&sk->sk_receive_queue.lock);
sock_set_flag(sk, SOCK_DEAD);
- spin_unlock(&sk->sk_receive_queue.lock);
+ spin_unlock_bh(&sk->sk_receive_queue.lock);
sock->sk = NULL;
dbfs_atomic_inc(&cnt.num_disconnect);
+ WARN_ON(IS_ERR(cf_sk->debugfs_socket_dir));
if (cf_sk->debugfs_socket_dir != NULL)
debugfs_remove_recursive(cf_sk->debugfs_socket_dir);
@@ -963,13 +955,12 @@ static int caif_release(struct socket *sock)
if (cf_sk->sk.sk_socket->state == SS_CONNECTED ||
cf_sk->sk.sk_socket->state == SS_CONNECTING)
- res = caif_disconnect_client(&cf_sk->layer);
+ res = caif_disconnect_client(sock_net(sk), &cf_sk->layer);
cf_sk->sk.sk_socket->state = SS_DISCONNECTING;
wake_up_interruptible_poll(sk_sleep(sk), POLLERR|POLLHUP);
sock_orphan(sk);
- cf_sk->layer.dn = NULL;
sk_stream_kill_queues(&cf_sk->sk);
release_sock(sk);
sock_put(sk);
@@ -1060,16 +1051,18 @@ static void caif_sock_destructor(struct sock *sk)
caif_assert(sk_unhashed(sk));
caif_assert(!sk->sk_socket);
if (!sock_flag(sk, SOCK_DEAD)) {
- pr_info("Attempt to release alive CAIF socket: %p\n", sk);
+ pr_debug("Attempt to release alive CAIF socket: %p\n", sk);
return;
}
sk_stream_kill_queues(&cf_sk->sk);
dbfs_atomic_dec(&cnt.caif_nr_socks);
+ caif_free_client(&cf_sk->layer);
}
static int caif_create(struct net *net, struct socket *sock, int protocol,
int kern)
{
+ int num;
struct sock *sk = NULL;
struct caifsock *cf_sk = NULL;
static struct proto prot = {.name = "PF_CAIF",
@@ -1132,14 +1125,16 @@ static int caif_create(struct net *net, struct socket *sock, int protocol,
cf_sk->conn_req.protocol = protocol;
/* Increase the number of sockets created. */
dbfs_atomic_inc(&cnt.caif_nr_socks);
+ num = dbfs_atomic_inc(&cnt.caif_sock_create);
#ifdef CONFIG_DEBUG_FS
if (!IS_ERR(debugfsdir)) {
+
/* Fill in some information concerning the misc socket. */
- snprintf(cf_sk->name, sizeof(cf_sk->name), "cfsk%d",
- atomic_read(&cnt.caif_nr_socks));
+ snprintf(cf_sk->name, sizeof(cf_sk->name), "cfsk%d", num);
cf_sk->debugfs_socket_dir =
debugfs_create_dir(cf_sk->name, debugfsdir);
+
debugfs_create_u32("sk_state", S_IRUSR | S_IWUSR,
cf_sk->debugfs_socket_dir,
(u32 *) &cf_sk->sk.sk_state);
@@ -1183,6 +1178,9 @@ static int __init caif_sktinit_module(void)
debugfs_create_u32("num_sockets", S_IRUSR | S_IWUSR,
debugfsdir,
(u32 *) &cnt.caif_nr_socks);
+ debugfs_create_u32("num_create", S_IRUSR | S_IWUSR,
+ debugfsdir,
+ (u32 *) &cnt.caif_sock_create);
debugfs_create_u32("num_connect_req", S_IRUSR | S_IWUSR,
debugfsdir,
(u32 *) &cnt.num_connect_req);
diff --git a/net/caif/cfcnfg.c b/net/caif/cfcnfg.c
index f1f98d967d8..351c2ca7e7b 100644
--- a/net/caif/cfcnfg.c
+++ b/net/caif/cfcnfg.c
@@ -10,6 +10,7 @@
#include <linux/stddef.h>
#include <linux/slab.h>
#include <linux/netdevice.h>
+#include <linux/module.h>
#include <net/caif/caif_layer.h>
#include <net/caif/cfpkt.h>
#include <net/caif/cfcnfg.h>
@@ -18,11 +19,7 @@
#include <net/caif/cffrml.h>
#include <net/caif/cfserl.h>
#include <net/caif/cfsrvl.h>
-
-#include <linux/module.h>
-#include <asm/atomic.h>
-
-#define MAX_PHY_LAYERS 7
+#include <net/caif/caif_dev.h>
#define container_obj(layr) container_of(layr, struct cfcnfg, layer)
@@ -30,6 +27,9 @@
* to manage physical interfaces
*/
struct cfcnfg_phyinfo {
+ struct list_head node;
+ bool up;
+
/* Pointer to the layer below the MUX (framing layer) */
struct cflayer *frm_layer;
/* Pointer to the lowest actual physical layer */
@@ -39,9 +39,6 @@ struct cfcnfg_phyinfo {
/* Preference of the physical in interface */
enum cfcnfg_phy_preference pref;
- /* Reference count, number of channels using the device */
- int phy_ref_count;
-
/* Information about the physical device */
struct dev_info dev_info;
@@ -59,8 +56,8 @@ struct cfcnfg {
struct cflayer layer;
struct cflayer *ctrl;
struct cflayer *mux;
- u8 last_phyid;
- struct cfcnfg_phyinfo phy_layers[MAX_PHY_LAYERS];
+ struct list_head phys;
+ struct mutex lock;
};
static void cfcnfg_linkup_rsp(struct cflayer *layer, u8 channel_id,
@@ -76,6 +73,9 @@ struct cfcnfg *cfcnfg_create(void)
{
struct cfcnfg *this;
struct cfctrl_rsp *resp;
+
+ might_sleep();
+
/* Initiate this layer */
this = kzalloc(sizeof(struct cfcnfg), GFP_ATOMIC);
if (!this) {
@@ -99,27 +99,33 @@ struct cfcnfg *cfcnfg_create(void)
resp->radioset_rsp = cfctrl_resp_func;
resp->linksetup_rsp = cfcnfg_linkup_rsp;
resp->reject_rsp = cfcnfg_reject_rsp;
-
- this->last_phyid = 1;
+ INIT_LIST_HEAD(&this->phys);
cfmuxl_set_uplayer(this->mux, this->ctrl, 0);
layer_set_dn(this->ctrl, this->mux);
layer_set_up(this->ctrl, this);
+ mutex_init(&this->lock);
+
return this;
out_of_mem:
pr_warn("Out of memory\n");
+
+ synchronize_rcu();
+
kfree(this->mux);
kfree(this->ctrl);
kfree(this);
return NULL;
}
-EXPORT_SYMBOL(cfcnfg_create);
void cfcnfg_remove(struct cfcnfg *cfg)
{
+ might_sleep();
if (cfg) {
+ synchronize_rcu();
+
kfree(cfg->mux);
- kfree(cfg->ctrl);
+ cfctrl_remove(cfg->ctrl);
kfree(cfg);
}
}
@@ -128,132 +134,96 @@ static void cfctrl_resp_func(void)
{
}
+static struct cfcnfg_phyinfo *cfcnfg_get_phyinfo_rcu(struct cfcnfg *cnfg,
+ u8 phyid)
+{
+ struct cfcnfg_phyinfo *phy;
+
+ list_for_each_entry_rcu(phy, &cnfg->phys, node)
+ if (phy->id == phyid)
+ return phy;
+ return NULL;
+}
+
static void cfctrl_enum_resp(void)
{
}
-struct dev_info *cfcnfg_get_phyid(struct cfcnfg *cnfg,
+static struct dev_info *cfcnfg_get_phyid(struct cfcnfg *cnfg,
enum cfcnfg_phy_preference phy_pref)
{
- u16 i;
-
/* Try to match with specified preference */
- for (i = 1; i < MAX_PHY_LAYERS; i++) {
- if (cnfg->phy_layers[i].id == i &&
- cnfg->phy_layers[i].pref == phy_pref &&
- cnfg->phy_layers[i].frm_layer != NULL) {
- caif_assert(cnfg->phy_layers != NULL);
- caif_assert(cnfg->phy_layers[i].id == i);
- return &cnfg->phy_layers[i].dev_info;
- }
+ struct cfcnfg_phyinfo *phy;
+
+ list_for_each_entry_rcu(phy, &cnfg->phys, node) {
+ if (phy->up && phy->pref == phy_pref &&
+ phy->frm_layer != NULL)
+
+ return &phy->dev_info;
}
+
/* Otherwise just return something */
- for (i = 1; i < MAX_PHY_LAYERS; i++) {
- if (cnfg->phy_layers[i].id == i) {
- caif_assert(cnfg->phy_layers != NULL);
- caif_assert(cnfg->phy_layers[i].id == i);
- return &cnfg->phy_layers[i].dev_info;
- }
- }
+ list_for_each_entry_rcu(phy, &cnfg->phys, node)
+ if (phy->up)
+ return &phy->dev_info;
return NULL;
}
-static struct cfcnfg_phyinfo *cfcnfg_get_phyinfo(struct cfcnfg *cnfg,
- u8 phyid)
+static int cfcnfg_get_id_from_ifi(struct cfcnfg *cnfg, int ifi)
{
- int i;
- /* Try to match with specified preference */
- for (i = 0; i < MAX_PHY_LAYERS; i++)
- if (cnfg->phy_layers[i].frm_layer != NULL &&
- cnfg->phy_layers[i].id == phyid)
- return &cnfg->phy_layers[i];
- return NULL;
-}
-
+ struct cfcnfg_phyinfo *phy;
-int cfcnfg_get_id_from_ifi(struct cfcnfg *cnfg, int ifi)
-{
- int i;
- for (i = 0; i < MAX_PHY_LAYERS; i++)
- if (cnfg->phy_layers[i].frm_layer != NULL &&
- cnfg->phy_layers[i].ifindex == ifi)
- return i;
+ list_for_each_entry_rcu(phy, &cnfg->phys, node)
+ if (phy->ifindex == ifi && phy->up)
+ return phy->id;
return -ENODEV;
}
-int cfcnfg_disconn_adapt_layer(struct cfcnfg *cnfg, struct cflayer *adap_layer)
+int caif_disconnect_client(struct net *net, struct cflayer *adap_layer)
{
u8 channel_id = 0;
int ret = 0;
struct cflayer *servl = NULL;
- struct cfcnfg_phyinfo *phyinfo = NULL;
- u8 phyid = 0;
+ struct cfcnfg *cfg = get_cfcnfg(net);
caif_assert(adap_layer != NULL);
+
channel_id = adap_layer->id;
if (adap_layer->dn == NULL || channel_id == 0) {
pr_err("adap_layer->dn == NULL or adap_layer->id is 0\n");
ret = -ENOTCONN;
goto end;
}
- servl = cfmuxl_remove_uplayer(cnfg->mux, channel_id);
+
+ servl = cfmuxl_remove_uplayer(cfg->mux, channel_id);
if (servl == NULL) {
- pr_err("PROTOCOL ERROR - Error removing service_layer Channel_Id(%d)",
- channel_id);
+ pr_err("PROTOCOL ERROR - "
+ "Error removing service_layer Channel_Id(%d)",
+ channel_id);
ret = -EINVAL;
goto end;
}
- layer_set_up(servl, NULL);
- ret = cfctrl_linkdown_req(cnfg->ctrl, channel_id, adap_layer);
- if (ret)
- goto end;
- caif_assert(channel_id == servl->id);
- if (adap_layer->dn != NULL) {
- phyid = cfsrvl_getphyid(adap_layer->dn);
-
- phyinfo = cfcnfg_get_phyinfo(cnfg, phyid);
- if (phyinfo == NULL) {
- pr_warn("No interface to send disconnect to\n");
- ret = -ENODEV;
- goto end;
- }
- if (phyinfo->id != phyid ||
- phyinfo->phy_layer->id != phyid ||
- phyinfo->frm_layer->id != phyid) {
- pr_err("Inconsistency in phy registration\n");
- ret = -EINVAL;
- goto end;
- }
- }
- if (phyinfo != NULL && --phyinfo->phy_ref_count == 0 &&
- phyinfo->phy_layer != NULL &&
- phyinfo->phy_layer->modemcmd != NULL) {
- phyinfo->phy_layer->modemcmd(phyinfo->phy_layer,
- _CAIF_MODEMCMD_PHYIF_USELESS);
- }
+
+ ret = cfctrl_linkdown_req(cfg->ctrl, channel_id, adap_layer);
+
end:
- cfsrvl_put(servl);
- cfctrl_cancel_req(cnfg->ctrl, adap_layer);
+ cfctrl_cancel_req(cfg->ctrl, adap_layer);
+
+ /* Do RCU sync before initiating cleanup */
+ synchronize_rcu();
if (adap_layer->ctrlcmd != NULL)
adap_layer->ctrlcmd(adap_layer, CAIF_CTRLCMD_DEINIT_RSP, 0);
return ret;
}
-EXPORT_SYMBOL(cfcnfg_disconn_adapt_layer);
-
-void cfcnfg_release_adap_layer(struct cflayer *adap_layer)
-{
- if (adap_layer->dn)
- cfsrvl_put(adap_layer->dn);
-}
-EXPORT_SYMBOL(cfcnfg_release_adap_layer);
+EXPORT_SYMBOL(caif_disconnect_client);
static void cfcnfg_linkdestroy_rsp(struct cflayer *layer, u8 channel_id)
{
}
-int protohead[CFCTRL_SRV_MASK] = {
+static const int protohead[CFCTRL_SRV_MASK] = {
[CFCTRL_SRV_VEI] = 4,
[CFCTRL_SRV_DATAGRAM] = 7,
[CFCTRL_SRV_UTIL] = 4,
@@ -261,49 +231,157 @@ int protohead[CFCTRL_SRV_MASK] = {
[CFCTRL_SRV_DBG] = 3,
};
-int cfcnfg_add_adaptation_layer(struct cfcnfg *cnfg,
- struct cfctrl_link_param *param,
- struct cflayer *adap_layer,
- int *ifindex,
+
+static int caif_connect_req_to_link_param(struct cfcnfg *cnfg,
+ struct caif_connect_request *s,
+ struct cfctrl_link_param *l)
+{
+ struct dev_info *dev_info;
+ enum cfcnfg_phy_preference pref;
+ int res;
+
+ memset(l, 0, sizeof(*l));
+ /* In caif protocol low value is high priority */
+ l->priority = CAIF_PRIO_MAX - s->priority + 1;
+
+ if (s->ifindex != 0) {
+ res = cfcnfg_get_id_from_ifi(cnfg, s->ifindex);
+ if (res < 0)
+ return res;
+ l->phyid = res;
+ } else {
+ switch (s->link_selector) {
+ case CAIF_LINK_HIGH_BANDW:
+ pref = CFPHYPREF_HIGH_BW;
+ break;
+ case CAIF_LINK_LOW_LATENCY:
+ pref = CFPHYPREF_LOW_LAT;
+ break;
+ default:
+ return -EINVAL;
+ }
+ dev_info = cfcnfg_get_phyid(cnfg, pref);
+ if (dev_info == NULL)
+ return -ENODEV;
+ l->phyid = dev_info->id;
+ }
+ switch (s->protocol) {
+ case CAIFPROTO_AT:
+ l->linktype = CFCTRL_SRV_VEI;
+ l->endpoint = (s->sockaddr.u.at.type >> 2) & 0x3;
+ l->chtype = s->sockaddr.u.at.type & 0x3;
+ break;
+ case CAIFPROTO_DATAGRAM:
+ l->linktype = CFCTRL_SRV_DATAGRAM;
+ l->chtype = 0x00;
+ l->u.datagram.connid = s->sockaddr.u.dgm.connection_id;
+ break;
+ case CAIFPROTO_DATAGRAM_LOOP:
+ l->linktype = CFCTRL_SRV_DATAGRAM;
+ l->chtype = 0x03;
+ l->endpoint = 0x00;
+ l->u.datagram.connid = s->sockaddr.u.dgm.connection_id;
+ break;
+ case CAIFPROTO_RFM:
+ l->linktype = CFCTRL_SRV_RFM;
+ l->u.datagram.connid = s->sockaddr.u.rfm.connection_id;
+ strncpy(l->u.rfm.volume, s->sockaddr.u.rfm.volume,
+ sizeof(l->u.rfm.volume)-1);
+ l->u.rfm.volume[sizeof(l->u.rfm.volume)-1] = 0;
+ break;
+ case CAIFPROTO_UTIL:
+ l->linktype = CFCTRL_SRV_UTIL;
+ l->endpoint = 0x00;
+ l->chtype = 0x00;
+ strncpy(l->u.utility.name, s->sockaddr.u.util.service,
+ sizeof(l->u.utility.name)-1);
+ l->u.utility.name[sizeof(l->u.utility.name)-1] = 0;
+ caif_assert(sizeof(l->u.utility.name) > 10);
+ l->u.utility.paramlen = s->param.size;
+ if (l->u.utility.paramlen > sizeof(l->u.utility.params))
+ l->u.utility.paramlen = sizeof(l->u.utility.params);
+
+ memcpy(l->u.utility.params, s->param.data,
+ l->u.utility.paramlen);
+
+ break;
+ case CAIFPROTO_DEBUG:
+ l->linktype = CFCTRL_SRV_DBG;
+ l->endpoint = s->sockaddr.u.dbg.service;
+ l->chtype = s->sockaddr.u.dbg.type;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int caif_connect_client(struct net *net, struct caif_connect_request *conn_req,
+ struct cflayer *adap_layer, int *ifindex,
int *proto_head,
int *proto_tail)
{
struct cflayer *frml;
+ struct cfcnfg_phyinfo *phy;
+ int err;
+ struct cfctrl_link_param param;
+ struct cfcnfg *cfg = get_cfcnfg(net);
+ caif_assert(cfg != NULL);
+
+ rcu_read_lock();
+ err = caif_connect_req_to_link_param(cfg, conn_req, &param);
+ if (err)
+ goto unlock;
+
+ phy = cfcnfg_get_phyinfo_rcu(cfg, param.phyid);
+ if (!phy) {
+ err = -ENODEV;
+ goto unlock;
+ }
+ err = -EINVAL;
+
if (adap_layer == NULL) {
pr_err("adap_layer is zero\n");
- return -EINVAL;
+ goto unlock;
}
if (adap_layer->receive == NULL) {
pr_err("adap_layer->receive is NULL\n");
- return -EINVAL;
+ goto unlock;
}
if (adap_layer->ctrlcmd == NULL) {
pr_err("adap_layer->ctrlcmd == NULL\n");
- return -EINVAL;
+ goto unlock;
}
- frml = cnfg->phy_layers[param->phyid].frm_layer;
+
+ err = -ENODEV;
+ frml = phy->frm_layer;
if (frml == NULL) {
pr_err("Specified PHY type does not exist!\n");
- return -ENODEV;
+ goto unlock;
}
- caif_assert(param->phyid == cnfg->phy_layers[param->phyid].id);
- caif_assert(cnfg->phy_layers[param->phyid].frm_layer->id ==
- param->phyid);
- caif_assert(cnfg->phy_layers[param->phyid].phy_layer->id ==
- param->phyid);
+ caif_assert(param.phyid == phy->id);
+ caif_assert(phy->frm_layer->id ==
+ param.phyid);
+ caif_assert(phy->phy_layer->id ==
+ param.phyid);
- *ifindex = cnfg->phy_layers[param->phyid].ifindex;
+ *ifindex = phy->ifindex;
+ *proto_tail = 2;
*proto_head =
- protohead[param->linktype]+
- (cnfg->phy_layers[param->phyid].use_stx ? 1 : 0);
- *proto_tail = 2;
+ protohead[param.linktype] + (phy->use_stx ? 1 : 0);
+
+ rcu_read_unlock();
/* FIXME: ENUMERATE INITIALLY WHEN ACTIVATING PHYSICAL INTERFACE */
- cfctrl_enum_req(cnfg->ctrl, param->phyid);
- return cfctrl_linkup_request(cnfg->ctrl, param, adap_layer);
+ cfctrl_enum_req(cfg->ctrl, param.phyid);
+ return cfctrl_linkup_request(cfg->ctrl, &param, adap_layer);
+
+unlock:
+ rcu_read_unlock();
+ return err;
}
-EXPORT_SYMBOL(cfcnfg_add_adaptation_layer);
+EXPORT_SYMBOL(caif_connect_client);
static void cfcnfg_reject_rsp(struct cflayer *layer, u8 channel_id,
struct cflayer *adapt_layer)
@@ -315,32 +393,37 @@ static void cfcnfg_reject_rsp(struct cflayer *layer, u8 channel_id,
static void
cfcnfg_linkup_rsp(struct cflayer *layer, u8 channel_id, enum cfctrl_srv serv,
- u8 phyid, struct cflayer *adapt_layer)
+ u8 phyid, struct cflayer *adapt_layer)
{
struct cfcnfg *cnfg = container_obj(layer);
struct cflayer *servicel = NULL;
struct cfcnfg_phyinfo *phyinfo;
struct net_device *netdev;
+ rcu_read_lock();
+
if (adapt_layer == NULL) {
- pr_debug("link setup response but no client exist, send linkdown back\n");
+ pr_debug("link setup response but no client exist,"
+ "send linkdown back\n");
cfctrl_linkdown_req(cnfg->ctrl, channel_id, NULL);
- return;
+ goto unlock;
}
caif_assert(cnfg != NULL);
caif_assert(phyid != 0);
- phyinfo = &cnfg->phy_layers[phyid];
+
+ phyinfo = cfcnfg_get_phyinfo_rcu(cnfg, phyid);
+ if (phyinfo == NULL) {
+ pr_err("ERROR: Link Layer Device dissapeared"
+ "while connecting\n");
+ goto unlock;
+ }
+
+ caif_assert(phyinfo != NULL);
caif_assert(phyinfo->id == phyid);
caif_assert(phyinfo->phy_layer != NULL);
caif_assert(phyinfo->phy_layer->id == phyid);
- phyinfo->phy_ref_count++;
- if (phyinfo->phy_ref_count == 1 &&
- phyinfo->phy_layer->modemcmd != NULL) {
- phyinfo->phy_layer->modemcmd(phyinfo->phy_layer,
- _CAIF_MODEMCMD_PHYIF_USEFULL);
- }
adapt_layer->id = channel_id;
switch (serv) {
@@ -348,7 +431,8 @@ cfcnfg_linkup_rsp(struct cflayer *layer, u8 channel_id, enum cfctrl_srv serv,
servicel = cfvei_create(channel_id, &phyinfo->dev_info);
break;
case CFCTRL_SRV_DATAGRAM:
- servicel = cfdgml_create(channel_id, &phyinfo->dev_info);
+ servicel = cfdgml_create(channel_id,
+ &phyinfo->dev_info);
break;
case CFCTRL_SRV_RFM:
netdev = phyinfo->dev_info.dev;
@@ -365,94 +449,93 @@ cfcnfg_linkup_rsp(struct cflayer *layer, u8 channel_id, enum cfctrl_srv serv,
servicel = cfdbgl_create(channel_id, &phyinfo->dev_info);
break;
default:
- pr_err("Protocol error. Link setup response - unknown channel type\n");
- return;
+ pr_err("Protocol error. Link setup response "
+ "- unknown channel type\n");
+ goto unlock;
}
if (!servicel) {
pr_warn("Out of memory\n");
- return;
+ goto unlock;
}
layer_set_dn(servicel, cnfg->mux);
cfmuxl_set_uplayer(cnfg->mux, servicel, channel_id);
layer_set_up(servicel, adapt_layer);
layer_set_dn(adapt_layer, servicel);
- cfsrvl_get(servicel);
+
+ rcu_read_unlock();
+
servicel->ctrlcmd(servicel, CAIF_CTRLCMD_INIT_RSP, 0);
+ return;
+unlock:
+ rcu_read_unlock();
}
void
cfcnfg_add_phy_layer(struct cfcnfg *cnfg, enum cfcnfg_phy_type phy_type,
struct net_device *dev, struct cflayer *phy_layer,
- u16 *phyid, enum cfcnfg_phy_preference pref,
+ enum cfcnfg_phy_preference pref,
bool fcs, bool stx)
{
struct cflayer *frml;
struct cflayer *phy_driver = NULL;
+ struct cfcnfg_phyinfo *phyinfo;
int i;
+ u8 phyid;
+ mutex_lock(&cnfg->lock);
- if (cnfg->phy_layers[cnfg->last_phyid].frm_layer == NULL) {
- *phyid = cnfg->last_phyid;
-
- /* range: * 1..(MAX_PHY_LAYERS-1) */
- cnfg->last_phyid =
- (cnfg->last_phyid % (MAX_PHY_LAYERS - 1)) + 1;
- } else {
- *phyid = 0;
- for (i = 1; i < MAX_PHY_LAYERS; i++) {
- if (cnfg->phy_layers[i].frm_layer == NULL) {
- *phyid = i;
- break;
- }
- }
- }
- if (*phyid == 0) {
- pr_err("No Available PHY ID\n");
- return;
+ /* CAIF protocol allow maximum 6 link-layers */
+ for (i = 0; i < 7; i++) {
+ phyid = (dev->ifindex + i) & 0x7;
+ if (phyid == 0)
+ continue;
+ if (cfcnfg_get_phyinfo_rcu(cnfg, phyid) == NULL)
+ goto got_phyid;
}
+ pr_warn("Too many CAIF Link Layers (max 6)\n");
+ goto out;
+
+got_phyid:
+ phyinfo = kzalloc(sizeof(struct cfcnfg_phyinfo), GFP_ATOMIC);
switch (phy_type) {
case CFPHYTYPE_FRAG:
phy_driver =
- cfserl_create(CFPHYTYPE_FRAG, *phyid, stx);
+ cfserl_create(CFPHYTYPE_FRAG, phyid, stx);
if (!phy_driver) {
pr_warn("Out of memory\n");
- return;
+ goto out;
}
-
break;
case CFPHYTYPE_CAIF:
phy_driver = NULL;
break;
default:
- pr_err("%d\n", phy_type);
- return;
- break;
+ goto out;
}
-
- phy_layer->id = *phyid;
- cnfg->phy_layers[*phyid].pref = pref;
- cnfg->phy_layers[*phyid].id = *phyid;
- cnfg->phy_layers[*phyid].dev_info.id = *phyid;
- cnfg->phy_layers[*phyid].dev_info.dev = dev;
- cnfg->phy_layers[*phyid].phy_layer = phy_layer;
- cnfg->phy_layers[*phyid].phy_ref_count = 0;
- cnfg->phy_layers[*phyid].ifindex = dev->ifindex;
- cnfg->phy_layers[*phyid].use_stx = stx;
- cnfg->phy_layers[*phyid].use_fcs = fcs;
+ phy_layer->id = phyid;
+ phyinfo->pref = pref;
+ phyinfo->id = phyid;
+ phyinfo->dev_info.id = phyid;
+ phyinfo->dev_info.dev = dev;
+ phyinfo->phy_layer = phy_layer;
+ phyinfo->ifindex = dev->ifindex;
+ phyinfo->use_stx = stx;
+ phyinfo->use_fcs = fcs;
phy_layer->type = phy_type;
- frml = cffrml_create(*phyid, fcs);
+ frml = cffrml_create(phyid, fcs);
+
if (!frml) {
pr_warn("Out of memory\n");
- return;
+ kfree(phyinfo);
+ goto out;
}
- cnfg->phy_layers[*phyid].frm_layer = frml;
- cfmuxl_set_dnlayer(cnfg->mux, frml, *phyid);
+ phyinfo->frm_layer = frml;
layer_set_up(frml, cnfg->mux);
if (phy_driver != NULL) {
- phy_driver->id = *phyid;
+ phy_driver->id = phyid;
layer_set_dn(frml, phy_driver);
layer_set_up(phy_driver, frml);
layer_set_dn(phy_driver, phy_layer);
@@ -461,33 +544,95 @@ cfcnfg_add_phy_layer(struct cfcnfg *cnfg, enum cfcnfg_phy_type phy_type,
layer_set_dn(frml, phy_layer);
layer_set_up(phy_layer, frml);
}
+
+ list_add_rcu(&phyinfo->node, &cnfg->phys);
+out:
+ mutex_unlock(&cnfg->lock);
}
EXPORT_SYMBOL(cfcnfg_add_phy_layer);
+int cfcnfg_set_phy_state(struct cfcnfg *cnfg, struct cflayer *phy_layer,
+ bool up)
+{
+ struct cfcnfg_phyinfo *phyinfo;
+
+ rcu_read_lock();
+ phyinfo = cfcnfg_get_phyinfo_rcu(cnfg, phy_layer->id);
+ if (phyinfo == NULL) {
+ rcu_read_unlock();
+ return -ENODEV;
+ }
+
+ if (phyinfo->up == up) {
+ rcu_read_unlock();
+ return 0;
+ }
+ phyinfo->up = up;
+
+ if (up) {
+ cffrml_hold(phyinfo->frm_layer);
+ cfmuxl_set_dnlayer(cnfg->mux, phyinfo->frm_layer,
+ phy_layer->id);
+ } else {
+ cfmuxl_remove_dnlayer(cnfg->mux, phy_layer->id);
+ cffrml_put(phyinfo->frm_layer);
+ }
+
+ rcu_read_unlock();
+ return 0;
+}
+EXPORT_SYMBOL(cfcnfg_set_phy_state);
+
int cfcnfg_del_phy_layer(struct cfcnfg *cnfg, struct cflayer *phy_layer)
{
struct cflayer *frml, *frml_dn;
u16 phyid;
+ struct cfcnfg_phyinfo *phyinfo;
+
+ might_sleep();
+
+ mutex_lock(&cnfg->lock);
+
phyid = phy_layer->id;
- caif_assert(phyid == cnfg->phy_layers[phyid].id);
- caif_assert(phy_layer == cnfg->phy_layers[phyid].phy_layer);
+ phyinfo = cfcnfg_get_phyinfo_rcu(cnfg, phyid);
+
+ if (phyinfo == NULL) {
+ mutex_unlock(&cnfg->lock);
+ return 0;
+ }
+ caif_assert(phyid == phyinfo->id);
+ caif_assert(phy_layer == phyinfo->phy_layer);
caif_assert(phy_layer->id == phyid);
- caif_assert(cnfg->phy_layers[phyid].frm_layer->id == phyid);
+ caif_assert(phyinfo->frm_layer->id == phyid);
- memset(&cnfg->phy_layers[phy_layer->id], 0,
- sizeof(struct cfcnfg_phyinfo));
- frml = cfmuxl_remove_dnlayer(cnfg->mux, phy_layer->id);
+ list_del_rcu(&phyinfo->node);
+ synchronize_rcu();
+
+ /* Fail if reference count is not zero */
+ if (cffrml_refcnt_read(phyinfo->frm_layer) != 0) {
+ pr_info("Wait for device inuse\n");
+ list_add_rcu(&phyinfo->node, &cnfg->phys);
+ mutex_unlock(&cnfg->lock);
+ return -EAGAIN;
+ }
+
+ frml = phyinfo->frm_layer;
frml_dn = frml->dn;
cffrml_set_uplayer(frml, NULL);
cffrml_set_dnlayer(frml, NULL);
- kfree(frml);
-
if (phy_layer != frml_dn) {
layer_set_up(frml_dn, NULL);
layer_set_dn(frml_dn, NULL);
- kfree(frml_dn);
}
layer_set_up(phy_layer, NULL);
+
+ if (phyinfo->phy_layer != frml_dn)
+ kfree(frml_dn);
+
+ cffrml_free(frml);
+ kfree(phyinfo);
+ mutex_unlock(&cnfg->lock);
+
return 0;
}
EXPORT_SYMBOL(cfcnfg_del_phy_layer);
diff --git a/net/caif/cfctrl.c b/net/caif/cfctrl.c
index 3cd8f978e30..0c00a6015dd 100644
--- a/net/caif/cfctrl.c
+++ b/net/caif/cfctrl.c
@@ -17,7 +17,6 @@
#define UTILITY_NAME_LENGTH 16
#define CFPKT_CTRL_PKT_LEN 20
-
#ifdef CAIF_NO_LOOP
static int handle_loop(struct cfctrl *ctrl,
int cmd, struct cfpkt *pkt){
@@ -51,14 +50,31 @@ struct cflayer *cfctrl_create(void)
this->serv.layer.receive = cfctrl_recv;
sprintf(this->serv.layer.name, "ctrl");
this->serv.layer.ctrlcmd = cfctrl_ctrlcmd;
+#ifndef CAIF_NO_LOOP
spin_lock_init(&this->loop_linkid_lock);
+ this->loop_linkid = 1;
+#endif
spin_lock_init(&this->info_list_lock);
INIT_LIST_HEAD(&this->list);
- this->loop_linkid = 1;
return &this->serv.layer;
}
-static bool param_eq(struct cfctrl_link_param *p1, struct cfctrl_link_param *p2)
+void cfctrl_remove(struct cflayer *layer)
+{
+ struct cfctrl_request_info *p, *tmp;
+ struct cfctrl *ctrl = container_obj(layer);
+
+ spin_lock_bh(&ctrl->info_list_lock);
+ list_for_each_entry_safe(p, tmp, &ctrl->list, list) {
+ list_del(&p->list);
+ kfree(p);
+ }
+ spin_unlock_bh(&ctrl->info_list_lock);
+ kfree(layer);
+}
+
+static bool param_eq(const struct cfctrl_link_param *p1,
+ const struct cfctrl_link_param *p2)
{
bool eq =
p1->linktype == p2->linktype &&
@@ -100,8 +116,8 @@ static bool param_eq(struct cfctrl_link_param *p1, struct cfctrl_link_param *p2)
return false;
}
-bool cfctrl_req_eq(struct cfctrl_request_info *r1,
- struct cfctrl_request_info *r2)
+static bool cfctrl_req_eq(const struct cfctrl_request_info *r1,
+ const struct cfctrl_request_info *r2)
{
if (r1->cmd != r2->cmd)
return false;
@@ -112,23 +128,22 @@ bool cfctrl_req_eq(struct cfctrl_request_info *r1,
}
/* Insert request at the end */
-void cfctrl_insert_req(struct cfctrl *ctrl,
+static void cfctrl_insert_req(struct cfctrl *ctrl,
struct cfctrl_request_info *req)
{
- spin_lock(&ctrl->info_list_lock);
+ spin_lock_bh(&ctrl->info_list_lock);
atomic_inc(&ctrl->req_seq_no);
req->sequence_no = atomic_read(&ctrl->req_seq_no);
list_add_tail(&req->list, &ctrl->list);
- spin_unlock(&ctrl->info_list_lock);
+ spin_unlock_bh(&ctrl->info_list_lock);
}
/* Compare and remove request */
-struct cfctrl_request_info *cfctrl_remove_req(struct cfctrl *ctrl,
- struct cfctrl_request_info *req)
+static struct cfctrl_request_info *cfctrl_remove_req(struct cfctrl *ctrl,
+ struct cfctrl_request_info *req)
{
struct cfctrl_request_info *p, *tmp, *first;
- spin_lock(&ctrl->info_list_lock);
first = list_first_entry(&ctrl->list, struct cfctrl_request_info, list);
list_for_each_entry_safe(p, tmp, &ctrl->list, list) {
@@ -144,7 +159,6 @@ struct cfctrl_request_info *cfctrl_remove_req(struct cfctrl *ctrl,
}
p = NULL;
out:
- spin_unlock(&ctrl->info_list_lock);
return p;
}
@@ -154,16 +168,6 @@ struct cfctrl_rsp *cfctrl_get_respfuncs(struct cflayer *layer)
return &this->res;
}
-void cfctrl_set_dnlayer(struct cflayer *this, struct cflayer *dn)
-{
- this->dn = dn;
-}
-
-void cfctrl_set_uplayer(struct cflayer *this, struct cflayer *up)
-{
- this->up = up;
-}
-
static void init_info(struct caif_payload_info *info, struct cfctrl *cfctrl)
{
info->hdr_len = 0;
@@ -188,10 +192,6 @@ void cfctrl_enum_req(struct cflayer *layer, u8 physlinkid)
cfpkt_addbdy(pkt, physlinkid);
ret =
cfctrl->serv.layer.dn->transmit(cfctrl->serv.layer.dn, pkt);
- if (ret < 0) {
- pr_err("Could not transmit enum message\n");
- cfpkt_destroy(pkt);
- }
}
int cfctrl_linkup_request(struct cflayer *layer,
@@ -205,14 +205,23 @@ int cfctrl_linkup_request(struct cflayer *layer,
struct cfctrl_request_info *req;
int ret;
char utility_name[16];
- struct cfpkt *pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN);
+ struct cfpkt *pkt;
+
+ if (cfctrl_cancel_req(layer, user_layer) > 0) {
+ /* Slight Paranoia, check if already connecting */
+ pr_err("Duplicate connect request for same client\n");
+ WARN_ON(1);
+ return -EALREADY;
+ }
+
+ pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN);
if (!pkt) {
pr_warn("Out of memory\n");
return -ENOMEM;
}
cfpkt_addbdy(pkt, CFCTRL_CMD_LINK_SETUP);
- cfpkt_addbdy(pkt, (param->chtype << 4) + param->linktype);
- cfpkt_addbdy(pkt, (param->priority << 3) + param->phyid);
+ cfpkt_addbdy(pkt, (param->chtype << 4) | param->linktype);
+ cfpkt_addbdy(pkt, (param->priority << 3) | param->phyid);
cfpkt_addbdy(pkt, param->endpoint & 0x03);
switch (param->linktype) {
@@ -275,9 +284,13 @@ int cfctrl_linkup_request(struct cflayer *layer,
ret =
cfctrl->serv.layer.dn->transmit(cfctrl->serv.layer.dn, pkt);
if (ret < 0) {
- pr_err("Could not transmit linksetup request\n");
- cfpkt_destroy(pkt);
- return -ENODEV;
+ int count;
+
+ count = cfctrl_cancel_req(&cfctrl->serv.layer,
+ user_layer);
+ if (count != 1)
+ pr_err("Could not remove request (%d)", count);
+ return -ENODEV;
}
return 0;
}
@@ -297,80 +310,29 @@ int cfctrl_linkdown_req(struct cflayer *layer, u8 channelid,
init_info(cfpkt_info(pkt), cfctrl);
ret =
cfctrl->serv.layer.dn->transmit(cfctrl->serv.layer.dn, pkt);
- if (ret < 0) {
- pr_err("Could not transmit link-down request\n");
- cfpkt_destroy(pkt);
- }
+#ifndef CAIF_NO_LOOP
+ cfctrl->loop_linkused[channelid] = 0;
+#endif
return ret;
}
-void cfctrl_sleep_req(struct cflayer *layer)
-{
- int ret;
- struct cfctrl *cfctrl = container_obj(layer);
- struct cfpkt *pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN);
- if (!pkt) {
- pr_warn("Out of memory\n");
- return;
- }
- cfpkt_addbdy(pkt, CFCTRL_CMD_SLEEP);
- init_info(cfpkt_info(pkt), cfctrl);
- ret =
- cfctrl->serv.layer.dn->transmit(cfctrl->serv.layer.dn, pkt);
- if (ret < 0)
- cfpkt_destroy(pkt);
-}
-
-void cfctrl_wake_req(struct cflayer *layer)
-{
- int ret;
- struct cfctrl *cfctrl = container_obj(layer);
- struct cfpkt *pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN);
- if (!pkt) {
- pr_warn("Out of memory\n");
- return;
- }
- cfpkt_addbdy(pkt, CFCTRL_CMD_WAKE);
- init_info(cfpkt_info(pkt), cfctrl);
- ret =
- cfctrl->serv.layer.dn->transmit(cfctrl->serv.layer.dn, pkt);
- if (ret < 0)
- cfpkt_destroy(pkt);
-}
-
-void cfctrl_getstartreason_req(struct cflayer *layer)
-{
- int ret;
- struct cfctrl *cfctrl = container_obj(layer);
- struct cfpkt *pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN);
- if (!pkt) {
- pr_warn("Out of memory\n");
- return;
- }
- cfpkt_addbdy(pkt, CFCTRL_CMD_START_REASON);
- init_info(cfpkt_info(pkt), cfctrl);
- ret =
- cfctrl->serv.layer.dn->transmit(cfctrl->serv.layer.dn, pkt);
- if (ret < 0)
- cfpkt_destroy(pkt);
-}
-
-
-void cfctrl_cancel_req(struct cflayer *layr, struct cflayer *adap_layer)
+int cfctrl_cancel_req(struct cflayer *layr, struct cflayer *adap_layer)
{
struct cfctrl_request_info *p, *tmp;
struct cfctrl *ctrl = container_obj(layr);
- spin_lock(&ctrl->info_list_lock);
+ int found = 0;
+ spin_lock_bh(&ctrl->info_list_lock);
list_for_each_entry_safe(p, tmp, &ctrl->list, list) {
if (p->client_layer == adap_layer) {
- pr_debug("cancel req :%d\n", p->sequence_no);
list_del(&p->list);
kfree(p);
+ found++;
}
}
- spin_unlock(&ctrl->info_list_lock);
+ spin_unlock_bh(&ctrl->info_list_lock);
+ return found;
}
static int cfctrl_recv(struct cflayer *layer, struct cfpkt *pkt)
@@ -522,6 +484,7 @@ static int cfctrl_recv(struct cflayer *layer, struct cfpkt *pkt)
rsp.cmd = cmd;
rsp.param = linkparam;
+ spin_lock_bh(&cfctrl->info_list_lock);
req = cfctrl_remove_req(cfctrl, &rsp);
if (CFCTRL_ERR_BIT == (CFCTRL_ERR_BIT & cmdrsp) ||
@@ -541,6 +504,8 @@ static int cfctrl_recv(struct cflayer *layer, struct cfpkt *pkt)
if (req != NULL)
kfree(req);
+
+ spin_unlock_bh(&cfctrl->info_list_lock);
}
break;
case CFCTRL_CMD_LINK_DESTROY:
@@ -584,12 +549,29 @@ static void cfctrl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
switch (ctrl) {
case _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND:
case CAIF_CTRLCMD_FLOW_OFF_IND:
- spin_lock(&this->info_list_lock);
+ spin_lock_bh(&this->info_list_lock);
if (!list_empty(&this->list)) {
pr_debug("Received flow off in control layer\n");
}
- spin_unlock(&this->info_list_lock);
+ spin_unlock_bh(&this->info_list_lock);
break;
+ case _CAIF_CTRLCMD_PHYIF_DOWN_IND: {
+ struct cfctrl_request_info *p, *tmp;
+
+ /* Find all connect request and report failure */
+ spin_lock_bh(&this->info_list_lock);
+ list_for_each_entry_safe(p, tmp, &this->list, list) {
+ if (p->param.phyid == phyid) {
+ list_del(&p->list);
+ p->client_layer->ctrlcmd(p->client_layer,
+ CAIF_CTRLCMD_INIT_FAIL_RSP,
+ phyid);
+ kfree(p);
+ }
+ }
+ spin_unlock_bh(&this->info_list_lock);
+ break;
+ }
default:
break;
}
@@ -599,27 +581,33 @@ static void cfctrl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
static int handle_loop(struct cfctrl *ctrl, int cmd, struct cfpkt *pkt)
{
static int last_linkid;
+ static int dec;
u8 linkid, linktype, tmp;
switch