]>
Commit | Line | Data |
---|---|---|
d7e09d03 PT |
1 | /* |
2 | * GPL HEADER START | |
3 | * | |
4 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License version 2 only, | |
8 | * as published by the Free Software Foundation. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, but | |
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 | * General Public License version 2 for more details (a copy is included | |
14 | * in the LICENSE file that accompanied this code). | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * version 2 along with this program; If not, see | |
6a5b99a4 | 18 | * http://www.gnu.org/licenses/gpl-2.0.html |
d7e09d03 | 19 | * |
d7e09d03 PT |
20 | * GPL HEADER END |
21 | */ | |
22 | /* | |
23 | * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. | |
24 | * Use is subject to license terms. | |
25 | * | |
26 | * Copyright (c) 2012, Intel Corporation. | |
27 | */ | |
28 | /* | |
29 | * This file is part of Lustre, http://www.lustre.org/ | |
30 | * Lustre is a trademark of Sun Microsystems, Inc. | |
31 | * | |
32 | * lnet/lnet/peer.c | |
33 | */ | |
34 | ||
35 | #define DEBUG_SUBSYSTEM S_LNET | |
36 | ||
9fdaf8c0 | 37 | #include "../../include/linux/lnet/lib-lnet.h" |
2e9a51bd | 38 | #include "../../include/linux/lnet/lib-dlc.h" |
d7e09d03 PT |
39 | |
40 | int | |
41 | lnet_peer_tables_create(void) | |
42 | { | |
7e7ab095 MS |
43 | struct lnet_peer_table *ptable; |
44 | struct list_head *hash; | |
45 | int i; | |
46 | int j; | |
d7e09d03 PT |
47 | |
48 | the_lnet.ln_peer_tables = cfs_percpt_alloc(lnet_cpt_table(), | |
49 | sizeof(*ptable)); | |
06ace26e | 50 | if (!the_lnet.ln_peer_tables) { |
d7e09d03 PT |
51 | CERROR("Failed to allocate cpu-partition peer tables\n"); |
52 | return -ENOMEM; | |
53 | } | |
54 | ||
55 | cfs_percpt_for_each(ptable, i, the_lnet.ln_peer_tables) { | |
56 | INIT_LIST_HEAD(&ptable->pt_deathrow); | |
57 | ||
58 | LIBCFS_CPT_ALLOC(hash, lnet_cpt_table(), i, | |
59 | LNET_PEER_HASH_SIZE * sizeof(*hash)); | |
06ace26e | 60 | if (!hash) { |
d7e09d03 PT |
61 | CERROR("Failed to create peer hash table\n"); |
62 | lnet_peer_tables_destroy(); | |
63 | return -ENOMEM; | |
64 | } | |
65 | ||
66 | for (j = 0; j < LNET_PEER_HASH_SIZE; j++) | |
67 | INIT_LIST_HEAD(&hash[j]); | |
68 | ptable->pt_hash = hash; /* sign of initialization */ | |
69 | } | |
70 | ||
71 | return 0; | |
72 | } | |
73 | ||
74 | void | |
75 | lnet_peer_tables_destroy(void) | |
76 | { | |
7e7ab095 MS |
77 | struct lnet_peer_table *ptable; |
78 | struct list_head *hash; | |
79 | int i; | |
80 | int j; | |
d7e09d03 | 81 | |
06ace26e | 82 | if (!the_lnet.ln_peer_tables) |
d7e09d03 PT |
83 | return; |
84 | ||
85 | cfs_percpt_for_each(ptable, i, the_lnet.ln_peer_tables) { | |
86 | hash = ptable->pt_hash; | |
06ace26e | 87 | if (!hash) /* not initialized */ |
d7e09d03 PT |
88 | break; |
89 | ||
90 | LASSERT(list_empty(&ptable->pt_deathrow)); | |
91 | ||
92 | ptable->pt_hash = NULL; | |
93 | for (j = 0; j < LNET_PEER_HASH_SIZE; j++) | |
94 | LASSERT(list_empty(&hash[j])); | |
95 | ||
96 | LIBCFS_FREE(hash, LNET_PEER_HASH_SIZE * sizeof(*hash)); | |
97 | } | |
98 | ||
99 | cfs_percpt_free(the_lnet.ln_peer_tables); | |
100 | the_lnet.ln_peer_tables = NULL; | |
101 | } | |
102 | ||
21602c7d | 103 | static void |
21e86fd3 JS |
104 | lnet_peer_table_cleanup_locked(struct lnet_ni *ni, |
105 | struct lnet_peer_table *ptable) | |
21602c7d AS |
106 | { |
107 | int i; | |
5716397a JS |
108 | struct lnet_peer *lp; |
109 | struct lnet_peer *tmp; | |
21602c7d AS |
110 | |
111 | for (i = 0; i < LNET_PEER_HASH_SIZE; i++) { | |
112 | list_for_each_entry_safe(lp, tmp, &ptable->pt_hash[i], | |
113 | lp_hashlist) { | |
114 | if (ni && ni != lp->lp_ni) | |
115 | continue; | |
116 | list_del_init(&lp->lp_hashlist); | |
117 | /* Lose hash table's ref */ | |
118 | ptable->pt_zombies++; | |
119 | lnet_peer_decref_locked(lp); | |
120 | } | |
121 | } | |
122 | } | |
123 | ||
124 | static void | |
125 | lnet_peer_table_deathrow_wait_locked(struct lnet_peer_table *ptable, | |
126 | int cpt_locked) | |
127 | { | |
128 | int i; | |
129 | ||
130 | for (i = 3; ptable->pt_zombies; i++) { | |
131 | lnet_net_unlock(cpt_locked); | |
132 | ||
133 | if (is_power_of_2(i)) { | |
134 | CDEBUG(D_WARNING, | |
135 | "Waiting for %d zombies on peer table\n", | |
136 | ptable->pt_zombies); | |
137 | } | |
138 | set_current_state(TASK_UNINTERRUPTIBLE); | |
139 | schedule_timeout(cfs_time_seconds(1) >> 1); | |
140 | lnet_net_lock(cpt_locked); | |
141 | } | |
142 | } | |
143 | ||
144 | static void | |
21e86fd3 JS |
145 | lnet_peer_table_del_rtrs_locked(struct lnet_ni *ni, |
146 | struct lnet_peer_table *ptable, | |
21602c7d AS |
147 | int cpt_locked) |
148 | { | |
5716397a JS |
149 | struct lnet_peer *lp; |
150 | struct lnet_peer *tmp; | |
21602c7d AS |
151 | lnet_nid_t lp_nid; |
152 | int i; | |
153 | ||
154 | for (i = 0; i < LNET_PEER_HASH_SIZE; i++) { | |
155 | list_for_each_entry_safe(lp, tmp, &ptable->pt_hash[i], | |
156 | lp_hashlist) { | |
157 | if (ni != lp->lp_ni) | |
158 | continue; | |
159 | ||
160 | if (!lp->lp_rtr_refcount) | |
161 | continue; | |
162 | ||
163 | lp_nid = lp->lp_nid; | |
164 | ||
165 | lnet_net_unlock(cpt_locked); | |
166 | lnet_del_route(LNET_NIDNET(LNET_NID_ANY), lp_nid); | |
167 | lnet_net_lock(cpt_locked); | |
168 | } | |
169 | } | |
170 | } | |
171 | ||
d7e09d03 | 172 | void |
21e86fd3 | 173 | lnet_peer_tables_cleanup(struct lnet_ni *ni) |
d7e09d03 | 174 | { |
7e7ab095 | 175 | struct lnet_peer_table *ptable; |
21602c7d | 176 | struct list_head deathrow; |
5716397a JS |
177 | struct lnet_peer *lp; |
178 | struct lnet_peer *temp; | |
7e7ab095 | 179 | int i; |
d7e09d03 | 180 | |
21602c7d | 181 | INIT_LIST_HEAD(&deathrow); |
d7e09d03 | 182 | |
21602c7d AS |
183 | LASSERT(the_lnet.ln_shutdown || ni); |
184 | /* | |
185 | * If just deleting the peers for a NI, get rid of any routes these | |
186 | * peers are gateways for. | |
187 | */ | |
d7e09d03 PT |
188 | cfs_percpt_for_each(ptable, i, the_lnet.ln_peer_tables) { |
189 | lnet_net_lock(i); | |
21602c7d | 190 | lnet_peer_table_del_rtrs_locked(ni, ptable, i); |
d7e09d03 PT |
191 | lnet_net_unlock(i); |
192 | } | |
193 | ||
21602c7d AS |
194 | /* |
195 | * Start the process of moving the applicable peers to | |
196 | * deathrow. | |
197 | */ | |
d7e09d03 | 198 | cfs_percpt_for_each(ptable, i, the_lnet.ln_peer_tables) { |
d7e09d03 | 199 | lnet_net_lock(i); |
21602c7d AS |
200 | lnet_peer_table_cleanup_locked(ni, ptable); |
201 | lnet_net_unlock(i); | |
202 | } | |
d7e09d03 | 203 | |
21602c7d AS |
204 | /* Cleanup all entries on deathrow. */ |
205 | cfs_percpt_for_each(ptable, i, the_lnet.ln_peer_tables) { | |
206 | lnet_net_lock(i); | |
207 | lnet_peer_table_deathrow_wait_locked(ptable, i); | |
d7e09d03 | 208 | list_splice_init(&ptable->pt_deathrow, &deathrow); |
d7e09d03 | 209 | lnet_net_unlock(i); |
21602c7d | 210 | } |
d7e09d03 | 211 | |
3e47a1cf | 212 | list_for_each_entry_safe(lp, temp, &deathrow, lp_hashlist) { |
21602c7d AS |
213 | list_del(&lp->lp_hashlist); |
214 | LIBCFS_FREE(lp, sizeof(*lp)); | |
d7e09d03 PT |
215 | } |
216 | } | |
217 | ||
218 | void | |
5716397a | 219 | lnet_destroy_peer_locked(struct lnet_peer *lp) |
d7e09d03 PT |
220 | { |
221 | struct lnet_peer_table *ptable; | |
222 | ||
5fd88337 JS |
223 | LASSERT(!lp->lp_refcount); |
224 | LASSERT(!lp->lp_rtr_refcount); | |
d7e09d03 PT |
225 | LASSERT(list_empty(&lp->lp_txq)); |
226 | LASSERT(list_empty(&lp->lp_hashlist)); | |
5fd88337 | 227 | LASSERT(!lp->lp_txqnob); |
d7e09d03 PT |
228 | |
229 | ptable = the_lnet.ln_peer_tables[lp->lp_cpt]; | |
230 | LASSERT(ptable->pt_number > 0); | |
231 | ptable->pt_number--; | |
232 | ||
233 | lnet_ni_decref_locked(lp->lp_ni, lp->lp_cpt); | |
234 | lp->lp_ni = NULL; | |
235 | ||
236 | list_add(&lp->lp_hashlist, &ptable->pt_deathrow); | |
21602c7d AS |
237 | LASSERT(ptable->pt_zombies > 0); |
238 | ptable->pt_zombies--; | |
d7e09d03 PT |
239 | } |
240 | ||
5716397a | 241 | struct lnet_peer * |
d7e09d03 PT |
242 | lnet_find_peer_locked(struct lnet_peer_table *ptable, lnet_nid_t nid) |
243 | { | |
7e7ab095 | 244 | struct list_head *peers; |
5716397a | 245 | struct lnet_peer *lp; |
d7e09d03 PT |
246 | |
247 | LASSERT(!the_lnet.ln_shutdown); | |
248 | ||
249 | peers = &ptable->pt_hash[lnet_nid2peerhash(nid)]; | |
250 | list_for_each_entry(lp, peers, lp_hashlist) { | |
251 | if (lp->lp_nid == nid) { | |
252 | lnet_peer_addref_locked(lp); | |
253 | return lp; | |
254 | } | |
255 | } | |
256 | ||
257 | return NULL; | |
258 | } | |
259 | ||
260 | int | |
5716397a | 261 | lnet_nid2peer_locked(struct lnet_peer **lpp, lnet_nid_t nid, int cpt) |
d7e09d03 | 262 | { |
7e7ab095 | 263 | struct lnet_peer_table *ptable; |
5716397a JS |
264 | struct lnet_peer *lp = NULL; |
265 | struct lnet_peer *lp2; | |
7e7ab095 MS |
266 | int cpt2; |
267 | int rc = 0; | |
d7e09d03 PT |
268 | |
269 | *lpp = NULL; | |
270 | if (the_lnet.ln_shutdown) /* it's shutting down */ | |
271 | return -ESHUTDOWN; | |
272 | ||
273 | /* cpt can be LNET_LOCK_EX if it's called from router functions */ | |
274 | cpt2 = cpt != LNET_LOCK_EX ? cpt : lnet_cpt_of_nid_locked(nid); | |
275 | ||
276 | ptable = the_lnet.ln_peer_tables[cpt2]; | |
277 | lp = lnet_find_peer_locked(ptable, nid); | |
06ace26e | 278 | if (lp) { |
d7e09d03 PT |
279 | *lpp = lp; |
280 | return 0; | |
281 | } | |
282 | ||
283 | if (!list_empty(&ptable->pt_deathrow)) { | |
284 | lp = list_entry(ptable->pt_deathrow.next, | |
5716397a | 285 | struct lnet_peer, lp_hashlist); |
d7e09d03 PT |
286 | list_del(&lp->lp_hashlist); |
287 | } | |
288 | ||
289 | /* | |
290 | * take extra refcount in case another thread has shutdown LNet | |
291 | * and destroyed locks and peer-table before I finish the allocation | |
292 | */ | |
293 | ptable->pt_number++; | |
294 | lnet_net_unlock(cpt); | |
295 | ||
06ace26e | 296 | if (lp) |
d7e09d03 PT |
297 | memset(lp, 0, sizeof(*lp)); |
298 | else | |
299 | LIBCFS_CPT_ALLOC(lp, lnet_cpt_table(), cpt2, sizeof(*lp)); | |
300 | ||
06ace26e | 301 | if (!lp) { |
d7e09d03 PT |
302 | rc = -ENOMEM; |
303 | lnet_net_lock(cpt); | |
304 | goto out; | |
305 | } | |
306 | ||
307 | INIT_LIST_HEAD(&lp->lp_txq); | |
308 | INIT_LIST_HEAD(&lp->lp_rtrq); | |
309 | INIT_LIST_HEAD(&lp->lp_routes); | |
310 | ||
311 | lp->lp_notify = 0; | |
312 | lp->lp_notifylnd = 0; | |
313 | lp->lp_notifying = 0; | |
314 | lp->lp_alive_count = 0; | |
315 | lp->lp_timestamp = 0; | |
316 | lp->lp_alive = !lnet_peers_start_down(); /* 1 bit!! */ | |
317 | lp->lp_last_alive = cfs_time_current(); /* assumes alive */ | |
318 | lp->lp_last_query = 0; /* haven't asked NI yet */ | |
319 | lp->lp_ping_timestamp = 0; | |
320 | lp->lp_ping_feats = LNET_PING_FEAT_INVAL; | |
321 | lp->lp_nid = nid; | |
322 | lp->lp_cpt = cpt2; | |
323 | lp->lp_refcount = 2; /* 1 for caller; 1 for hash */ | |
324 | lp->lp_rtr_refcount = 0; | |
325 | ||
326 | lnet_net_lock(cpt); | |
327 | ||
328 | if (the_lnet.ln_shutdown) { | |
329 | rc = -ESHUTDOWN; | |
330 | goto out; | |
331 | } | |
332 | ||
333 | lp2 = lnet_find_peer_locked(ptable, nid); | |
06ace26e | 334 | if (lp2) { |
d7e09d03 PT |
335 | *lpp = lp2; |
336 | goto out; | |
337 | } | |
338 | ||
339 | lp->lp_ni = lnet_net2ni_locked(LNET_NIDNET(nid), cpt2); | |
06ace26e | 340 | if (!lp->lp_ni) { |
d7e09d03 PT |
341 | rc = -EHOSTUNREACH; |
342 | goto out; | |
343 | } | |
344 | ||
d3d3d37a JS |
345 | lp->lp_txcredits = lp->lp_ni->ni_peertxcredits; |
346 | lp->lp_mintxcredits = lp->lp_ni->ni_peertxcredits; | |
347 | lp->lp_rtrcredits = lnet_peer_buffer_credits(lp->lp_ni); | |
d7e09d03 PT |
348 | lp->lp_minrtrcredits = lnet_peer_buffer_credits(lp->lp_ni); |
349 | ||
350 | list_add_tail(&lp->lp_hashlist, | |
c314c319 | 351 | &ptable->pt_hash[lnet_nid2peerhash(nid)]); |
d7e09d03 PT |
352 | ptable->pt_version++; |
353 | *lpp = lp; | |
354 | ||
355 | return 0; | |
356 | out: | |
06ace26e | 357 | if (lp) |
d7e09d03 PT |
358 | list_add(&lp->lp_hashlist, &ptable->pt_deathrow); |
359 | ptable->pt_number--; | |
360 | return rc; | |
361 | } | |
362 | ||
363 | void | |
364 | lnet_debug_peer(lnet_nid_t nid) | |
365 | { | |
7e7ab095 | 366 | char *aliveness = "NA"; |
5716397a | 367 | struct lnet_peer *lp; |
7e7ab095 MS |
368 | int rc; |
369 | int cpt; | |
d7e09d03 PT |
370 | |
371 | cpt = lnet_cpt_of_nid(nid); | |
372 | lnet_net_lock(cpt); | |
373 | ||
374 | rc = lnet_nid2peer_locked(&lp, nid, cpt); | |
5fd88337 | 375 | if (rc) { |
d7e09d03 PT |
376 | lnet_net_unlock(cpt); |
377 | CDEBUG(D_WARNING, "No peer %s\n", libcfs_nid2str(nid)); | |
378 | return; | |
379 | } | |
380 | ||
381 | if (lnet_isrouter(lp) || lnet_peer_aliveness_enabled(lp)) | |
382 | aliveness = lp->lp_alive ? "up" : "down"; | |
383 | ||
384 | CDEBUG(D_WARNING, "%-24s %4d %5s %5d %5d %5d %5d %5d %ld\n", | |
385 | libcfs_nid2str(lp->lp_nid), lp->lp_refcount, | |
386 | aliveness, lp->lp_ni->ni_peertxcredits, | |
387 | lp->lp_rtrcredits, lp->lp_minrtrcredits, | |
388 | lp->lp_txcredits, lp->lp_mintxcredits, lp->lp_txqnob); | |
389 | ||
390 | lnet_peer_decref_locked(lp); | |
391 | ||
392 | lnet_net_unlock(cpt); | |
393 | } | |
2e9a51bd | 394 | |
edeb5d8c AS |
395 | int |
396 | lnet_get_peer_info(__u32 peer_index, __u64 *nid, | |
397 | char aliveness[LNET_MAX_STR_LEN], | |
398 | __u32 *cpt_iter, __u32 *refcount, | |
399 | __u32 *ni_peer_tx_credits, __u32 *peer_tx_credits, | |
400 | __u32 *peer_rtr_credits, __u32 *peer_min_rtr_credits, | |
401 | __u32 *peer_tx_qnob) | |
2e9a51bd AS |
402 | { |
403 | struct lnet_peer_table *peer_table; | |
5716397a | 404 | struct lnet_peer *lp; |
edeb5d8c AS |
405 | bool found = false; |
406 | int lncpt, j; | |
2e9a51bd AS |
407 | |
408 | /* get the number of CPTs */ | |
409 | lncpt = cfs_percpt_number(the_lnet.ln_peer_tables); | |
410 | ||
411 | /* | |
412 | * if the cpt number to be examined is >= the number of cpts in | |
413 | * the system then indicate that there are no more cpts to examin | |
414 | */ | |
edeb5d8c | 415 | if (*cpt_iter >= lncpt) |
2e9a51bd AS |
416 | return -ENOENT; |
417 | ||
418 | /* get the current table */ | |
edeb5d8c | 419 | peer_table = the_lnet.ln_peer_tables[*cpt_iter]; |
2e9a51bd AS |
420 | /* if the ptable is NULL then there are no more cpts to examine */ |
421 | if (!peer_table) | |
422 | return -ENOENT; | |
423 | ||
edeb5d8c | 424 | lnet_net_lock(*cpt_iter); |
2e9a51bd AS |
425 | |
426 | for (j = 0; j < LNET_PEER_HASH_SIZE && !found; j++) { | |
427 | struct list_head *peers = &peer_table->pt_hash[j]; | |
428 | ||
429 | list_for_each_entry(lp, peers, lp_hashlist) { | |
edeb5d8c | 430 | if (peer_index-- > 0) |
2e9a51bd AS |
431 | continue; |
432 | ||
433 | snprintf(aliveness, LNET_MAX_STR_LEN, "NA"); | |
434 | if (lnet_isrouter(lp) || | |
435 | lnet_peer_aliveness_enabled(lp)) | |
436 | snprintf(aliveness, LNET_MAX_STR_LEN, | |
437 | lp->lp_alive ? "up" : "down"); | |
438 | ||
439 | *nid = lp->lp_nid; | |
440 | *refcount = lp->lp_refcount; | |
441 | *ni_peer_tx_credits = lp->lp_ni->ni_peertxcredits; | |
442 | *peer_tx_credits = lp->lp_txcredits; | |
443 | *peer_rtr_credits = lp->lp_rtrcredits; | |
444 | *peer_min_rtr_credits = lp->lp_mintxcredits; | |
445 | *peer_tx_qnob = lp->lp_txqnob; | |
446 | ||
edeb5d8c | 447 | found = true; |
2e9a51bd AS |
448 | } |
449 | } | |
edeb5d8c | 450 | lnet_net_unlock(*cpt_iter); |
2e9a51bd | 451 | |
edeb5d8c | 452 | *cpt_iter = lncpt; |
2e9a51bd AS |
453 | |
454 | return found ? 0 : -ENOENT; | |
455 | } |