... | ... | ||
---|---|---|---|
57 | where the external address is advertised and multiple connections | 57 | where the external address is advertised and multiple connections |
58 | already exist, multiple subflow SYNs arrive in parallel which tends to | 58 | already exist, multiple subflow SYNs arrive in parallel which tends to |
59 | trigger the race during creation of the first local_addr_list entries | 59 | trigger the race during creation of the first local_addr_list entries |
60 | which have the internal address instead. | 60 | which have the internal address instead. |
61 | 61 | ||
62 | Fix this problem by switching mptcp_pm_nl_append_new_local_addr to use | 62 | Fix by skipping the replacement of an existing implicit local address if |
63 | call_rcu . As part of plumbing this up, make | 63 | called via mptcp_pm_nl_get_local_id. |
64 | __mptcp_pm_release_addr_entry take a rcu_head which is used by all | ||
65 | callers regardless of cleanup method. | ||
66 | 64 | ||
67 | Cc: stable@vger.kernel.org | 65 | Cc: stable@vger.kernel.org |
68 | Fixes: d045b9eb95a9 ("mptcp: introduce implicit endpoints") | 66 | Fixes: d045b9eb95a9 ("mptcp: introduce implicit endpoints") |
67 | Suggested-by: Paolo Abeni <pabeni@redhat.com> | ||
69 | Signed-off-by: Krister Johansen <kjlx@templeofstupid.com> | 68 | Signed-off-by: Krister Johansen <kjlx@templeofstupid.com> |
70 | --- | 69 | --- |
71 | net/mptcp/pm_netlink.c | 19 ++++++++++++------- | 70 | v2: |
72 | net/mptcp/protocol.h | 1 + | 71 | - Switch from call_rcu to skipping replacement if invoked via |
73 | 2 files changed, 13 insertions(+), 7 deletions(-) | 72 | mptcp_pm_nl_get_local_id. (Feedback from Paolo Abeni) |
73 | --- | ||
74 | net/mptcp/pm_netlink.c | 18 +++++++++++++++--- | ||
75 | 1 file changed, 15 insertions(+), 3 deletions(-) | ||
74 | 76 | ||
75 | diff --git a/net/mptcp/pm_netlink.c b/net/mptcp/pm_netlink.c | 77 | diff --git a/net/mptcp/pm_netlink.c b/net/mptcp/pm_netlink.c |
76 | index XXXXXXX..XXXXXXX 100644 | 78 | index XXXXXXX..XXXXXXX 100644 |
77 | --- a/net/mptcp/pm_netlink.c | 79 | --- a/net/mptcp/pm_netlink.c |
78 | +++ b/net/mptcp/pm_netlink.c | 80 | +++ b/net/mptcp/pm_netlink.c |
79 | @@ -XXX,XX +XXX,XX @@ static bool address_use_port(struct mptcp_pm_addr_entry *entry) | 81 | @@ -XXX,XX +XXX,XX @@ static void __mptcp_pm_release_addr_entry(struct mptcp_pm_addr_entry *entry) |
80 | MPTCP_PM_ADDR_FLAG_SIGNAL; | 82 | |
81 | } | 83 | static int mptcp_pm_nl_append_new_local_addr(struct pm_nl_pernet *pernet, |
82 | 84 | struct mptcp_pm_addr_entry *entry, | |
83 | -/* caller must ensure the RCU grace period is already elapsed */ | 85 | - bool needs_id) |
84 | -static void __mptcp_pm_release_addr_entry(struct mptcp_pm_addr_entry *entry) | 86 | + bool needs_id, bool replace) |
85 | +/* | ||
86 | + * Caller must ensure the RCU grace period is already elapsed or call this | ||
87 | + * via a RCU callback. | ||
88 | + */ | ||
89 | +static void __mptcp_pm_release_addr_entry(struct rcu_head *head) | ||
90 | { | 87 | { |
91 | + struct mptcp_pm_addr_entry *entry; | 88 | struct mptcp_pm_addr_entry *cur, *del_entry = NULL; |
89 | unsigned int addr_max; | ||
90 | @@ -XXX,XX +XXX,XX @@ static int mptcp_pm_nl_append_new_local_addr(struct pm_nl_pernet *pernet, | ||
91 | if (entry->addr.id) | ||
92 | goto out; | ||
93 | |||
94 | + /* allow callers that only need to look up the local | ||
95 | + * addr's id to skip replacement. This allows them to | ||
96 | + * avoid calling synchronize_rcu in the packet recv | ||
97 | + * path. | ||
98 | + */ | ||
99 | + if (!replace) { | ||
100 | + kfree(entry); | ||
101 | + ret = cur->addr.id; | ||
102 | + goto out; | ||
103 | + } | ||
92 | + | 104 | + |
93 | + entry = container_of(head, struct mptcp_pm_addr_entry, rcu_head); | 105 | pernet->addrs--; |
94 | if (entry->lsk) | 106 | entry->addr.id = cur->addr.id; |
95 | sock_release(entry->lsk); | 107 | list_del_rcu(&cur->list); |
96 | kfree(entry); | 108 | @@ -XXX,XX +XXX,XX @@ int mptcp_pm_nl_get_local_id(struct mptcp_sock *msk, struct mptcp_addr_info *skc |
97 | @@ -XXX,XX +XXX,XX @@ static int mptcp_pm_nl_append_new_local_addr(struct pm_nl_pernet *pernet, | 109 | entry->ifindex = 0; |
98 | 110 | entry->flags = MPTCP_PM_ADDR_FLAG_IMPLICIT; | |
99 | /* just replaced an existing entry, free it */ | 111 | entry->lsk = NULL; |
100 | if (del_entry) { | 112 | - ret = mptcp_pm_nl_append_new_local_addr(pernet, entry, true); |
101 | - synchronize_rcu(); | 113 | + ret = mptcp_pm_nl_append_new_local_addr(pernet, entry, true, false); |
102 | - __mptcp_pm_release_addr_entry(del_entry); | 114 | if (ret < 0) |
103 | + call_rcu(&del_entry->rcu_head, __mptcp_pm_release_addr_entry); | 115 | kfree(entry); |
116 | |||
117 | @@ -XXX,XX +XXX,XX @@ int mptcp_pm_nl_add_addr_doit(struct sk_buff *skb, struct genl_info *info) | ||
118 | } | ||
104 | } | 119 | } |
105 | return ret; | 120 | ret = mptcp_pm_nl_append_new_local_addr(pernet, entry, |
106 | } | 121 | - !mptcp_pm_has_addr_attr_id(attr, info)); |
107 | @@ -XXX,XX +XXX,XX @@ int mptcp_pm_nl_add_addr_doit(struct sk_buff *skb, struct genl_info *info) | 122 | + !mptcp_pm_has_addr_attr_id(attr, info), |
108 | return 0; | 123 | + true); |
109 | 124 | if (ret < 0) { | |
110 | out_free: | 125 | GENL_SET_ERR_MSG_FMT(info, "too many addresses or duplicate one: %d", ret); |
111 | - __mptcp_pm_release_addr_entry(entry); | 126 | goto out_free; |
112 | + __mptcp_pm_release_addr_entry(&entry->rcu_head); | 127 | |
113 | return ret; | 128 | base-commit: 384fa1d90d092d36bfe13c0473194120ce28a50e |
114 | } | ||
115 | |||
116 | @@ -XXX,XX +XXX,XX @@ int mptcp_pm_nl_del_addr_doit(struct sk_buff *skb, struct genl_info *info) | ||
117 | |||
118 | mptcp_nl_remove_subflow_and_signal_addr(sock_net(skb->sk), entry); | ||
119 | synchronize_rcu(); | ||
120 | - __mptcp_pm_release_addr_entry(entry); | ||
121 | + __mptcp_pm_release_addr_entry(&entry->rcu_head); | ||
122 | |||
123 | return ret; | ||
124 | } | ||
125 | @@ -XXX,XX +XXX,XX @@ static void __flush_addrs(struct list_head *list) | ||
126 | cur = list_entry(list->next, | ||
127 | struct mptcp_pm_addr_entry, list); | ||
128 | list_del_rcu(&cur->list); | ||
129 | - __mptcp_pm_release_addr_entry(cur); | ||
130 | + __mptcp_pm_release_addr_entry(&cur->rcu_head); | ||
131 | } | ||
132 | } | ||
133 | |||
134 | diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h | ||
135 | index XXXXXXX..XXXXXXX 100644 | ||
136 | --- a/net/mptcp/protocol.h | ||
137 | +++ b/net/mptcp/protocol.h | ||
138 | @@ -XXX,XX +XXX,XX @@ struct mptcp_pm_addr_entry { | ||
139 | u8 flags; | ||
140 | int ifindex; | ||
141 | struct socket *lsk; | ||
142 | + struct rcu_head rcu_head; | ||
143 | }; | ||
144 | |||
145 | struct mptcp_data_frag { | ||
146 | -- | 129 | -- |
147 | 2.25.1 | 130 | 2.25.1 | diff view generated by jsdifflib |