aboutsummaryrefslogtreecommitdiffstats
path: root/net/tipc
diff options
context:
space:
mode:
authorYing Xue <ying.xue@windriver.com>2015-05-03 22:36:46 -0400
committerDavid S. Miller <davem@davemloft.net>2015-05-04 15:04:01 -0400
commit00bc00a9384c306cdd48611a53b955d936349bf6 (patch)
treeb6e9a41331a0fa5b401fd6c3727ed11bf065b0fc /net/tipc
parent1b764828add9feaa18a8f916a79b954ac8a20a73 (diff)
tipc: involve reference counter for subscriber
At present subscriber's lock is used to protect the subscription list of subscriber as well as subscriptions linked into the list. While one or all subscriptions are deleted through iterating the list, the subscriber's lock must be held. Meanwhile, as deletion of subscription may happen in subscription timer's handler, the lock must be grabbed in the function as well. When subscription's timer is terminated with del_timer_sync() during above iteration, subscriber's lock has to be temporarily released, otherwise, deadlock may occur. However, the temporary release may cause the double free of a subscription as the subscription is not disconnected from the subscription list. Now if a reference counter is introduced to subscriber, subscription's timer can be asynchronously stopped with del_timer(). As a result, the issue is not only able to be fixed, but also relevant code is pretty readable and understandable. Signed-off-by: Ying Xue <ying.xue@windriver.com> Reviewed-by: Jon Maloy <jon.maloy@ericson.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/tipc')
-rw-r--r--net/tipc/subscr.c120
1 files changed, 52 insertions, 68 deletions
diff --git a/net/tipc/subscr.c b/net/tipc/subscr.c
index d0dbde420540..9b24c9c7e05a 100644
--- a/net/tipc/subscr.c
+++ b/net/tipc/subscr.c
@@ -40,16 +40,21 @@
40 40
41/** 41/**
42 * struct tipc_subscriber - TIPC network topology subscriber 42 * struct tipc_subscriber - TIPC network topology subscriber
43 * @kref: reference counter to tipc_subscription object
43 * @conid: connection identifier to server connecting to subscriber 44 * @conid: connection identifier to server connecting to subscriber
44 * @lock: control access to subscriber 45 * @lock: control access to subscriber
45 * @subscrp_list: list of subscription objects for this subscriber 46 * @subscrp_list: list of subscription objects for this subscriber
46 */ 47 */
47struct tipc_subscriber { 48struct tipc_subscriber {
49 struct kref kref;
48 int conid; 50 int conid;
49 spinlock_t lock; 51 spinlock_t lock;
50 struct list_head subscrp_list; 52 struct list_head subscrp_list;
51}; 53};
52 54
55static void tipc_subscrp_delete(struct tipc_subscription *sub);
56static void tipc_subscrb_put(struct tipc_subscriber *subscriber);
57
53/** 58/**
54 * htohl - convert value to endianness used by destination 59 * htohl - convert value to endianness used by destination
55 * @in: value to convert 60 * @in: value to convert
@@ -116,42 +121,34 @@ static void tipc_subscrp_timeout(unsigned long data)
116{ 121{
117 struct tipc_subscription *sub = (struct tipc_subscription *)data; 122 struct tipc_subscription *sub = (struct tipc_subscription *)data;
118 struct tipc_subscriber *subscriber = sub->subscriber; 123 struct tipc_subscriber *subscriber = sub->subscriber;
119 struct tipc_net *tn = net_generic(sub->net, tipc_net_id);
120
121 /* The spin lock per subscriber is used to protect its members */
122 spin_lock_bh(&subscriber->lock);
123
124 /* Validate timeout (in case subscription is being cancelled) */
125 if (sub->timeout == TIPC_WAIT_FOREVER) {
126 spin_unlock_bh(&subscriber->lock);
127 return;
128 }
129
130 /* Unlink subscription from name table */
131 tipc_nametbl_unsubscribe(sub);
132
133 /* Unlink subscription from subscriber */
134 list_del(&sub->subscrp_list);
135
136 spin_unlock_bh(&subscriber->lock);
137 124
138 /* Notify subscriber of timeout */ 125 /* Notify subscriber of timeout */
139 tipc_subscrp_send_event(sub, sub->evt.s.seq.lower, sub->evt.s.seq.upper, 126 tipc_subscrp_send_event(sub, sub->evt.s.seq.lower, sub->evt.s.seq.upper,
140 TIPC_SUBSCR_TIMEOUT, 0, 0); 127 TIPC_SUBSCR_TIMEOUT, 0, 0);
141 128
142 /* Now destroy subscription */ 129 spin_lock_bh(&subscriber->lock);
143 kfree(sub); 130 tipc_subscrp_delete(sub);
144 atomic_dec(&tn->subscription_count); 131 spin_unlock_bh(&subscriber->lock);
132
133 tipc_subscrb_put(subscriber);
145} 134}
146 135
147static void tipc_subscrp_delete(struct tipc_subscription *sub) 136static void tipc_subscrb_kref_release(struct kref *kref)
148{ 137{
149 struct tipc_net *tn = net_generic(sub->net, tipc_net_id); 138 struct tipc_subscriber *subcriber = container_of(kref,
139 struct tipc_subscriber, kref);
150 140
151 tipc_nametbl_unsubscribe(sub); 141 kfree(subcriber);
152 list_del(&sub->subscrp_list); 142}
153 kfree(sub); 143
154 atomic_dec(&tn->subscription_count); 144static void tipc_subscrb_put(struct tipc_subscriber *subscriber)
145{
146 kref_put(&subscriber->kref, tipc_subscrb_kref_release);
147}
148
149static void tipc_subscrb_get(struct tipc_subscriber *subscriber)
150{
151 kref_get(&subscriber->kref);
155} 152}
156 153
157static struct tipc_subscriber *tipc_subscrb_create(int conid) 154static struct tipc_subscriber *tipc_subscrb_create(int conid)
@@ -163,6 +160,7 @@ static struct tipc_subscriber *tipc_subscrb_create(int conid)
163 pr_warn("Subscriber rejected, no memory\n"); 160 pr_warn("Subscriber rejected, no memory\n");
164 return NULL; 161 return NULL;
165 } 162 }
163 kref_init(&subscriber->kref);
166 INIT_LIST_HEAD(&subscriber->subscrp_list); 164 INIT_LIST_HEAD(&subscriber->subscrp_list);
167 subscriber->conid = conid; 165 subscriber->conid = conid;
168 spin_lock_init(&subscriber->lock); 166 spin_lock_init(&subscriber->lock);
@@ -172,62 +170,48 @@ static struct tipc_subscriber *tipc_subscrb_create(int conid)
172 170
173static void tipc_subscrb_delete(struct tipc_subscriber *subscriber) 171static void tipc_subscrb_delete(struct tipc_subscriber *subscriber)
174{ 172{
175 struct tipc_subscription *sub; 173 struct tipc_subscription *sub, *temp;
176 struct tipc_subscription *sub_temp;
177 174
178 spin_lock_bh(&subscriber->lock); 175 spin_lock_bh(&subscriber->lock);
179
180 /* Destroy any existing subscriptions for subscriber */ 176 /* Destroy any existing subscriptions for subscriber */
181 list_for_each_entry_safe(sub, sub_temp, &subscriber->subscrp_list, 177 list_for_each_entry_safe(sub, temp, &subscriber->subscrp_list,
182 subscrp_list) { 178 subscrp_list) {
183 if (sub->timeout != TIPC_WAIT_FOREVER) { 179 if (del_timer(&sub->timer)) {
184 spin_unlock_bh(&subscriber->lock); 180 tipc_subscrp_delete(sub);
185 del_timer_sync(&sub->timer); 181 tipc_subscrb_put(subscriber);
186 spin_lock_bh(&subscriber->lock);
187 } 182 }
188 tipc_subscrp_delete(sub);
189 } 183 }
190 spin_unlock_bh(&subscriber->lock); 184 spin_unlock_bh(&subscriber->lock);
191 185
192 /* Now destroy subscriber */ 186 tipc_subscrb_put(subscriber);
193 kfree(subscriber); 187}
188
189static void tipc_subscrp_delete(struct tipc_subscription *sub)
190{
191 struct tipc_net *tn = net_generic(sub->net, tipc_net_id);
192
193 tipc_nametbl_unsubscribe(sub);
194 list_del(&sub->subscrp_list);
195 kfree(sub);
196 atomic_dec(&tn->subscription_count);
194} 197}
195 198
196/**
197 * tipc_subscrp_cancel - handle subscription cancellation request
198 *
199 * Called with subscriber lock held. Routine must temporarily release lock
200 * to enable the subscription timeout routine to finish without deadlocking;
201 * the lock is then reclaimed to allow caller to release it upon return.
202 *
203 * Note that fields of 's' use subscriber's endianness!
204 */
205static void tipc_subscrp_cancel(struct tipc_subscr *s, 199static void tipc_subscrp_cancel(struct tipc_subscr *s,
206 struct tipc_subscriber *subscriber) 200 struct tipc_subscriber *subscriber)
207{ 201{
208 struct tipc_subscription *sub; 202 struct tipc_subscription *sub, *temp;
209 struct tipc_subscription *sub_temp;
210 int found = 0;
211 203
212 /* Find first matching subscription, exit if not found */ 204 /* Find first matching subscription, exit if not found */
213 list_for_each_entry_safe(sub, sub_temp, &subscriber->subscrp_list, 205 list_for_each_entry_safe(sub, temp, &subscriber->subscrp_list,
214 subscrp_list) { 206 subscrp_list) {
215 if (!memcmp(s, &sub->evt.s, sizeof(struct tipc_subscr))) { 207 if (!memcmp(s, &sub->evt.s, sizeof(struct tipc_subscr))) {
216 found = 1; 208 if (del_timer(&sub->timer)) {
209 tipc_subscrp_delete(sub);
210 tipc_subscrb_put(subscriber);
211 }
217 break; 212 break;
218 } 213 }
219 } 214 }
220 if (!found)
221 return;
222
223 /* Cancel subscription timer (if used), then delete subscription */
224 if (sub->timeout != TIPC_WAIT_FOREVER) {
225 sub->timeout = TIPC_WAIT_FOREVER;
226 spin_unlock_bh(&subscriber->lock);
227 del_timer_sync(&sub->timer);
228 spin_lock_bh(&subscriber->lock);
229 }
230 tipc_subscrp_delete(sub);
231} 215}
232 216
233static int tipc_subscrp_create(struct net *net, struct tipc_subscr *s, 217static int tipc_subscrp_create(struct net *net, struct tipc_subscr *s,
@@ -281,11 +265,11 @@ static int tipc_subscrp_create(struct net *net, struct tipc_subscr *s,
281 sub->swap = swap; 265 sub->swap = swap;
282 memcpy(&sub->evt.s, s, sizeof(*s)); 266 memcpy(&sub->evt.s, s, sizeof(*s));
283 atomic_inc(&tn->subscription_count); 267 atomic_inc(&tn->subscription_count);
284 if (sub->timeout != TIPC_WAIT_FOREVER) { 268 setup_timer(&sub->timer, tipc_subscrp_timeout, (unsigned long)sub);
285 setup_timer(&sub->timer, tipc_subscrp_timeout, 269 if (sub->timeout != TIPC_WAIT_FOREVER)
286 (unsigned long)sub); 270 sub->timeout += jiffies;
287 mod_timer(&sub->timer, jiffies + sub->timeout); 271 if (!mod_timer(&sub->timer, sub->timeout))
288 } 272 tipc_subscrb_get(subscriber);
289 *sub_p = sub; 273 *sub_p = sub;
290 return 0; 274 return 0;
291} 275}