Skip to content

Commit 5dba9fa

Browse files
committed
pinctrl: Convert the put_ARP/ND/FDB actions to be lockless.
The put_ARP/ND/FDB were bound to pinctrl lock to prevent data races as they have used shared hmap to store the data that were later on added into SB. This is problematic in situations when main thread holds the lock and we have to process any put_ARP/ND/FDB packet. That contention would block the pinctrl thread from processing the incoming packets. The same can happen from the opposite side, pinctrl could be holding a lock, delaying main thread from processing other updates. To prevent that use the hmap only by main thread, this will ensure that we wait properly for the multicast timeout to expire and at the same time to do the deduplication. Once the entry arrives it will be shared by thread-safe ring buffer and consumed by main thread. Reported-at: https://redhat.atlassian.net/browse/FDP-3924 Signed-off-by: Ales Musil <amusil@redhat.com>
1 parent 65f22df commit 5dba9fa

1 file changed

Lines changed: 98 additions & 70 deletions

File tree

controller/pinctrl.c

Lines changed: 98 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
#include "ovn-sb-idl.h"
6666
#include "ovn-dns.h"
6767
#include "garp_rarp.h"
68+
#include "spsc-ring.h"
6869

6970
VLOG_DEFINE_THIS_MODULE(pinctrl);
7071

@@ -83,24 +84,31 @@ VLOG_DEFINE_THIS_MODULE(pinctrl);
8384
* and pinctrl_run().
8485
*
8586
*
86-
* - put_arp/put_nd - These actions stores the IPv4/IPv6 and MAC addresses
87+
* - put_arp/put_nd - These actions store the IPv4/IPv6 and MAC addresses
8788
* in the 'MAC_Binding' table.
8889
* The function 'pinctrl_handle_put_mac_binding()' (which
89-
* is called with in the pinctrl_handler thread), stores
90-
* the IPv4/IPv6 and MAC addresses in the
91-
* hmap - put_mac_bindings.
90+
* is called within the pinctrl_handler thread), pushes
91+
* the IPv4/IPv6 and MAC addresses directly into a
92+
* lock-free SPSC ring buffer ('mac_bindings_ring').
93+
* This does not require pinctrl_mutex. For multicast
94+
* replies, a special cookie value
95+
* (MAC_BINDINGS_MC_COOKIE) is set to signal the main
96+
* thread to apply a random delay.
9297
*
93-
* pinctrl_run(), reads these mac bindings from the hmap
94-
* 'put_mac_bindings' and writes to the 'MAC_Binding'
95-
* table in the Southbound DB.
98+
* pinctrl_run() drains the ring in
99+
* run_put_mac_bindings(), inserts entries into the
100+
* main-thread-only hmap 'put_mac_bindings' (applying
101+
* a random delay for multicast replies to avoid
102+
* thundering herd), and writes expired entries to the
103+
* 'MAC_Binding' table in the Southbound DB.
96104
*
97105
* - arp/nd_ns - These actions generate an ARP/IPv6 Neighbor solicit
98106
* requests. The original packets are buffered and
99107
* injected back when put_arp/put_nd resolves
100108
* corresponding ARP/IPv6 Neighbor solicit requests.
101-
* When pinctrl_run(), writes the mac bindings from the
102-
* 'put_mac_bindings' hmap to the MAC_Binding table in
103-
* SB DB, run_buffered_binding will add the buffered
109+
* When pinctrl_run() writes the mac bindings from the
110+
* ring buffer to the MAC_Binding table in SB DB,
111+
* run_buffered_binding will add the buffered
104112
* packets to buffered_mac_bindings and notify
105113
* pinctrl_handler.
106114
*
@@ -197,16 +205,14 @@ run_buffered_binding(const struct sbrec_mac_binding_table *mac_binding_table,
197205

198206
static void pinctrl_handle_put_mac_binding(const struct flow *md,
199207
const struct flow *headers,
200-
bool is_arp)
201-
OVS_REQUIRES(pinctrl_mutex);
202-
static void init_put_mac_bindings(void);
203-
static void destroy_put_mac_bindings(void);
208+
bool is_arp);
209+
static void init_mac_bindings(void);
210+
static void destroy_mac_bindings(void);
204211
static void run_put_mac_bindings(
205212
struct ovsdb_idl_txn *ovnsb_idl_txn,
206213
struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
207214
struct ovsdb_idl_index *sbrec_port_binding_by_key,
208-
struct ovsdb_idl_index *sbrec_mac_binding_by_lport_ip)
209-
OVS_REQUIRES(pinctrl_mutex);
215+
struct ovsdb_idl_index *sbrec_mac_binding_by_lport_ip);
210216
static void wait_put_mac_bindings(void);
211217
static void send_mac_binding_buffered_pkts(struct rconn *swconn);
212218

@@ -366,18 +372,15 @@ static void run_put_fdb(struct ovsdb_idl_txn *ovnsb_idl_txn,
366372
struct ovsdb_idl_index *sbrec_fdb_by_dp_key_mac,
367373
struct ovsdb_idl_index *sbrec_port_binding_by_key,
368374
struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
369-
struct fdb *fdb, uint64_t cur_cfg)
370-
OVS_REQUIRES(pinctrl_mutex);
375+
struct fdb *fdb, uint64_t cur_cfg);
371376
static void run_put_fdbs(struct ovsdb_idl_txn *ovnsb_idl_txn,
372377
struct ovsdb_idl_index *sbrec_port_binding_by_key,
373378
struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
374379
struct ovsdb_idl_index *sbrec_fdb_by_dp_key_mac,
375-
uint64_t cur_cfg)
376-
OVS_REQUIRES(pinctrl_mutex);
380+
uint64_t cur_cfg);
377381
static void wait_put_fdbs(void);
378382
static void pinctrl_handle_put_fdb(const struct flow *md,
379-
const struct flow *headers)
380-
OVS_REQUIRES(pinctrl_mutex);
383+
const struct flow *headers);
381384

382385
static void set_from_ctrl_flag_in_pkt_metadata(struct ofputil_packet_in *);
383386

@@ -556,7 +559,7 @@ controller_event_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
556559
void
557560
pinctrl_init(void)
558561
{
559-
init_put_mac_bindings();
562+
init_mac_bindings();
560563
init_send_arps_nds();
561564
init_ipv6_ras();
562565
init_ipv6_prefixd();
@@ -3776,10 +3779,8 @@ process_packet_in(struct rconn *swconn, const struct ofp_header *msg)
37763779
break;
37773780

37783781
case ACTION_OPCODE_PUT_ARP:
3779-
ovs_mutex_lock(&pinctrl_mutex);
37803782
pinctrl_handle_put_mac_binding(&pin.flow_metadata.flow, &headers,
37813783
true);
3782-
ovs_mutex_unlock(&pinctrl_mutex);
37833784
break;
37843785

37853786
case ACTION_OPCODE_DHCP_RELAY_REQ_CHK:
@@ -3808,16 +3809,12 @@ process_packet_in(struct rconn *swconn, const struct ofp_header *msg)
38083809
break;
38093810

38103811
case ACTION_OPCODE_PUT_ND:
3811-
ovs_mutex_lock(&pinctrl_mutex);
38123812
pinctrl_handle_put_mac_binding(&pin.flow_metadata.flow, &headers,
38133813
false);
3814-
ovs_mutex_unlock(&pinctrl_mutex);
38153814
break;
38163815

38173816
case ACTION_OPCODE_PUT_FDB:
3818-
ovs_mutex_lock(&pinctrl_mutex);
38193817
pinctrl_handle_put_fdb(&pin.flow_metadata.flow, &headers);
3820-
ovs_mutex_unlock(&pinctrl_mutex);
38213818
break;
38223819

38233820
case ACTION_OPCODE_PUT_DHCPV6_OPTS:
@@ -4757,7 +4754,7 @@ pinctrl_destroy(void)
47574754
destroy_buffered_packets_map();
47584755
destroy_activated_ports();
47594756
event_table_destroy();
4760-
destroy_put_mac_bindings();
4757+
destroy_mac_bindings();
47614758
destroy_put_vport_bindings();
47624759
ip_mcast_snoop_destroy();
47634760
destroy_svc_monitors();
@@ -4782,37 +4779,42 @@ pinctrl_destroy(void)
47824779

47834780
#define MAX_MAC_BINDING_DELAY_MSEC 50
47844781
#define MAX_FDB_DELAY_MSEC 50
4785-
#define MAX_MAC_BINDINGS 1000
4782+
#define MAX_MAC_BINDINGS 1024
4783+
#define MAC_BINDINGS_MC_COOKIE 0xff
4784+
47864785

4787-
/* Contains "struct mac_binding"s. */
4786+
/* Contains "struct mac_binding"s. This is used by main thread only. */
47884787
static struct hmap put_mac_bindings;
4788+
/* Contains "struct mac_binding_data". This is populated by pinctrl thread
4789+
* and consumed by main thread. */
4790+
static struct spsc_ring mac_bindings_ring;
47894791

47904792
static void
4791-
init_put_mac_bindings(void)
4793+
init_mac_bindings(void)
47924794
{
47934795
hmap_init(&put_mac_bindings);
4796+
spsc_ring_init(&mac_bindings_ring, MAX_MAC_BINDINGS,
4797+
sizeof (struct mac_binding_data));
47944798
}
47954799

47964800
static void
4797-
destroy_put_mac_bindings(void)
4801+
destroy_mac_bindings(void)
47984802
{
47994803
mac_bindings_clear(&put_mac_bindings);
48004804
hmap_destroy(&put_mac_bindings);
4805+
spsc_ring_destroy(&mac_bindings_ring);
48014806
}
48024807

48034808
/* Called with in the pinctrl_handler thread context. */
48044809
static void
48054810
pinctrl_handle_put_mac_binding(const struct flow *md,
48064811
const struct flow *headers,
48074812
bool is_arp)
4808-
OVS_REQUIRES(pinctrl_mutex)
48094813
{
4810-
if (hmap_count(&put_mac_bindings) >= MAX_MAC_BINDINGS) {
4811-
COVERAGE_INC(pinctrl_drop_put_mac_binding);
4812-
return;
4813-
}
4814-
48154814
struct mac_binding_data mb_data = (struct mac_binding_data) {
4815+
.cookie = eth_addr_is_multicast(headers->dl_dst)
4816+
? MAC_BINDINGS_MC_COOKIE
4817+
: 0,
48164818
.dp_key = ntohll(md->metadata),
48174819
.port_key = md->regs[MFF_LOG_INPORT - MFF_REG0],
48184820
.mac = headers->dl_src,
@@ -4825,18 +4827,13 @@ pinctrl_handle_put_mac_binding(const struct flow *md,
48254827
memcpy(&mb_data.ip, &ip6, sizeof mb_data.ip);
48264828
}
48274829

4828-
/* If the ARP reply was unicast we should not delay it,
4829-
* there won't be any race. */
4830-
uint32_t delay = eth_addr_is_multicast(headers->dl_dst)
4831-
? random_range(MAX_MAC_BINDING_DELAY_MSEC) + 1
4832-
: 0;
4833-
long long timestamp = time_msec() + delay;
4834-
mac_binding_add(&put_mac_bindings, mb_data, NULL, timestamp);
4835-
4836-
/* We can send the buffered packet once the main ovn-controller
4837-
* thread calls pinctrl_run() and it writes the mac_bindings stored
4838-
* in 'put_mac_bindings' hmap into the Southbound MAC_Binding table. */
4839-
notify_pinctrl_main();
4830+
if (!spsc_ring_push(&mac_bindings_ring, &mb_data)) {
4831+
/* This shouldn't happen, the main thread drains the ring buffer
4832+
* every iteration. */
4833+
COVERAGE_INC(pinctrl_drop_put_mac_binding);
4834+
} else {
4835+
notify_pinctrl_main();
4836+
}
48404837
}
48414838

48424839
/* Called with in the pinctrl_handler thread context. */
@@ -4898,14 +4895,32 @@ run_put_mac_bindings(struct ovsdb_idl_txn *ovnsb_idl_txn,
48984895
struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
48994896
struct ovsdb_idl_index *sbrec_port_binding_by_key,
49004897
struct ovsdb_idl_index *sbrec_mac_binding_by_lport_ip)
4901-
OVS_REQUIRES(pinctrl_mutex)
49024898
{
4899+
long long now = time_msec();
4900+
4901+
struct mac_binding_data mb_data;
4902+
SPSC_RING_FOR_EACH_POP (&mac_bindings_ring, mb_data) {
4903+
if (hmap_count(&put_mac_bindings) >= MAX_MAC_BINDINGS) {
4904+
COVERAGE_INC(pinctrl_drop_put_mac_binding);
4905+
continue;
4906+
}
4907+
4908+
/* If the ARP reply was unicast we should not delay it,
4909+
* there won't be any race. */
4910+
uint32_t delay = mb_data.cookie == MAC_BINDINGS_MC_COOKIE
4911+
? random_range(MAX_MAC_BINDING_DELAY_MSEC) + 1
4912+
: 0;
4913+
/* Clear the cookie, it's needed just to know if it was delayed or
4914+
* not. */
4915+
mb_data.cookie = 0;
4916+
4917+
mac_binding_add(&put_mac_bindings, mb_data, NULL, now + delay);
4918+
}
4919+
49034920
if (!ovnsb_idl_txn) {
49044921
return;
49054922
}
49064923

4907-
long long now = time_msec();
4908-
49094924
struct mac_binding *mb;
49104925
HMAP_FOR_EACH_SAFE (mb, hmap_node, &put_mac_bindings) {
49114926
if (now >= mb->timestamp) {
@@ -4986,7 +5001,6 @@ run_buffered_binding(const struct sbrec_mac_binding_table *mac_binding_table,
49865001

49875002
static void
49885003
wait_put_mac_bindings(void)
4989-
OVS_REQUIRES(pinctrl_mutex)
49905004
{
49915005
struct mac_binding *mb;
49925006
HMAP_FOR_EACH (mb, hmap_node, &put_mac_bindings) {
@@ -8878,9 +8892,13 @@ pinctrl_split_buf_action_handler(struct rconn *swconn, struct dp_packet *pkt,
88788892
ofpbuf_uninit(&ofpacts);
88798893
}
88808894

8881-
#define MAX_FDB_ENTRIES 1000
8895+
#define MAX_FDB_ENTRIES 1024
88828896

8897+
/* Contains "struct fdb"s. This is used by main thread only. */
88838898
static struct hmap put_fdbs;
8899+
/* Contains "struct fdb_data". This is populated by pinctrl thread
8900+
* and consumed by main thread. */
8901+
static struct spsc_ring fdbs_ring;
88848902

88858903
/* MAC learning (fdb) related functions. Runs within the main
88868904
* ovn-controller thread context. */
@@ -8889,13 +8907,16 @@ static void
88898907
init_fdb_entries(void)
88908908
{
88918909
hmap_init(&put_fdbs);
8910+
spsc_ring_init(&fdbs_ring, MAX_FDB_ENTRIES,
8911+
sizeof (struct fdb_data));
88928912
}
88938913

88948914
static void
88958915
destroy_fdb_entries(void)
88968916
{
88978917
fdbs_clear(&put_fdbs);
88988918
hmap_destroy(&put_fdbs);
8919+
spsc_ring_destroy(&fdbs_ring);
88998920
}
89008921

89018922
static const struct sbrec_fdb *
@@ -8975,13 +8996,24 @@ run_put_fdbs(struct ovsdb_idl_txn *ovnsb_idl_txn,
89758996
struct ovsdb_idl_index *sbrec_port_binding_by_key,
89768997
struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
89778998
struct ovsdb_idl_index *sbrec_fdb_by_dp_key_mac, uint64_t cur_cfg)
8978-
OVS_REQUIRES(pinctrl_mutex)
89798999
{
9000+
long long now = time_msec();
9001+
9002+
struct fdb_data fdb_data;
9003+
SPSC_RING_FOR_EACH_POP (&fdbs_ring, fdb_data) {
9004+
if (hmap_count(&put_fdbs) >= MAX_FDB_ENTRIES) {
9005+
COVERAGE_INC(pinctrl_drop_put_fdb);
9006+
continue;
9007+
}
9008+
9009+
uint32_t delay = random_range(MAX_FDB_DELAY_MSEC) + 1;
9010+
fdb_add(&put_fdbs, fdb_data, now + delay);
9011+
}
9012+
89809013
if (!ovnsb_idl_txn) {
89819014
return;
89829015
}
89839016

8984-
long long now = time_msec();
89859017
struct fdb *fdb;
89869018
HMAP_FOR_EACH_SAFE (fdb, hmap_node, &put_fdbs) {
89879019
if (fdb->cfg >= 0 && pinctrl_is_sb_commited(fdb->cfg, cur_cfg)) {
@@ -8997,7 +9029,6 @@ run_put_fdbs(struct ovsdb_idl_txn *ovnsb_idl_txn,
89979029

89989030
static void
89999031
wait_put_fdbs(void)
9000-
OVS_REQUIRES(pinctrl_mutex)
90019032
{
90029033
struct fdb *fdb;
90039034
HMAP_FOR_EACH (fdb, hmap_node, &put_fdbs) {
@@ -9008,23 +9039,20 @@ wait_put_fdbs(void)
90089039
/* Called with in the pinctrl_handler thread context. */
90099040
static void
90109041
pinctrl_handle_put_fdb(const struct flow *md, const struct flow *headers)
9011-
OVS_REQUIRES(pinctrl_mutex)
90129042
{
9013-
if (hmap_count(&put_fdbs) >= MAX_FDB_ENTRIES) {
9014-
COVERAGE_INC(pinctrl_drop_put_fdb);
9015-
return;
9016-
}
9017-
90189043
struct fdb_data fdb_data = (struct fdb_data) {
90199044
.dp_key = ntohll(md->metadata),
90209045
.port_key = md->regs[MFF_LOG_INPORT - MFF_REG0],
90219046
.mac = headers->dl_src,
90229047
};
90239048

9024-
uint32_t delay = random_range(MAX_FDB_DELAY_MSEC) + 1;
9025-
long long timestamp = time_msec() + delay;
9026-
fdb_add(&put_fdbs, fdb_data, timestamp);
9027-
notify_pinctrl_main();
9049+
if (!spsc_ring_push(&fdbs_ring, &fdb_data)) {
9050+
/* This shouldn't happen, the main thread drains the ring buffer
9051+
* every iteration. */
9052+
COVERAGE_INC(pinctrl_drop_put_fdb);
9053+
} else {
9054+
notify_pinctrl_main();
9055+
}
90289056
}
90299057

90309058
/* This function sets the register bit 'MLF_FROM_CTRL_BIT'

0 commit comments

Comments
 (0)