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 | |
18 | * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf | |
19 | * | |
20 | * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, | |
21 | * CA 95054 USA or visit www.sun.com if you need additional information or | |
22 | * have any questions. | |
23 | * | |
24 | * GPL HEADER END | |
25 | */ | |
26 | /* | |
27 | * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. | |
28 | * Use is subject to license terms. | |
29 | * | |
30 | * Copyright (c) 2011, 2012, Intel Corporation. | |
31 | */ | |
32 | /* | |
33 | * This file is part of Lustre, http://www.lustre.org/ | |
34 | * Lustre is a trademark of Sun Microsystems, Inc. | |
35 | * | |
36 | * lustre/obdclass/genops.c | |
37 | * | |
38 | * These are the only exported functions, they provide some generic | |
39 | * infrastructure for managing object devices | |
40 | */ | |
41 | ||
42 | #define DEBUG_SUBSYSTEM S_CLASS | |
610f7377 GKH |
43 | #include "../include/obd_ost.h" |
44 | #include "../include/obd_class.h" | |
45 | #include "../include/lprocfs_status.h" | |
d7e09d03 PT |
46 | |
47 | extern struct list_head obd_types; | |
48 | spinlock_t obd_types_lock; | |
49 | ||
50 | struct kmem_cache *obd_device_cachep; | |
51 | struct kmem_cache *obdo_cachep; | |
52 | EXPORT_SYMBOL(obdo_cachep); | |
53 | struct kmem_cache *import_cachep; | |
54 | ||
55 | struct list_head obd_zombie_imports; | |
56 | struct list_head obd_zombie_exports; | |
57 | spinlock_t obd_zombie_impexp_lock; | |
58 | static void obd_zombie_impexp_notify(void); | |
59 | static void obd_zombie_export_add(struct obd_export *exp); | |
60 | static void obd_zombie_import_add(struct obd_import *imp); | |
61 | static void print_export_data(struct obd_export *exp, | |
62 | const char *status, int locks); | |
63 | ||
64 | int (*ptlrpc_put_connection_superhack)(struct ptlrpc_connection *c); | |
65 | EXPORT_SYMBOL(ptlrpc_put_connection_superhack); | |
66 | ||
67 | /* | |
68 | * support functions: we could use inter-module communication, but this | |
69 | * is more portable to other OS's | |
70 | */ | |
71 | static struct obd_device *obd_device_alloc(void) | |
72 | { | |
73 | struct obd_device *obd; | |
74 | ||
0be19afa | 75 | OBD_SLAB_ALLOC_PTR_GFP(obd, obd_device_cachep, GFP_NOFS); |
d7e09d03 PT |
76 | if (obd != NULL) { |
77 | obd->obd_magic = OBD_DEVICE_MAGIC; | |
78 | } | |
79 | return obd; | |
80 | } | |
81 | ||
82 | static void obd_device_free(struct obd_device *obd) | |
83 | { | |
84 | LASSERT(obd != NULL); | |
85 | LASSERTF(obd->obd_magic == OBD_DEVICE_MAGIC, "obd %p obd_magic %08x != %08x\n", | |
86 | obd, obd->obd_magic, OBD_DEVICE_MAGIC); | |
87 | if (obd->obd_namespace != NULL) { | |
88 | CERROR("obd %p: namespace %p was not properly cleaned up (obd_force=%d)!\n", | |
89 | obd, obd->obd_namespace, obd->obd_force); | |
90 | LBUG(); | |
91 | } | |
92 | lu_ref_fini(&obd->obd_reference); | |
93 | OBD_SLAB_FREE_PTR(obd, obd_device_cachep); | |
94 | } | |
95 | ||
96 | struct obd_type *class_search_type(const char *name) | |
97 | { | |
98 | struct list_head *tmp; | |
99 | struct obd_type *type; | |
100 | ||
101 | spin_lock(&obd_types_lock); | |
102 | list_for_each(tmp, &obd_types) { | |
103 | type = list_entry(tmp, struct obd_type, typ_chain); | |
104 | if (strcmp(type->typ_name, name) == 0) { | |
105 | spin_unlock(&obd_types_lock); | |
106 | return type; | |
107 | } | |
108 | } | |
109 | spin_unlock(&obd_types_lock); | |
110 | return NULL; | |
111 | } | |
112 | EXPORT_SYMBOL(class_search_type); | |
113 | ||
114 | struct obd_type *class_get_type(const char *name) | |
115 | { | |
116 | struct obd_type *type = class_search_type(name); | |
117 | ||
118 | if (!type) { | |
119 | const char *modname = name; | |
120 | ||
121 | if (strcmp(modname, "obdfilter") == 0) | |
122 | modname = "ofd"; | |
123 | ||
124 | if (strcmp(modname, LUSTRE_LWP_NAME) == 0) | |
125 | modname = LUSTRE_OSP_NAME; | |
126 | ||
127 | if (!strncmp(modname, LUSTRE_MDS_NAME, strlen(LUSTRE_MDS_NAME))) | |
128 | modname = LUSTRE_MDT_NAME; | |
129 | ||
130 | if (!request_module("%s", modname)) { | |
131 | CDEBUG(D_INFO, "Loaded module '%s'\n", modname); | |
132 | type = class_search_type(name); | |
133 | } else { | |
134 | LCONSOLE_ERROR_MSG(0x158, "Can't load module '%s'\n", | |
135 | modname); | |
136 | } | |
137 | } | |
138 | if (type) { | |
139 | spin_lock(&type->obd_type_lock); | |
140 | type->typ_refcnt++; | |
141 | try_module_get(type->typ_dt_ops->o_owner); | |
142 | spin_unlock(&type->obd_type_lock); | |
143 | } | |
144 | return type; | |
145 | } | |
146 | EXPORT_SYMBOL(class_get_type); | |
147 | ||
148 | void class_put_type(struct obd_type *type) | |
149 | { | |
150 | LASSERT(type); | |
151 | spin_lock(&type->obd_type_lock); | |
152 | type->typ_refcnt--; | |
153 | module_put(type->typ_dt_ops->o_owner); | |
154 | spin_unlock(&type->obd_type_lock); | |
155 | } | |
156 | EXPORT_SYMBOL(class_put_type); | |
157 | ||
158 | #define CLASS_MAX_NAME 1024 | |
159 | ||
160 | int class_register_type(struct obd_ops *dt_ops, struct md_ops *md_ops, | |
161 | struct lprocfs_vars *vars, const char *name, | |
162 | struct lu_device_type *ldt) | |
163 | { | |
164 | struct obd_type *type; | |
165 | int rc = 0; | |
d7e09d03 PT |
166 | |
167 | /* sanity check */ | |
168 | LASSERT(strnlen(name, CLASS_MAX_NAME) < CLASS_MAX_NAME); | |
169 | ||
170 | if (class_search_type(name)) { | |
171 | CDEBUG(D_IOCTL, "Type %s already registered\n", name); | |
0a3bdb00 | 172 | return -EEXIST; |
d7e09d03 PT |
173 | } |
174 | ||
175 | rc = -ENOMEM; | |
176 | OBD_ALLOC(type, sizeof(*type)); | |
177 | if (type == NULL) | |
0a3bdb00 | 178 | return rc; |
d7e09d03 PT |
179 | |
180 | OBD_ALLOC_PTR(type->typ_dt_ops); | |
181 | OBD_ALLOC_PTR(type->typ_md_ops); | |
182 | OBD_ALLOC(type->typ_name, strlen(name) + 1); | |
183 | ||
184 | if (type->typ_dt_ops == NULL || | |
185 | type->typ_md_ops == NULL || | |
186 | type->typ_name == NULL) | |
187 | GOTO (failed, rc); | |
188 | ||
189 | *(type->typ_dt_ops) = *dt_ops; | |
190 | /* md_ops is optional */ | |
191 | if (md_ops) | |
192 | *(type->typ_md_ops) = *md_ops; | |
193 | strcpy(type->typ_name, name); | |
194 | spin_lock_init(&type->obd_type_lock); | |
195 | ||
d7e09d03 PT |
196 | type->typ_procroot = lprocfs_register(type->typ_name, proc_lustre_root, |
197 | vars, type); | |
198 | if (IS_ERR(type->typ_procroot)) { | |
199 | rc = PTR_ERR(type->typ_procroot); | |
200 | type->typ_procroot = NULL; | |
201 | GOTO (failed, rc); | |
202 | } | |
8cc420d0 | 203 | |
d7e09d03 PT |
204 | if (ldt != NULL) { |
205 | type->typ_lu = ldt; | |
206 | rc = lu_device_type_init(ldt); | |
207 | if (rc != 0) | |
208 | GOTO (failed, rc); | |
209 | } | |
210 | ||
211 | spin_lock(&obd_types_lock); | |
212 | list_add(&type->typ_chain, &obd_types); | |
213 | spin_unlock(&obd_types_lock); | |
214 | ||
0a3bdb00 | 215 | return 0; |
d7e09d03 PT |
216 | |
217 | failed: | |
218 | if (type->typ_name != NULL) | |
219 | OBD_FREE(type->typ_name, strlen(name) + 1); | |
220 | if (type->typ_md_ops != NULL) | |
221 | OBD_FREE_PTR(type->typ_md_ops); | |
222 | if (type->typ_dt_ops != NULL) | |
223 | OBD_FREE_PTR(type->typ_dt_ops); | |
224 | OBD_FREE(type, sizeof(*type)); | |
0a3bdb00 | 225 | return rc; |
d7e09d03 PT |
226 | } |
227 | EXPORT_SYMBOL(class_register_type); | |
228 | ||
229 | int class_unregister_type(const char *name) | |
230 | { | |
231 | struct obd_type *type = class_search_type(name); | |
d7e09d03 PT |
232 | |
233 | if (!type) { | |
234 | CERROR("unknown obd type\n"); | |
0a3bdb00 | 235 | return -EINVAL; |
d7e09d03 PT |
236 | } |
237 | ||
238 | if (type->typ_refcnt) { | |
239 | CERROR("type %s has refcount (%d)\n", name, type->typ_refcnt); | |
240 | /* This is a bad situation, let's make the best of it */ | |
241 | /* Remove ops, but leave the name for debugging */ | |
242 | OBD_FREE_PTR(type->typ_dt_ops); | |
243 | OBD_FREE_PTR(type->typ_md_ops); | |
0a3bdb00 | 244 | return -EBUSY; |
d7e09d03 PT |
245 | } |
246 | ||
73bb1da6 PT |
247 | if (type->typ_procroot) { |
248 | lprocfs_remove(&type->typ_procroot); | |
249 | } | |
d7e09d03 PT |
250 | |
251 | if (type->typ_lu) | |
252 | lu_device_type_fini(type->typ_lu); | |
253 | ||
254 | spin_lock(&obd_types_lock); | |
255 | list_del(&type->typ_chain); | |
256 | spin_unlock(&obd_types_lock); | |
257 | OBD_FREE(type->typ_name, strlen(name) + 1); | |
258 | if (type->typ_dt_ops != NULL) | |
259 | OBD_FREE_PTR(type->typ_dt_ops); | |
260 | if (type->typ_md_ops != NULL) | |
261 | OBD_FREE_PTR(type->typ_md_ops); | |
262 | OBD_FREE(type, sizeof(*type)); | |
0a3bdb00 | 263 | return 0; |
d7e09d03 PT |
264 | } /* class_unregister_type */ |
265 | EXPORT_SYMBOL(class_unregister_type); | |
266 | ||
267 | /** | |
268 | * Create a new obd device. | |
269 | * | |
270 | * Find an empty slot in ::obd_devs[], create a new obd device in it. | |
271 | * | |
272 | * \param[in] type_name obd device type string. | |
273 | * \param[in] name obd device name. | |
274 | * | |
275 | * \retval NULL if create fails, otherwise return the obd device | |
276 | * pointer created. | |
277 | */ | |
278 | struct obd_device *class_newdev(const char *type_name, const char *name) | |
279 | { | |
280 | struct obd_device *result = NULL; | |
281 | struct obd_device *newdev; | |
282 | struct obd_type *type = NULL; | |
283 | int i; | |
284 | int new_obd_minor = 0; | |
d7e09d03 PT |
285 | |
286 | if (strlen(name) >= MAX_OBD_NAME) { | |
287 | CERROR("name/uuid must be < %u bytes long\n", MAX_OBD_NAME); | |
0a3bdb00 | 288 | return ERR_PTR(-EINVAL); |
d7e09d03 PT |
289 | } |
290 | ||
291 | type = class_get_type(type_name); | |
292 | if (type == NULL){ | |
293 | CERROR("OBD: unknown type: %s\n", type_name); | |
0a3bdb00 | 294 | return ERR_PTR(-ENODEV); |
d7e09d03 PT |
295 | } |
296 | ||
297 | newdev = obd_device_alloc(); | |
298 | if (newdev == NULL) | |
299 | GOTO(out_type, result = ERR_PTR(-ENOMEM)); | |
300 | ||
301 | LASSERT(newdev->obd_magic == OBD_DEVICE_MAGIC); | |
302 | ||
303 | write_lock(&obd_dev_lock); | |
304 | for (i = 0; i < class_devno_max(); i++) { | |
305 | struct obd_device *obd = class_num2obd(i); | |
306 | ||
307 | if (obd && (strcmp(name, obd->obd_name) == 0)) { | |
308 | CERROR("Device %s already exists at %d, won't add\n", | |
309 | name, i); | |
310 | if (result) { | |
311 | LASSERTF(result->obd_magic == OBD_DEVICE_MAGIC, | |
312 | "%p obd_magic %08x != %08x\n", result, | |
313 | result->obd_magic, OBD_DEVICE_MAGIC); | |
314 | LASSERTF(result->obd_minor == new_obd_minor, | |
315 | "%p obd_minor %d != %d\n", result, | |
316 | result->obd_minor, new_obd_minor); | |
317 | ||
318 | obd_devs[result->obd_minor] = NULL; | |
319 | result->obd_name[0]='\0'; | |
320 | } | |
321 | result = ERR_PTR(-EEXIST); | |
322 | break; | |
323 | } | |
324 | if (!result && !obd) { | |
325 | result = newdev; | |
326 | result->obd_minor = i; | |
327 | new_obd_minor = i; | |
328 | result->obd_type = type; | |
329 | strncpy(result->obd_name, name, | |
330 | sizeof(result->obd_name) - 1); | |
331 | obd_devs[i] = result; | |
332 | } | |
333 | } | |
334 | write_unlock(&obd_dev_lock); | |
335 | ||
336 | if (result == NULL && i >= class_devno_max()) { | |
337 | CERROR("all %u OBD devices used, increase MAX_OBD_DEVICES\n", | |
338 | class_devno_max()); | |
339 | GOTO(out, result = ERR_PTR(-EOVERFLOW)); | |
340 | } | |
341 | ||
342 | if (IS_ERR(result)) | |
343 | GOTO(out, result); | |
344 | ||
345 | CDEBUG(D_IOCTL, "Adding new device %s (%p)\n", | |
346 | result->obd_name, result); | |
347 | ||
0a3bdb00 | 348 | return result; |
d7e09d03 PT |
349 | out: |
350 | obd_device_free(newdev); | |
351 | out_type: | |
352 | class_put_type(type); | |
353 | return result; | |
354 | } | |
355 | ||
356 | void class_release_dev(struct obd_device *obd) | |
357 | { | |
358 | struct obd_type *obd_type = obd->obd_type; | |
359 | ||
360 | LASSERTF(obd->obd_magic == OBD_DEVICE_MAGIC, "%p obd_magic %08x != %08x\n", | |
361 | obd, obd->obd_magic, OBD_DEVICE_MAGIC); | |
362 | LASSERTF(obd == obd_devs[obd->obd_minor], "obd %p != obd_devs[%d] %p\n", | |
363 | obd, obd->obd_minor, obd_devs[obd->obd_minor]); | |
364 | LASSERT(obd_type != NULL); | |
365 | ||
366 | CDEBUG(D_INFO, "Release obd device %s at %d obd_type name =%s\n", | |
367 | obd->obd_name, obd->obd_minor, obd->obd_type->typ_name); | |
368 | ||
369 | write_lock(&obd_dev_lock); | |
370 | obd_devs[obd->obd_minor] = NULL; | |
371 | write_unlock(&obd_dev_lock); | |
372 | obd_device_free(obd); | |
373 | ||
374 | class_put_type(obd_type); | |
375 | } | |
376 | ||
377 | int class_name2dev(const char *name) | |
378 | { | |
379 | int i; | |
380 | ||
381 | if (!name) | |
382 | return -1; | |
383 | ||
384 | read_lock(&obd_dev_lock); | |
385 | for (i = 0; i < class_devno_max(); i++) { | |
386 | struct obd_device *obd = class_num2obd(i); | |
387 | ||
388 | if (obd && strcmp(name, obd->obd_name) == 0) { | |
389 | /* Make sure we finished attaching before we give | |
390 | out any references */ | |
391 | LASSERT(obd->obd_magic == OBD_DEVICE_MAGIC); | |
392 | if (obd->obd_attached) { | |
393 | read_unlock(&obd_dev_lock); | |
394 | return i; | |
395 | } | |
396 | break; | |
397 | } | |
398 | } | |
399 | read_unlock(&obd_dev_lock); | |
400 | ||
401 | return -1; | |
402 | } | |
403 | EXPORT_SYMBOL(class_name2dev); | |
404 | ||
405 | struct obd_device *class_name2obd(const char *name) | |
406 | { | |
407 | int dev = class_name2dev(name); | |
408 | ||
409 | if (dev < 0 || dev > class_devno_max()) | |
410 | return NULL; | |
411 | return class_num2obd(dev); | |
412 | } | |
413 | EXPORT_SYMBOL(class_name2obd); | |
414 | ||
415 | int class_uuid2dev(struct obd_uuid *uuid) | |
416 | { | |
417 | int i; | |
418 | ||
419 | read_lock(&obd_dev_lock); | |
420 | for (i = 0; i < class_devno_max(); i++) { | |
421 | struct obd_device *obd = class_num2obd(i); | |
422 | ||
423 | if (obd && obd_uuid_equals(uuid, &obd->obd_uuid)) { | |
424 | LASSERT(obd->obd_magic == OBD_DEVICE_MAGIC); | |
425 | read_unlock(&obd_dev_lock); | |
426 | return i; | |
427 | } | |
428 | } | |
429 | read_unlock(&obd_dev_lock); | |
430 | ||
431 | return -1; | |
432 | } | |
433 | EXPORT_SYMBOL(class_uuid2dev); | |
434 | ||
435 | struct obd_device *class_uuid2obd(struct obd_uuid *uuid) | |
436 | { | |
437 | int dev = class_uuid2dev(uuid); | |
438 | if (dev < 0) | |
439 | return NULL; | |
440 | return class_num2obd(dev); | |
441 | } | |
442 | EXPORT_SYMBOL(class_uuid2obd); | |
443 | ||
444 | /** | |
445 | * Get obd device from ::obd_devs[] | |
446 | * | |
447 | * \param num [in] array index | |
448 | * | |
449 | * \retval NULL if ::obd_devs[\a num] does not contains an obd device | |
450 | * otherwise return the obd device there. | |
451 | */ | |
452 | struct obd_device *class_num2obd(int num) | |
453 | { | |
454 | struct obd_device *obd = NULL; | |
455 | ||
456 | if (num < class_devno_max()) { | |
457 | obd = obd_devs[num]; | |
458 | if (obd == NULL) | |
459 | return NULL; | |
460 | ||
461 | LASSERTF(obd->obd_magic == OBD_DEVICE_MAGIC, | |
462 | "%p obd_magic %08x != %08x\n", | |
463 | obd, obd->obd_magic, OBD_DEVICE_MAGIC); | |
464 | LASSERTF(obd->obd_minor == num, | |
465 | "%p obd_minor %0d != %0d\n", | |
466 | obd, obd->obd_minor, num); | |
467 | } | |
468 | ||
469 | return obd; | |
470 | } | |
471 | EXPORT_SYMBOL(class_num2obd); | |
472 | ||
473 | /** | |
474 | * Get obd devices count. Device in any | |
475 | * state are counted | |
476 | * \retval obd device count | |
477 | */ | |
478 | int get_devices_count(void) | |
479 | { | |
480 | int index, max_index = class_devno_max(), dev_count = 0; | |
481 | ||
482 | read_lock(&obd_dev_lock); | |
483 | for (index = 0; index <= max_index; index++) { | |
484 | struct obd_device *obd = class_num2obd(index); | |
485 | if (obd != NULL) | |
486 | dev_count++; | |
487 | } | |
488 | read_unlock(&obd_dev_lock); | |
489 | ||
490 | return dev_count; | |
491 | } | |
492 | EXPORT_SYMBOL(get_devices_count); | |
493 | ||
494 | void class_obd_list(void) | |
495 | { | |
496 | char *status; | |
497 | int i; | |
498 | ||
499 | read_lock(&obd_dev_lock); | |
500 | for (i = 0; i < class_devno_max(); i++) { | |
501 | struct obd_device *obd = class_num2obd(i); | |
502 | ||
503 | if (obd == NULL) | |
504 | continue; | |
505 | if (obd->obd_stopping) | |
506 | status = "ST"; | |
507 | else if (obd->obd_set_up) | |
508 | status = "UP"; | |
509 | else if (obd->obd_attached) | |
510 | status = "AT"; | |
511 | else | |
512 | status = "--"; | |
513 | LCONSOLE(D_CONFIG, "%3d %s %s %s %s %d\n", | |
514 | i, status, obd->obd_type->typ_name, | |
515 | obd->obd_name, obd->obd_uuid.uuid, | |
516 | atomic_read(&obd->obd_refcount)); | |
517 | } | |
518 | read_unlock(&obd_dev_lock); | |
519 | return; | |
520 | } | |
521 | ||
522 | /* Search for a client OBD connected to tgt_uuid. If grp_uuid is | |
523 | specified, then only the client with that uuid is returned, | |
524 | otherwise any client connected to the tgt is returned. */ | |
525 | struct obd_device * class_find_client_obd(struct obd_uuid *tgt_uuid, | |
526 | const char * typ_name, | |
527 | struct obd_uuid *grp_uuid) | |
528 | { | |
529 | int i; | |
530 | ||
531 | read_lock(&obd_dev_lock); | |
532 | for (i = 0; i < class_devno_max(); i++) { | |
533 | struct obd_device *obd = class_num2obd(i); | |
534 | ||
535 | if (obd == NULL) | |
536 | continue; | |
537 | if ((strncmp(obd->obd_type->typ_name, typ_name, | |
538 | strlen(typ_name)) == 0)) { | |
539 | if (obd_uuid_equals(tgt_uuid, | |
540 | &obd->u.cli.cl_target_uuid) && | |
541 | ((grp_uuid)? obd_uuid_equals(grp_uuid, | |
542 | &obd->obd_uuid) : 1)) { | |
543 | read_unlock(&obd_dev_lock); | |
544 | return obd; | |
545 | } | |
546 | } | |
547 | } | |
548 | read_unlock(&obd_dev_lock); | |
549 | ||
550 | return NULL; | |
551 | } | |
552 | EXPORT_SYMBOL(class_find_client_obd); | |
553 | ||
554 | /* Iterate the obd_device list looking devices have grp_uuid. Start | |
555 | searching at *next, and if a device is found, the next index to look | |
556 | at is saved in *next. If next is NULL, then the first matching device | |
557 | will always be returned. */ | |
558 | struct obd_device * class_devices_in_group(struct obd_uuid *grp_uuid, int *next) | |
559 | { | |
560 | int i; | |
561 | ||
562 | if (next == NULL) | |
563 | i = 0; | |
564 | else if (*next >= 0 && *next < class_devno_max()) | |
565 | i = *next; | |
566 | else | |
567 | return NULL; | |
568 | ||
569 | read_lock(&obd_dev_lock); | |
570 | for (; i < class_devno_max(); i++) { | |
571 | struct obd_device *obd = class_num2obd(i); | |
572 | ||
573 | if (obd == NULL) | |
574 | continue; | |
575 | if (obd_uuid_equals(grp_uuid, &obd->obd_uuid)) { | |
576 | if (next != NULL) | |
577 | *next = i+1; | |
578 | read_unlock(&obd_dev_lock); | |
579 | return obd; | |
580 | } | |
581 | } | |
582 | read_unlock(&obd_dev_lock); | |
583 | ||
584 | return NULL; | |
585 | } | |
586 | EXPORT_SYMBOL(class_devices_in_group); | |
587 | ||
588 | /** | |
589 | * to notify sptlrpc log for \a fsname has changed, let every relevant OBD | |
590 | * adjust sptlrpc settings accordingly. | |
591 | */ | |
592 | int class_notify_sptlrpc_conf(const char *fsname, int namelen) | |
593 | { | |
594 | struct obd_device *obd; | |
595 | const char *type; | |
596 | int i, rc = 0, rc2; | |
597 | ||
598 | LASSERT(namelen > 0); | |
599 | ||
600 | read_lock(&obd_dev_lock); | |
601 | for (i = 0; i < class_devno_max(); i++) { | |
602 | obd = class_num2obd(i); | |
603 | ||
604 | if (obd == NULL || obd->obd_set_up == 0 || obd->obd_stopping) | |
605 | continue; | |
606 | ||
607 | /* only notify mdc, osc, mdt, ost */ | |
608 | type = obd->obd_type->typ_name; | |
609 | if (strcmp(type, LUSTRE_MDC_NAME) != 0 && | |
610 | strcmp(type, LUSTRE_OSC_NAME) != 0 && | |
611 | strcmp(type, LUSTRE_MDT_NAME) != 0 && | |
612 | strcmp(type, LUSTRE_OST_NAME) != 0) | |
613 | continue; | |
614 | ||
615 | if (strncmp(obd->obd_name, fsname, namelen)) | |
616 | continue; | |
617 | ||
f9bd9c1a | 618 | class_incref(obd, __func__, obd); |
d7e09d03 PT |
619 | read_unlock(&obd_dev_lock); |
620 | rc2 = obd_set_info_async(NULL, obd->obd_self_export, | |
621 | sizeof(KEY_SPTLRPC_CONF), | |
622 | KEY_SPTLRPC_CONF, 0, NULL, NULL); | |
623 | rc = rc ? rc : rc2; | |
f9bd9c1a | 624 | class_decref(obd, __func__, obd); |
d7e09d03 PT |
625 | read_lock(&obd_dev_lock); |
626 | } | |
627 | read_unlock(&obd_dev_lock); | |
628 | return rc; | |
629 | } | |
630 | EXPORT_SYMBOL(class_notify_sptlrpc_conf); | |
631 | ||
632 | void obd_cleanup_caches(void) | |
633 | { | |
d7e09d03 PT |
634 | if (obd_device_cachep) { |
635 | kmem_cache_destroy(obd_device_cachep); | |
636 | obd_device_cachep = NULL; | |
637 | } | |
638 | if (obdo_cachep) { | |
639 | kmem_cache_destroy(obdo_cachep); | |
640 | obdo_cachep = NULL; | |
641 | } | |
642 | if (import_cachep) { | |
643 | kmem_cache_destroy(import_cachep); | |
644 | import_cachep = NULL; | |
645 | } | |
646 | if (capa_cachep) { | |
647 | kmem_cache_destroy(capa_cachep); | |
648 | capa_cachep = NULL; | |
649 | } | |
d7e09d03 PT |
650 | } |
651 | ||
652 | int obd_init_caches(void) | |
653 | { | |
d7e09d03 PT |
654 | LASSERT(obd_device_cachep == NULL); |
655 | obd_device_cachep = kmem_cache_create("ll_obd_dev_cache", | |
656 | sizeof(struct obd_device), | |
657 | 0, 0, NULL); | |
658 | if (!obd_device_cachep) | |
659 | GOTO(out, -ENOMEM); | |
660 | ||
661 | LASSERT(obdo_cachep == NULL); | |
662 | obdo_cachep = kmem_cache_create("ll_obdo_cache", sizeof(struct obdo), | |
663 | 0, 0, NULL); | |
664 | if (!obdo_cachep) | |
665 | GOTO(out, -ENOMEM); | |
666 | ||
667 | LASSERT(import_cachep == NULL); | |
668 | import_cachep = kmem_cache_create("ll_import_cache", | |
669 | sizeof(struct obd_import), | |
670 | 0, 0, NULL); | |
671 | if (!import_cachep) | |
672 | GOTO(out, -ENOMEM); | |
673 | ||
674 | LASSERT(capa_cachep == NULL); | |
675 | capa_cachep = kmem_cache_create("capa_cache", | |
676 | sizeof(struct obd_capa), 0, 0, NULL); | |
677 | if (!capa_cachep) | |
678 | GOTO(out, -ENOMEM); | |
679 | ||
0a3bdb00 | 680 | return 0; |
d7e09d03 PT |
681 | out: |
682 | obd_cleanup_caches(); | |
0a3bdb00 | 683 | return -ENOMEM; |
d7e09d03 PT |
684 | |
685 | } | |
686 | ||
687 | /* map connection to client */ | |
688 | struct obd_export *class_conn2export(struct lustre_handle *conn) | |
689 | { | |
690 | struct obd_export *export; | |
d7e09d03 PT |
691 | |
692 | if (!conn) { | |
693 | CDEBUG(D_CACHE, "looking for null handle\n"); | |
0a3bdb00 | 694 | return NULL; |
d7e09d03 PT |
695 | } |
696 | ||
697 | if (conn->cookie == -1) { /* this means assign a new connection */ | |
698 | CDEBUG(D_CACHE, "want a new connection\n"); | |
0a3bdb00 | 699 | return NULL; |
d7e09d03 PT |
700 | } |
701 | ||
55f5a824 | 702 | CDEBUG(D_INFO, "looking for export cookie %#llx\n", conn->cookie); |
d7e09d03 | 703 | export = class_handle2object(conn->cookie); |
0a3bdb00 | 704 | return export; |
d7e09d03 PT |
705 | } |
706 | EXPORT_SYMBOL(class_conn2export); | |
707 | ||
708 | struct obd_device *class_exp2obd(struct obd_export *exp) | |
709 | { | |
710 | if (exp) | |
711 | return exp->exp_obd; | |
712 | return NULL; | |
713 | } | |
714 | EXPORT_SYMBOL(class_exp2obd); | |
715 | ||
716 | struct obd_device *class_conn2obd(struct lustre_handle *conn) | |
717 | { | |
718 | struct obd_export *export; | |
719 | export = class_conn2export(conn); | |
720 | if (export) { | |
721 | struct obd_device *obd = export->exp_obd; | |
722 | class_export_put(export); | |
723 | return obd; | |
724 | } | |
725 | return NULL; | |
726 | } | |
727 | EXPORT_SYMBOL(class_conn2obd); | |
728 | ||
729 | struct obd_import *class_exp2cliimp(struct obd_export *exp) | |
730 | { | |
731 | struct obd_device *obd = exp->exp_obd; | |
732 | if (obd == NULL) | |
733 | return NULL; | |
734 | return obd->u.cli.cl_import; | |
735 | } | |
736 | EXPORT_SYMBOL(class_exp2cliimp); | |
737 | ||
738 | struct obd_import *class_conn2cliimp(struct lustre_handle *conn) | |
739 | { | |
740 | struct obd_device *obd = class_conn2obd(conn); | |
741 | if (obd == NULL) | |
742 | return NULL; | |
743 | return obd->u.cli.cl_import; | |
744 | } | |
745 | EXPORT_SYMBOL(class_conn2cliimp); | |
746 | ||
747 | /* Export management functions */ | |
748 | static void class_export_destroy(struct obd_export *exp) | |
749 | { | |
750 | struct obd_device *obd = exp->exp_obd; | |
d7e09d03 PT |
751 | |
752 | LASSERT_ATOMIC_ZERO(&exp->exp_refcount); | |
753 | LASSERT(obd != NULL); | |
754 | ||
755 | CDEBUG(D_IOCTL, "destroying export %p/%s for %s\n", exp, | |
756 | exp->exp_client_uuid.uuid, obd->obd_name); | |
757 | ||
758 | /* "Local" exports (lctl, LOV->{mdc,osc}) have no connection. */ | |
759 | if (exp->exp_connection) | |
760 | ptlrpc_put_connection_superhack(exp->exp_connection); | |
761 | ||
762 | LASSERT(list_empty(&exp->exp_outstanding_replies)); | |
763 | LASSERT(list_empty(&exp->exp_uncommitted_replies)); | |
764 | LASSERT(list_empty(&exp->exp_req_replay_queue)); | |
765 | LASSERT(list_empty(&exp->exp_hp_rpcs)); | |
766 | obd_destroy_export(exp); | |
767 | class_decref(obd, "export", exp); | |
768 | ||
769 | OBD_FREE_RCU(exp, sizeof(*exp), &exp->exp_handle); | |
d7e09d03 PT |
770 | } |
771 | ||
772 | static void export_handle_addref(void *export) | |
773 | { | |
774 | class_export_get(export); | |
775 | } | |
776 | ||
777 | static struct portals_handle_ops export_handle_ops = { | |
778 | .hop_addref = export_handle_addref, | |
779 | .hop_free = NULL, | |
780 | }; | |
781 | ||
782 | struct obd_export *class_export_get(struct obd_export *exp) | |
783 | { | |
784 | atomic_inc(&exp->exp_refcount); | |
785 | CDEBUG(D_INFO, "GETting export %p : new refcount %d\n", exp, | |
786 | atomic_read(&exp->exp_refcount)); | |
787 | return exp; | |
788 | } | |
789 | EXPORT_SYMBOL(class_export_get); | |
790 | ||
791 | void class_export_put(struct obd_export *exp) | |
792 | { | |
793 | LASSERT(exp != NULL); | |
794 | LASSERT_ATOMIC_GT_LT(&exp->exp_refcount, 0, LI_POISON); | |
795 | CDEBUG(D_INFO, "PUTting export %p : new refcount %d\n", exp, | |
796 | atomic_read(&exp->exp_refcount) - 1); | |
797 | ||
798 | if (atomic_dec_and_test(&exp->exp_refcount)) { | |
799 | LASSERT(!list_empty(&exp->exp_obd_chain)); | |
800 | CDEBUG(D_IOCTL, "final put %p/%s\n", | |
801 | exp, exp->exp_client_uuid.uuid); | |
802 | ||
803 | /* release nid stat refererence */ | |
804 | lprocfs_exp_cleanup(exp); | |
805 | ||
806 | obd_zombie_export_add(exp); | |
807 | } | |
808 | } | |
809 | EXPORT_SYMBOL(class_export_put); | |
810 | ||
811 | /* Creates a new export, adds it to the hash table, and returns a | |
812 | * pointer to it. The refcount is 2: one for the hash reference, and | |
813 | * one for the pointer returned by this function. */ | |
814 | struct obd_export *class_new_export(struct obd_device *obd, | |
815 | struct obd_uuid *cluuid) | |
816 | { | |
817 | struct obd_export *export; | |
6da6eabe | 818 | struct cfs_hash *hash = NULL; |
d7e09d03 | 819 | int rc = 0; |
d7e09d03 PT |
820 | |
821 | OBD_ALLOC_PTR(export); | |
822 | if (!export) | |
823 | return ERR_PTR(-ENOMEM); | |
824 | ||
825 | export->exp_conn_cnt = 0; | |
826 | export->exp_lock_hash = NULL; | |
827 | export->exp_flock_hash = NULL; | |
828 | atomic_set(&export->exp_refcount, 2); | |
829 | atomic_set(&export->exp_rpc_count, 0); | |
830 | atomic_set(&export->exp_cb_count, 0); | |
831 | atomic_set(&export->exp_locks_count, 0); | |
832 | #if LUSTRE_TRACKS_LOCK_EXP_REFS | |
833 | INIT_LIST_HEAD(&export->exp_locks_list); | |
834 | spin_lock_init(&export->exp_locks_list_guard); | |
835 | #endif | |
836 | atomic_set(&export->exp_replay_count, 0); | |
837 | export->exp_obd = obd; | |
838 | INIT_LIST_HEAD(&export->exp_outstanding_replies); | |
839 | spin_lock_init(&export->exp_uncommitted_replies_lock); | |
840 | INIT_LIST_HEAD(&export->exp_uncommitted_replies); | |
841 | INIT_LIST_HEAD(&export->exp_req_replay_queue); | |
842 | INIT_LIST_HEAD(&export->exp_handle.h_link); | |
843 | INIT_LIST_HEAD(&export->exp_hp_rpcs); | |
844 | class_handle_hash(&export->exp_handle, &export_handle_ops); | |
7264b8a5 | 845 | export->exp_last_request_time = get_seconds(); |
d7e09d03 PT |
846 | spin_lock_init(&export->exp_lock); |
847 | spin_lock_init(&export->exp_rpc_lock); | |
848 | INIT_HLIST_NODE(&export->exp_uuid_hash); | |
849 | INIT_HLIST_NODE(&export->exp_nid_hash); | |
850 | spin_lock_init(&export->exp_bl_list_lock); | |
851 | INIT_LIST_HEAD(&export->exp_bl_list); | |
852 | ||
853 | export->exp_sp_peer = LUSTRE_SP_ANY; | |
854 | export->exp_flvr.sf_rpc = SPTLRPC_FLVR_INVALID; | |
855 | export->exp_client_uuid = *cluuid; | |
856 | obd_init_export(export); | |
857 | ||
858 | spin_lock(&obd->obd_dev_lock); | |
859 | /* shouldn't happen, but might race */ | |
860 | if (obd->obd_stopping) | |
861 | GOTO(exit_unlock, rc = -ENODEV); | |
862 | ||
863 | hash = cfs_hash_getref(obd->obd_uuid_hash); | |
864 | if (hash == NULL) | |
865 | GOTO(exit_unlock, rc = -ENODEV); | |
866 | spin_unlock(&obd->obd_dev_lock); | |
867 | ||
868 | if (!obd_uuid_equals(cluuid, &obd->obd_uuid)) { | |
869 | rc = cfs_hash_add_unique(hash, cluuid, &export->exp_uuid_hash); | |
870 | if (rc != 0) { | |
871 | LCONSOLE_WARN("%s: denying duplicate export for %s, %d\n", | |
872 | obd->obd_name, cluuid->uuid, rc); | |
873 | GOTO(exit_err, rc = -EALREADY); | |
874 | } | |
875 | } | |
876 | ||
877 | spin_lock(&obd->obd_dev_lock); | |
878 | if (obd->obd_stopping) { | |
879 | cfs_hash_del(hash, cluuid, &export->exp_uuid_hash); | |
880 | GOTO(exit_unlock, rc = -ENODEV); | |
881 | } | |
882 | ||
883 | class_incref(obd, "export", export); | |
884 | list_add(&export->exp_obd_chain, &export->exp_obd->obd_exports); | |
885 | list_add_tail(&export->exp_obd_chain_timed, | |
886 | &export->exp_obd->obd_exports_timed); | |
887 | export->exp_obd->obd_num_exports++; | |
888 | spin_unlock(&obd->obd_dev_lock); | |
889 | cfs_hash_putref(hash); | |
0a3bdb00 | 890 | return export; |
d7e09d03 PT |
891 | |
892 | exit_unlock: | |
893 | spin_unlock(&obd->obd_dev_lock); | |
894 | exit_err: | |
895 | if (hash) | |
896 | cfs_hash_putref(hash); | |
897 | class_handle_unhash(&export->exp_handle); | |
898 | LASSERT(hlist_unhashed(&export->exp_uuid_hash)); | |
899 | obd_destroy_export(export); | |
900 | OBD_FREE_PTR(export); | |
901 | return ERR_PTR(rc); | |
902 | } | |
903 | EXPORT_SYMBOL(class_new_export); | |
904 | ||
905 | void class_unlink_export(struct obd_export *exp) | |
906 | { | |
907 | class_handle_unhash(&exp->exp_handle); | |
908 | ||
909 | spin_lock(&exp->exp_obd->obd_dev_lock); | |
910 | /* delete an uuid-export hashitem from hashtables */ | |
911 | if (!hlist_unhashed(&exp->exp_uuid_hash)) | |
912 | cfs_hash_del(exp->exp_obd->obd_uuid_hash, | |
913 | &exp->exp_client_uuid, | |
914 | &exp->exp_uuid_hash); | |
915 | ||
916 | list_move(&exp->exp_obd_chain, &exp->exp_obd->obd_unlinked_exports); | |
917 | list_del_init(&exp->exp_obd_chain_timed); | |
918 | exp->exp_obd->obd_num_exports--; | |
919 | spin_unlock(&exp->exp_obd->obd_dev_lock); | |
920 | class_export_put(exp); | |
921 | } | |
922 | EXPORT_SYMBOL(class_unlink_export); | |
923 | ||
924 | /* Import management functions */ | |
925 | void class_import_destroy(struct obd_import *imp) | |
926 | { | |
d7e09d03 PT |
927 | CDEBUG(D_IOCTL, "destroying import %p for %s\n", imp, |
928 | imp->imp_obd->obd_name); | |
929 | ||
930 | LASSERT_ATOMIC_ZERO(&imp->imp_refcount); | |
931 | ||
932 | ptlrpc_put_connection_superhack(imp->imp_connection); | |
933 | ||
934 | while (!list_empty(&imp->imp_conn_list)) { | |
935 | struct obd_import_conn *imp_conn; | |
936 | ||
937 | imp_conn = list_entry(imp->imp_conn_list.next, | |
938 | struct obd_import_conn, oic_item); | |
939 | list_del_init(&imp_conn->oic_item); | |
940 | ptlrpc_put_connection_superhack(imp_conn->oic_conn); | |
941 | OBD_FREE(imp_conn, sizeof(*imp_conn)); | |
942 | } | |
943 | ||
944 | LASSERT(imp->imp_sec == NULL); | |
945 | class_decref(imp->imp_obd, "import", imp); | |
946 | OBD_FREE_RCU(imp, sizeof(*imp), &imp->imp_handle); | |
d7e09d03 PT |
947 | } |
948 | ||
949 | static void import_handle_addref(void *import) | |
950 | { | |
951 | class_import_get(import); | |
952 | } | |
953 | ||
954 | static struct portals_handle_ops import_handle_ops = { | |
955 | .hop_addref = import_handle_addref, | |
956 | .hop_free = NULL, | |
957 | }; | |
958 | ||
959 | struct obd_import *class_import_get(struct obd_import *import) | |
960 | { | |
961 | atomic_inc(&import->imp_refcount); | |
962 | CDEBUG(D_INFO, "import %p refcount=%d obd=%s\n", import, | |
963 | atomic_read(&import->imp_refcount), | |
964 | import->imp_obd->obd_name); | |
965 | return import; | |
966 | } | |
967 | EXPORT_SYMBOL(class_import_get); | |
968 | ||
969 | void class_import_put(struct obd_import *imp) | |
970 | { | |
d7e09d03 PT |
971 | LASSERT(list_empty(&imp->imp_zombie_chain)); |
972 | LASSERT_ATOMIC_GT_LT(&imp->imp_refcount, 0, LI_POISON); | |
973 | ||
974 | CDEBUG(D_INFO, "import %p refcount=%d obd=%s\n", imp, | |
975 | atomic_read(&imp->imp_refcount) - 1, | |
976 | imp->imp_obd->obd_name); | |
977 | ||
978 | if (atomic_dec_and_test(&imp->imp_refcount)) { | |
979 | CDEBUG(D_INFO, "final put import %p\n", imp); | |
980 | obd_zombie_import_add(imp); | |
981 | } | |
982 | ||
983 | /* catch possible import put race */ | |
984 | LASSERT_ATOMIC_GE_LT(&imp->imp_refcount, 0, LI_POISON); | |
d7e09d03 PT |
985 | } |
986 | EXPORT_SYMBOL(class_import_put); | |
987 | ||
988 | static void init_imp_at(struct imp_at *at) { | |
989 | int i; | |
990 | at_init(&at->iat_net_latency, 0, 0); | |
991 | for (i = 0; i < IMP_AT_MAX_PORTALS; i++) { | |
992 | /* max service estimates are tracked on the server side, so | |
993 | don't use the AT history here, just use the last reported | |
994 | val. (But keep hist for proc histogram, worst_ever) */ | |
995 | at_init(&at->iat_service_estimate[i], INITIAL_CONNECT_TIMEOUT, | |
996 | AT_FLG_NOHIST); | |
997 | } | |
998 | } | |
999 | ||
1000 | struct obd_import *class_new_import(struct obd_device *obd) | |
1001 | { | |
1002 | struct obd_import *imp; | |
1003 | ||
1004 | OBD_ALLOC(imp, sizeof(*imp)); | |
1005 | if (imp == NULL) | |
1006 | return NULL; | |
1007 | ||
1008 | INIT_LIST_HEAD(&imp->imp_pinger_chain); | |
1009 | INIT_LIST_HEAD(&imp->imp_zombie_chain); | |
1010 | INIT_LIST_HEAD(&imp->imp_replay_list); | |
1011 | INIT_LIST_HEAD(&imp->imp_sending_list); | |
1012 | INIT_LIST_HEAD(&imp->imp_delayed_list); | |
63d42578 HZ |
1013 | INIT_LIST_HEAD(&imp->imp_committed_list); |
1014 | imp->imp_replay_cursor = &imp->imp_committed_list; | |
d7e09d03 PT |
1015 | spin_lock_init(&imp->imp_lock); |
1016 | imp->imp_last_success_conn = 0; | |
1017 | imp->imp_state = LUSTRE_IMP_NEW; | |
1018 | imp->imp_obd = class_incref(obd, "import", imp); | |
1019 | mutex_init(&imp->imp_sec_mutex); | |
1020 | init_waitqueue_head(&imp->imp_recovery_waitq); | |
1021 | ||
1022 | atomic_set(&imp->imp_refcount, 2); | |
1023 | atomic_set(&imp->imp_unregistering, 0); | |
1024 | atomic_set(&imp->imp_inflight, 0); | |
1025 | atomic_set(&imp->imp_replay_inflight, 0); | |
1026 | atomic_set(&imp->imp_inval_count, 0); | |
1027 | INIT_LIST_HEAD(&imp->imp_conn_list); | |
1028 | INIT_LIST_HEAD(&imp->imp_handle.h_link); | |
1029 | class_handle_hash(&imp->imp_handle, &import_handle_ops); | |
1030 | init_imp_at(&imp->imp_at); | |
1031 | ||
1032 | /* the default magic is V2, will be used in connect RPC, and | |
1033 | * then adjusted according to the flags in request/reply. */ | |
1034 | imp->imp_msg_magic = LUSTRE_MSG_MAGIC_V2; | |
1035 | ||
1036 | return imp; | |
1037 | } | |
1038 | EXPORT_SYMBOL(class_new_import); | |
1039 | ||
1040 | void class_destroy_import(struct obd_import *import) | |
1041 | { | |
1042 | LASSERT(import != NULL); | |
1043 | LASSERT(import != LP_POISON); | |
1044 | ||
1045 | class_handle_unhash(&import->imp_handle); | |
1046 | ||
1047 | spin_lock(&import->imp_lock); | |
1048 | import->imp_generation++; | |
1049 | spin_unlock(&import->imp_lock); | |
1050 | class_import_put(import); | |
1051 | } | |
1052 | EXPORT_SYMBOL(class_destroy_import); | |
1053 | ||
1054 | #if LUSTRE_TRACKS_LOCK_EXP_REFS | |
1055 | ||
1056 | void __class_export_add_lock_ref(struct obd_export *exp, struct ldlm_lock *lock) | |
1057 | { | |
1058 | spin_lock(&exp->exp_locks_list_guard); | |
1059 | ||
1060 | LASSERT(lock->l_exp_refs_nr >= 0); | |
1061 | ||
1062 | if (lock->l_exp_refs_target != NULL && | |
1063 | lock->l_exp_refs_target != exp) { | |
1064 | LCONSOLE_WARN("setting export %p for lock %p which already has export %p\n", | |
1065 | exp, lock, lock->l_exp_refs_target); | |
1066 | } | |
1067 | if ((lock->l_exp_refs_nr ++) == 0) { | |
1068 | list_add(&lock->l_exp_refs_link, &exp->exp_locks_list); | |
1069 | lock->l_exp_refs_target = exp; | |
1070 | } | |
1071 | CDEBUG(D_INFO, "lock = %p, export = %p, refs = %u\n", | |
1072 | lock, exp, lock->l_exp_refs_nr); | |
1073 | spin_unlock(&exp->exp_locks_list_guard); | |
1074 | } | |
1075 | EXPORT_SYMBOL(__class_export_add_lock_ref); | |
1076 | ||
1077 | void __class_export_del_lock_ref(struct obd_export *exp, struct ldlm_lock *lock) | |
1078 | { | |
1079 | spin_lock(&exp->exp_locks_list_guard); | |
1080 | LASSERT(lock->l_exp_refs_nr > 0); | |
1081 | if (lock->l_exp_refs_target != exp) { | |
1082 | LCONSOLE_WARN("lock %p, " | |
1083 | "mismatching export pointers: %p, %p\n", | |
1084 | lock, lock->l_exp_refs_target, exp); | |
1085 | } | |
1086 | if (-- lock->l_exp_refs_nr == 0) { | |
1087 | list_del_init(&lock->l_exp_refs_link); | |
1088 | lock->l_exp_refs_target = NULL; | |
1089 | } | |
1090 | CDEBUG(D_INFO, "lock = %p, export = %p, refs = %u\n", | |
1091 | lock, exp, lock->l_exp_refs_nr); | |
1092 | spin_unlock(&exp->exp_locks_list_guard); | |
1093 | } | |
1094 | EXPORT_SYMBOL(__class_export_del_lock_ref); | |
1095 | #endif | |
1096 | ||
1097 | /* A connection defines an export context in which preallocation can | |
1098 | be managed. This releases the export pointer reference, and returns | |
1099 | the export handle, so the export refcount is 1 when this function | |
1100 | returns. */ | |
1101 | int class_connect(struct lustre_handle *conn, struct obd_device *obd, | |
1102 | struct obd_uuid *cluuid) | |
1103 | { | |
1104 | struct obd_export *export; | |
1105 | LASSERT(conn != NULL); | |
1106 | LASSERT(obd != NULL); | |
1107 | LASSERT(cluuid != NULL); | |
d7e09d03 PT |
1108 | |
1109 | export = class_new_export(obd, cluuid); | |
1110 | if (IS_ERR(export)) | |
0a3bdb00 | 1111 | return PTR_ERR(export); |
d7e09d03 PT |
1112 | |
1113 | conn->cookie = export->exp_handle.h_cookie; | |
1114 | class_export_put(export); | |
1115 | ||
55f5a824 | 1116 | CDEBUG(D_IOCTL, "connect: client %s, cookie %#llx\n", |
d7e09d03 | 1117 | cluuid->uuid, conn->cookie); |
0a3bdb00 | 1118 | return 0; |
d7e09d03 PT |
1119 | } |
1120 | EXPORT_SYMBOL(class_connect); | |
1121 | ||
1122 | /* if export is involved in recovery then clean up related things */ | |
1123 | void class_export_recovery_cleanup(struct obd_export *exp) | |
1124 | { | |
1125 | struct obd_device *obd = exp->exp_obd; | |
1126 | ||
1127 | spin_lock(&obd->obd_recovery_task_lock); | |
1128 | if (exp->exp_delayed) | |
1129 | obd->obd_delayed_clients--; | |
1130 | if (obd->obd_recovering) { | |
1131 | if (exp->exp_in_recovery) { | |
1132 | spin_lock(&exp->exp_lock); | |
1133 | exp->exp_in_recovery = 0; | |
1134 | spin_unlock(&exp->exp_lock); | |
1135 | LASSERT_ATOMIC_POS(&obd->obd_connected_clients); | |
1136 | atomic_dec(&obd->obd_connected_clients); | |
1137 | } | |
1138 | ||
1139 | /* if called during recovery then should update | |
1140 | * obd_stale_clients counter, | |
1141 | * lightweight exports are not counted */ | |
1142 | if (exp->exp_failed && | |
1143 | (exp_connect_flags(exp) & OBD_CONNECT_LIGHTWEIGHT) == 0) | |
1144 | exp->exp_obd->obd_stale_clients++; | |
1145 | } | |
1146 | spin_unlock(&obd->obd_recovery_task_lock); | |
1147 | /** Cleanup req replay fields */ | |
1148 | if (exp->exp_req_replay_needed) { | |
1149 | spin_lock(&exp->exp_lock); | |
1150 | exp->exp_req_replay_needed = 0; | |
1151 | spin_unlock(&exp->exp_lock); | |
1152 | LASSERT(atomic_read(&obd->obd_req_replay_clients)); | |
1153 | atomic_dec(&obd->obd_req_replay_clients); | |
1154 | } | |
1155 | /** Cleanup lock replay data */ | |
1156 | if (exp->exp_lock_replay_needed) { | |
1157 | spin_lock(&exp->exp_lock); | |
1158 | exp->exp_lock_replay_needed = 0; | |
1159 | spin_unlock(&exp->exp_lock); | |
1160 | LASSERT(atomic_read(&obd->obd_lock_replay_clients)); | |
1161 | atomic_dec(&obd->obd_lock_replay_clients); | |
1162 | } | |
1163 | } | |
1164 | ||
1165 | /* This function removes 1-3 references from the export: | |
1166 | * 1 - for export pointer passed | |
1167 | * and if disconnect really need | |
1168 | * 2 - removing from hash | |
1169 | * 3 - in client_unlink_export | |
1170 | * The export pointer passed to this function can destroyed */ | |
1171 | int class_disconnect(struct obd_export *export) | |
1172 | { | |
1173 | int already_disconnected; | |
d7e09d03 PT |
1174 | |
1175 | if (export == NULL) { | |
1176 | CWARN("attempting to free NULL export %p\n", export); | |
0a3bdb00 | 1177 | return -EINVAL; |
d7e09d03 PT |
1178 | } |
1179 | ||
1180 | spin_lock(&export->exp_lock); | |
1181 | already_disconnected = export->exp_disconnected; | |
1182 | export->exp_disconnected = 1; | |
1183 | spin_unlock(&export->exp_lock); | |
1184 | ||
1185 | /* class_cleanup(), abort_recovery(), and class_fail_export() | |
1186 | * all end up in here, and if any of them race we shouldn't | |
1187 | * call extra class_export_puts(). */ | |
1188 | if (already_disconnected) { | |
1189 | LASSERT(hlist_unhashed(&export->exp_nid_hash)); | |
1190 | GOTO(no_disconn, already_disconnected); | |
1191 | } | |
1192 | ||
55f5a824 | 1193 | CDEBUG(D_IOCTL, "disconnect: cookie %#llx\n", |
d7e09d03 PT |
1194 | export->exp_handle.h_cookie); |
1195 | ||
1196 | if (!hlist_unhashed(&export->exp_nid_hash)) | |
1197 | cfs_hash_del(export->exp_obd->obd_nid_hash, | |
1198 | &export->exp_connection->c_peer.nid, | |
1199 | &export->exp_nid_hash); | |
1200 | ||
1201 | class_export_recovery_cleanup(export); | |
1202 | class_unlink_export(export); | |
1203 | no_disconn: | |
1204 | class_export_put(export); | |
0a3bdb00 | 1205 | return 0; |
d7e09d03 PT |
1206 | } |
1207 | EXPORT_SYMBOL(class_disconnect); | |
1208 | ||
1209 | /* Return non-zero for a fully connected export */ | |
1210 | int class_connected_export(struct obd_export *exp) | |
1211 | { | |
1212 | if (exp) { | |
1213 | int connected; | |
1214 | spin_lock(&exp->exp_lock); | |
1215 | connected = (exp->exp_conn_cnt > 0); | |
1216 | spin_unlock(&exp->exp_lock); | |
1217 | return connected; | |
1218 | } | |
1219 | return 0; | |
1220 | } | |
1221 | EXPORT_SYMBOL(class_connected_export); | |
1222 | ||
1223 | static void class_disconnect_export_list(struct list_head *list, | |
1224 | enum obd_option flags) | |
1225 | { | |
1226 | int rc; | |
1227 | struct obd_export *exp; | |
d7e09d03 PT |
1228 | |
1229 | /* It's possible that an export may disconnect itself, but | |
1230 | * nothing else will be added to this list. */ | |
1231 | while (!list_empty(list)) { | |
1232 | exp = list_entry(list->next, struct obd_export, | |
1233 | exp_obd_chain); | |
1234 | /* need for safe call CDEBUG after obd_disconnect */ | |
1235 | class_export_get(exp); | |
1236 | ||
1237 | spin_lock(&exp->exp_lock); | |
1238 | exp->exp_flags = flags; | |
1239 | spin_unlock(&exp->exp_lock); | |
1240 | ||
1241 | if (obd_uuid_equals(&exp->exp_client_uuid, | |
1242 | &exp->exp_obd->obd_uuid)) { | |
1243 | CDEBUG(D_HA, | |
1244 | "exp %p export uuid == obd uuid, don't discon\n", | |
1245 | exp); | |
1246 | /* Need to delete this now so we don't end up pointing | |
1247 | * to work_list later when this export is cleaned up. */ | |
1248 | list_del_init(&exp->exp_obd_chain); | |
1249 | class_export_put(exp); | |
1250 | continue; | |
1251 | } | |
1252 | ||
1253 | class_export_get(exp); | |
1254 | CDEBUG(D_HA, "%s: disconnecting export at %s (%p), " | |
1255 | "last request at "CFS_TIME_T"\n", | |
1256 | exp->exp_obd->obd_name, obd_export_nid2str(exp), | |
1257 | exp, exp->exp_last_request_time); | |
1258 | /* release one export reference anyway */ | |
1259 | rc = obd_disconnect(exp); | |
1260 | ||
1261 | CDEBUG(D_HA, "disconnected export at %s (%p): rc %d\n", | |
1262 | obd_export_nid2str(exp), exp, rc); | |
1263 | class_export_put(exp); | |
1264 | } | |
d7e09d03 PT |
1265 | } |
1266 | ||
1267 | void class_disconnect_exports(struct obd_device *obd) | |
1268 | { | |
1269 | struct list_head work_list; | |
d7e09d03 PT |
1270 | |
1271 | /* Move all of the exports from obd_exports to a work list, en masse. */ | |
1272 | INIT_LIST_HEAD(&work_list); | |
1273 | spin_lock(&obd->obd_dev_lock); | |
1274 | list_splice_init(&obd->obd_exports, &work_list); | |
1275 | list_splice_init(&obd->obd_delayed_exports, &work_list); | |
1276 | spin_unlock(&obd->obd_dev_lock); | |
1277 | ||
1278 | if (!list_empty(&work_list)) { | |
1279 | CDEBUG(D_HA, "OBD device %d (%p) has exports, " | |
1280 | "disconnecting them\n", obd->obd_minor, obd); | |
1281 | class_disconnect_export_list(&work_list, | |
1282 | exp_flags_from_obd(obd)); | |
1283 | } else | |
1284 | CDEBUG(D_HA, "OBD device %d (%p) has no exports\n", | |
1285 | obd->obd_minor, obd); | |
d7e09d03 PT |
1286 | } |
1287 | EXPORT_SYMBOL(class_disconnect_exports); | |
1288 | ||
1289 | /* Remove exports that have not completed recovery. | |
1290 | */ | |
1291 | void class_disconnect_stale_exports(struct obd_device *obd, | |
1292 | int (*test_export)(struct obd_export *)) | |
1293 | { | |
1294 | struct list_head work_list; | |
1295 | struct obd_export *exp, *n; | |
1296 | int evicted = 0; | |
d7e09d03 PT |
1297 | |
1298 | INIT_LIST_HEAD(&work_list); | |
1299 | spin_lock(&obd->obd_dev_lock); | |
1300 | list_for_each_entry_safe(exp, n, &obd->obd_exports, | |
1301 | exp_obd_chain) { | |
1302 | /* don't count self-export as client */ | |
1303 | if (obd_uuid_equals(&exp->exp_client_uuid, | |
1304 | &exp->exp_obd->obd_uuid)) | |
1305 | continue; | |
1306 | ||
1307 | /* don't evict clients which have no slot in last_rcvd | |
1308 | * (e.g. lightweight connection) */ | |
1309 | if (exp->exp_target_data.ted_lr_idx == -1) | |
1310 | continue; | |
1311 | ||
1312 | spin_lock(&exp->exp_lock); | |
1313 | if (exp->exp_failed || test_export(exp)) { | |
1314 | spin_unlock(&exp->exp_lock); | |
1315 | continue; | |
1316 | } | |
1317 | exp->exp_failed = 1; | |
1318 | spin_unlock(&exp->exp_lock); | |
1319 | ||
1320 | list_move(&exp->exp_obd_chain, &work_list); | |
1321 | evicted++; | |
1322 | CDEBUG(D_HA, "%s: disconnect stale client %s@%s\n", | |
1323 | obd->obd_name, exp->exp_client_uuid.uuid, | |
1324 | exp->exp_connection == NULL ? "<unknown>" : | |
1325 | libcfs_nid2str(exp->exp_connection->c_peer.nid)); | |
1326 | print_export_data(exp, "EVICTING", 0); | |
1327 | } | |
1328 | spin_unlock(&obd->obd_dev_lock); | |
1329 | ||
1330 | if (evicted) | |
1331 | LCONSOLE_WARN("%s: disconnecting %d stale clients\n", | |
1332 | obd->obd_name, evicted); | |
1333 | ||
1334 | class_disconnect_export_list(&work_list, exp_flags_from_obd(obd) | | |
1335 | OBD_OPT_ABORT_RECOV); | |
d7e09d03 PT |
1336 | } |
1337 | EXPORT_SYMBOL(class_disconnect_stale_exports); | |
1338 | ||
1339 | void class_fail_export(struct obd_export *exp) | |
1340 | { | |
1341 | int rc, already_failed; | |
1342 | ||
1343 | spin_lock(&exp->exp_lock); | |
1344 | already_failed = exp->exp_failed; | |
1345 | exp->exp_failed = 1; | |
1346 | spin_unlock(&exp->exp_lock); | |
1347 | ||
1348 | if (already_failed) { | |
1349 | CDEBUG(D_HA, "disconnecting dead export %p/%s; skipping\n", | |
1350 | exp, exp->exp_client_uuid.uuid); | |
1351 | return; | |
1352 | } | |
1353 | ||
1354 | CDEBUG(D_HA, "disconnecting export %p/%s\n", | |
1355 | exp, exp->exp_client_uuid.uuid); | |
1356 | ||
1357 | if (obd_dump_on_timeout) | |
1358 | libcfs_debug_dumplog(); | |
1359 | ||
1360 | /* need for safe call CDEBUG after obd_disconnect */ | |
1361 | class_export_get(exp); | |
1362 | ||
1363 | /* Most callers into obd_disconnect are removing their own reference | |
1364 | * (request, for example) in addition to the one from the hash table. | |
1365 | * We don't have such a reference here, so make one. */ | |
1366 | class_export_get(exp); | |
1367 | rc = obd_disconnect(exp); | |
1368 | if (rc) | |
1369 | CERROR("disconnecting export %p failed: %d\n", exp, rc); | |
1370 | else | |
1371 | CDEBUG(D_HA, "disconnected export %p/%s\n", | |
1372 | exp, exp->exp_client_uuid.uuid); | |
1373 | class_export_put(exp); | |
1374 | } | |
1375 | EXPORT_SYMBOL(class_fail_export); | |
1376 | ||
1377 | char *obd_export_nid2str(struct obd_export *exp) | |
1378 | { | |
1379 | if (exp->exp_connection != NULL) | |
1380 | return libcfs_nid2str(exp->exp_connection->c_peer.nid); | |
1381 | ||
1382 | return "(no nid)"; | |
1383 | } | |
1384 | EXPORT_SYMBOL(obd_export_nid2str); | |
1385 | ||
1386 | int obd_export_evict_by_nid(struct obd_device *obd, const char *nid) | |
1387 | { | |
6da6eabe | 1388 | struct cfs_hash *nid_hash; |
d7e09d03 PT |
1389 | struct obd_export *doomed_exp = NULL; |
1390 | int exports_evicted = 0; | |
1391 | ||
1392 | lnet_nid_t nid_key = libcfs_str2nid((char *)nid); | |
1393 | ||
1394 | spin_lock(&obd->obd_dev_lock); | |
1395 | /* umount has run already, so evict thread should leave | |
1396 | * its task to umount thread now */ | |
1397 | if (obd->obd_stopping) { | |
1398 | spin_unlock(&obd->obd_dev_lock); | |
1399 | return exports_evicted; | |
1400 | } | |
1401 | nid_hash = obd->obd_nid_hash; | |
1402 | cfs_hash_getref(nid_hash); | |
1403 | spin_unlock(&obd->obd_dev_lock); | |
1404 | ||
1405 | do { | |
1406 | doomed_exp = cfs_hash_lookup(nid_hash, &nid_key); | |
1407 | if (doomed_exp == NULL) | |
1408 | break; | |
1409 | ||
1410 | LASSERTF(doomed_exp->exp_connection->c_peer.nid == nid_key, | |
1411 | "nid %s found, wanted nid %s, requested nid %s\n", | |
1412 | obd_export_nid2str(doomed_exp), | |
1413 | libcfs_nid2str(nid_key), nid); | |
1414 | LASSERTF(doomed_exp != obd->obd_self_export, | |
1415 | "self-export is hashed by NID?\n"); | |
1416 | exports_evicted++; | |
1417 | LCONSOLE_WARN("%s: evicting %s (at %s) by administrative " | |
1418 | "request\n", obd->obd_name, | |
1419 | obd_uuid2str(&doomed_exp->exp_client_uuid), | |
1420 | obd_export_nid2str(doomed_exp)); | |
1421 | class_fail_export(doomed_exp); | |
1422 | class_export_put(doomed_exp); | |
1423 | } while (1); | |
1424 | ||
1425 | cfs_hash_putref(nid_hash); | |
1426 | ||
1427 | if (!exports_evicted) | |
1428 | CDEBUG(D_HA,"%s: can't disconnect NID '%s': no exports found\n", | |
1429 | obd->obd_name, nid); | |
1430 | return exports_evicted; | |
1431 | } | |
1432 | EXPORT_SYMBOL(obd_export_evict_by_nid); | |
1433 | ||
1434 | int obd_export_evict_by_uuid(struct obd_device *obd, const char *uuid) | |
1435 | { | |
6da6eabe | 1436 | struct cfs_hash *uuid_hash; |
d7e09d03 PT |
1437 | struct obd_export *doomed_exp = NULL; |
1438 | struct obd_uuid doomed_uuid; | |
1439 | int exports_evicted = 0; | |
1440 | ||
1441 | spin_lock(&obd->obd_dev_lock); | |
1442 | if (obd->obd_stopping) { | |
1443 | spin_unlock(&obd->obd_dev_lock); | |
1444 | return exports_evicted; | |
1445 | } | |
1446 | uuid_hash = obd->obd_uuid_hash; | |
1447 | cfs_hash_getref(uuid_hash); | |
1448 | spin_unlock(&obd->obd_dev_lock); | |
1449 | ||
1450 | obd_str2uuid(&doomed_uuid, uuid); | |
1451 | if (obd_uuid_equals(&doomed_uuid, &obd->obd_uuid)) { | |
1452 | CERROR("%s: can't evict myself\n", obd->obd_name); | |
1453 | cfs_hash_putref(uuid_hash); | |
1454 | return exports_evicted; | |
1455 | } | |
1456 | ||
1457 | doomed_exp = cfs_hash_lookup(uuid_hash, &doomed_uuid); | |
1458 | ||
1459 | if (doomed_exp == NULL) { | |
1460 | CERROR("%s: can't disconnect %s: no exports found\n", | |
1461 | obd->obd_name, uuid); | |
1462 | } else { | |
c4f39553 | 1463 | CWARN("%s: evicting %s at administrative request\n", |
d7e09d03 PT |
1464 | obd->obd_name, doomed_exp->exp_client_uuid.uuid); |
1465 | class_fail_export(doomed_exp); | |
1466 | class_export_put(doomed_exp); | |
1467 | exports_evicted++; | |
1468 | } | |
1469 | cfs_hash_putref(uuid_hash); | |
1470 | ||
1471 | return exports_evicted; | |
1472 | } | |
1473 | EXPORT_SYMBOL(obd_export_evict_by_uuid); | |
1474 | ||
1475 | #if LUSTRE_TRACKS_LOCK_EXP_REFS | |
1476 | void (*class_export_dump_hook)(struct obd_export*) = NULL; | |
1477 | EXPORT_SYMBOL(class_export_dump_hook); | |
1478 | #endif | |
1479 | ||
1480 | static void print_export_data(struct obd_export *exp, const char *status, | |
1481 | int locks) | |
1482 | { | |
1483 | struct ptlrpc_reply_state *rs; | |
1484 | struct ptlrpc_reply_state *first_reply = NULL; | |
1485 | int nreplies = 0; | |
1486 | ||
1487 | spin_lock(&exp->exp_lock); | |
1488 | list_for_each_entry(rs, &exp->exp_outstanding_replies, | |
1489 | rs_exp_list) { | |
1490 | if (nreplies == 0) | |
1491 | first_reply = rs; | |
1492 | nreplies++; | |
1493 | } | |
1494 | spin_unlock(&exp->exp_lock); | |
1495 | ||
b0f5aad5 | 1496 | CDEBUG(D_HA, "%s: %s %p %s %s %d (%d %d %d) %d %d %d %d: %p %s %llu\n", |
d7e09d03 PT |
1497 | exp->exp_obd->obd_name, status, exp, exp->exp_client_uuid.uuid, |
1498 | obd_export_nid2str(exp), atomic_read(&exp->exp_refcount), | |
1499 | atomic_read(&exp->exp_rpc_count), | |
1500 | atomic_read(&exp->exp_cb_count), | |
1501 | atomic_read(&exp->exp_locks_count), | |
1502 | exp->exp_disconnected, exp->exp_delayed, exp->exp_failed, | |
1503 | nreplies, first_reply, nreplies > 3 ? "..." : "", | |
1504 | exp->exp_last_committed); | |
1505 | #if LUSTRE_TRACKS_LOCK_EXP_REFS | |
1506 | if (locks && class_export_dump_hook != NULL) | |
1507 | class_export_dump_hook(exp); | |
1508 | #endif | |
1509 | } | |
1510 | ||
1511 | void dump_exports(struct obd_device *obd, int locks) | |
1512 | { | |
1513 | struct obd_export *exp; | |
1514 | ||
1515 | spin_lock(&obd->obd_dev_lock); | |
1516 | list_for_each_entry(exp, &obd->obd_exports, exp_obd_chain) | |
1517 | print_export_data(exp, "ACTIVE", locks); | |
1518 | list_for_each_entry(exp, &obd->obd_unlinked_exports, exp_obd_chain) | |
1519 | print_export_data(exp, "UNLINKED", locks); | |
1520 | list_for_each_entry(exp, &obd->obd_delayed_exports, exp_obd_chain) | |
1521 | print_export_data(exp, "DELAYED", locks); | |
1522 | spin_unlock(&obd->obd_dev_lock); | |
1523 | spin_lock(&obd_zombie_impexp_lock); | |
1524 | list_for_each_entry(exp, &obd_zombie_exports, exp_obd_chain) | |
1525 | print_export_data(exp, "ZOMBIE", locks); | |
1526 | spin_unlock(&obd_zombie_impexp_lock); | |
1527 | } | |
1528 | EXPORT_SYMBOL(dump_exports); | |
1529 | ||
1530 | void obd_exports_barrier(struct obd_device *obd) | |
1531 | { | |
1532 | int waited = 2; | |
1533 | LASSERT(list_empty(&obd->obd_exports)); | |
1534 | spin_lock(&obd->obd_dev_lock); | |
1535 | while (!list_empty(&obd->obd_unlinked_exports)) { | |
1536 | spin_unlock(&obd->obd_dev_lock); | |
18fd5baa PT |
1537 | set_current_state(TASK_UNINTERRUPTIBLE); |
1538 | schedule_timeout(cfs_time_seconds(waited)); | |
d7e09d03 PT |
1539 | if (waited > 5 && IS_PO2(waited)) { |
1540 | LCONSOLE_WARN("%s is waiting for obd_unlinked_exports " | |
1541 | "more than %d seconds. " | |
1542 | "The obd refcount = %d. Is it stuck?\n", | |
1543 | obd->obd_name, waited, | |
1544 | atomic_read(&obd->obd_refcount)); | |
1545 | dump_exports(obd, 1); | |
1546 | } | |
1547 | waited *= 2; | |
1548 | spin_lock(&obd->obd_dev_lock); | |
1549 | } | |
1550 | spin_unlock(&obd->obd_dev_lock); | |
1551 | } | |
1552 | EXPORT_SYMBOL(obd_exports_barrier); | |
1553 | ||
1554 | /* Total amount of zombies to be destroyed */ | |
1555 | static int zombies_count = 0; | |
1556 | ||
1557 | /** | |
1558 | * kill zombie imports and exports | |
1559 | */ | |
1560 | void obd_zombie_impexp_cull(void) | |
1561 | { | |
1562 | struct obd_import *import; | |
1563 | struct obd_export *export; | |
d7e09d03 PT |
1564 | |
1565 | do { | |
1566 | spin_lock(&obd_zombie_impexp_lock); | |
1567 | ||
1568 | import = NULL; | |
1569 | if (!list_empty(&obd_zombie_imports)) { | |
1570 | import = list_entry(obd_zombie_imports.next, | |
1571 | struct obd_import, | |
1572 | imp_zombie_chain); | |
1573 | list_del_init(&import->imp_zombie_chain); | |
1574 | } | |
1575 | ||
1576 | export = NULL; | |
1577 | if (!list_empty(&obd_zombie_exports)) { | |
1578 | export = list_entry(obd_zombie_exports.next, | |
1579 | struct obd_export, | |
1580 | exp_obd_chain); | |
1581 | list_del_init(&export->exp_obd_chain); | |
1582 | } | |
1583 | ||
1584 | spin_unlock(&obd_zombie_impexp_lock); | |
1585 | ||
1586 | if (import != NULL) { | |
1587 | class_import_destroy(import); | |
1588 | spin_lock(&obd_zombie_impexp_lock); | |
1589 | zombies_count--; | |
1590 | spin_unlock(&obd_zombie_impexp_lock); | |
1591 | } | |
1592 | ||
1593 | if (export != NULL) { | |
1594 | class_export_destroy(export); | |
1595 | spin_lock(&obd_zombie_impexp_lock); | |
1596 | zombies_count--; | |
1597 | spin_unlock(&obd_zombie_impexp_lock); | |
1598 | } | |
1599 | ||
1600 | cond_resched(); | |
1601 | } while (import != NULL || export != NULL); | |
d7e09d03 PT |
1602 | } |
1603 | ||
1604 | static struct completion obd_zombie_start; | |
1605 | static struct completion obd_zombie_stop; | |
1606 | static unsigned long obd_zombie_flags; | |
1607 | static wait_queue_head_t obd_zombie_waitq; | |
1608 | static pid_t obd_zombie_pid; | |
1609 | ||
1610 | enum { | |
1611 | OBD_ZOMBIE_STOP = 0x0001, | |
1612 | }; | |
1613 | ||
1614 | /** | |
1615 | * check for work for kill zombie import/export thread. | |
1616 | */ | |
1617 | static int obd_zombie_impexp_check(void *arg) | |
1618 | { | |
1619 | int rc; | |
1620 | ||
1621 | spin_lock(&obd_zombie_impexp_lock); | |
1622 | rc = (zombies_count == 0) && | |
1623 | !test_bit(OBD_ZOMBIE_STOP, &obd_zombie_flags); | |
1624 | spin_unlock(&obd_zombie_impexp_lock); | |
1625 | ||
0a3bdb00 | 1626 | return rc; |
d7e09d03 PT |
1627 | } |
1628 | ||
1629 | /** | |
52cf6acf | 1630 | * Add export to the obd_zombie thread and notify it. |
d7e09d03 PT |
1631 | */ |
1632 | static void obd_zombie_export_add(struct obd_export *exp) { | |
1633 | spin_lock(&exp->exp_obd->obd_dev_lock); | |
1634 | LASSERT(!list_empty(&exp->exp_obd_chain)); | |
1635 | list_del_init(&exp->exp_obd_chain); | |
1636 | spin_unlock(&exp->exp_obd->obd_dev_lock); | |
1637 | spin_lock(&obd_zombie_impexp_lock); | |
1638 | zombies_count++; | |
1639 | list_add(&exp->exp_obd_chain, &obd_zombie_exports); | |
1640 | spin_unlock(&obd_zombie_impexp_lock); | |
1641 | ||
1642 | obd_zombie_impexp_notify(); | |
1643 | } | |
1644 | ||
1645 | /** | |
52cf6acf | 1646 | * Add import to the obd_zombie thread and notify it. |
d7e09d03 PT |
1647 | */ |
1648 | static void obd_zombie_import_add(struct obd_import *imp) { | |
1649 | LASSERT(imp->imp_sec == NULL); | |
1650 | LASSERT(imp->imp_rq_pool == NULL); | |
1651 | spin_lock(&obd_zombie_impexp_lock); | |
1652 | LASSERT(list_empty(&imp->imp_zombie_chain)); | |
1653 | zombies_count++; | |
1654 | list_add(&imp->imp_zombie_chain, &obd_zombie_imports); | |
1655 | spin_unlock(&obd_zombie_impexp_lock); | |
1656 | ||
1657 | obd_zombie_impexp_notify(); | |
1658 | } | |
1659 | ||
1660 | /** | |
1661 | * notify import/export destroy thread about new zombie. | |
1662 | */ | |
1663 | static void obd_zombie_impexp_notify(void) | |
1664 | { | |
1665 | /* | |
52cf6acf | 1666 | * Make sure obd_zombie_impexp_thread get this notification. |
d7e09d03 PT |
1667 | * It is possible this signal only get by obd_zombie_barrier, and |
1668 | * barrier gulps this notification and sleeps away and hangs ensues | |
1669 | */ | |
1670 | wake_up_all(&obd_zombie_waitq); | |
1671 | } | |
1672 | ||
1673 | /** | |
1674 | * check whether obd_zombie is idle | |
1675 | */ | |
1676 | static int obd_zombie_is_idle(void) | |
1677 | { | |
1678 | int rc; | |
1679 | ||
1680 | LASSERT(!test_bit(OBD_ZOMBIE_STOP, &obd_zombie_flags)); | |
1681 | spin_lock(&obd_zombie_impexp_lock); | |
1682 | rc = (zombies_count == 0); | |
1683 | spin_unlock(&obd_zombie_impexp_lock); | |
1684 | return rc; | |
1685 | } | |
1686 | ||
1687 | /** | |
1688 | * wait when obd_zombie import/export queues become empty | |
1689 | */ | |
1690 | void obd_zombie_barrier(void) | |
1691 | { | |
1692 | struct l_wait_info lwi = { 0 }; | |
1693 | ||
1694 | if (obd_zombie_pid == current_pid()) | |
1695 | /* don't wait for myself */ | |
1696 | return; | |
1697 | l_wait_event(obd_zombie_waitq, obd_zombie_is_idle(), &lwi); | |
1698 | } | |
1699 | EXPORT_SYMBOL(obd_zombie_barrier); | |
1700 | ||
1701 | ||
1702 | /** | |
1703 | * destroy zombie export/import thread. | |
1704 | */ | |
1705 | static int obd_zombie_impexp_thread(void *unused) | |
1706 | { | |
1707 | unshare_fs_struct(); | |
1708 | complete(&obd_zombie_start); | |
1709 | ||
1710 | obd_zombie_pid = current_pid(); | |
1711 | ||
1712 | while (!test_bit(OBD_ZOMBIE_STOP, &obd_zombie_flags)) { | |
1713 | struct l_wait_info lwi = { 0 }; | |
1714 | ||
1715 | l_wait_event(obd_zombie_waitq, | |
1716 | !obd_zombie_impexp_check(NULL), &lwi); | |
1717 | obd_zombie_impexp_cull(); | |
1718 | ||
1719 | /* | |
1720 | * Notify obd_zombie_barrier callers that queues | |
1721 | * may be empty. | |
1722 | */ | |
1723 | wake_up(&obd_zombie_waitq); | |
1724 | } | |
1725 | ||
1726 | complete(&obd_zombie_stop); | |
1727 | ||
0a3bdb00 | 1728 | return 0; |
d7e09d03 PT |
1729 | } |
1730 | ||
1731 | ||
1732 | /** | |
1733 | * start destroy zombie import/export thread | |
1734 | */ | |
1735 | int obd_zombie_impexp_init(void) | |
1736 | { | |
68b636b6 | 1737 | struct task_struct *task; |
d7e09d03 PT |
1738 | |
1739 | INIT_LIST_HEAD(&obd_zombie_imports); | |
1740 | INIT_LIST_HEAD(&obd_zombie_exports); | |
1741 | spin_lock_init(&obd_zombie_impexp_lock); | |
1742 | init_completion(&obd_zombie_start); | |
1743 | init_completion(&obd_zombie_stop); | |
1744 | init_waitqueue_head(&obd_zombie_waitq); | |
1745 | obd_zombie_pid = 0; | |
1746 | ||
1747 | task = kthread_run(obd_zombie_impexp_thread, NULL, "obd_zombid"); | |
1748 | if (IS_ERR(task)) | |
0a3bdb00 | 1749 | return PTR_ERR(task); |
d7e09d03 PT |
1750 | |
1751 | wait_for_completion(&obd_zombie_start); | |
0a3bdb00 | 1752 | return 0; |
d7e09d03 PT |
1753 | } |
1754 | /** | |
1755 | * stop destroy zombie import/export thread | |
1756 | */ | |
1757 | void obd_zombie_impexp_stop(void) | |
1758 | { | |
1759 | set_bit(OBD_ZOMBIE_STOP, &obd_zombie_flags); | |
1760 | obd_zombie_impexp_notify(); | |
1761 | wait_for_completion(&obd_zombie_stop); | |
1762 | } | |
1763 | ||
1764 | /***** Kernel-userspace comm helpers *******/ | |
1765 | ||
1766 | /* Get length of entire message, including header */ | |
1767 | int kuc_len(int payload_len) | |
1768 | { | |
1769 | return sizeof(struct kuc_hdr) + payload_len; | |
1770 | } | |
1771 | EXPORT_SYMBOL(kuc_len); | |
1772 | ||
1773 | /* Get a pointer to kuc header, given a ptr to the payload | |
1774 | * @param p Pointer to payload area | |
1775 | * @returns Pointer to kuc header | |
1776 | */ | |
1777 | struct kuc_hdr * kuc_ptr(void *p) | |
1778 | { | |
1779 | struct kuc_hdr *lh = ((struct kuc_hdr *)p) - 1; | |
1780 | LASSERT(lh->kuc_magic == KUC_MAGIC); | |
1781 | return lh; | |
1782 | } | |
1783 | EXPORT_SYMBOL(kuc_ptr); | |
1784 | ||
1785 | /* Test if payload is part of kuc message | |
1786 | * @param p Pointer to payload area | |
1787 | * @returns boolean | |
1788 | */ | |
1789 | int kuc_ispayload(void *p) | |
1790 | { | |
1791 | struct kuc_hdr *kh = ((struct kuc_hdr *)p) - 1; | |
1792 | ||
1793 | if (kh->kuc_magic == KUC_MAGIC) | |
1794 | return 1; | |
1795 | else | |
1796 | return 0; | |
1797 | } | |
1798 | EXPORT_SYMBOL(kuc_ispayload); | |
1799 | ||
1800 | /* Alloc space for a message, and fill in header | |
1801 | * @return Pointer to payload area | |
1802 | */ | |
1803 | void *kuc_alloc(int payload_len, int transport, int type) | |
1804 | { | |
1805 | struct kuc_hdr *lh; | |
1806 | int len = kuc_len(payload_len); | |
1807 | ||
1808 | OBD_ALLOC(lh, len); | |
1809 | if (lh == NULL) | |
1810 | return ERR_PTR(-ENOMEM); | |
1811 | ||
1812 | lh->kuc_magic = KUC_MAGIC; | |
1813 | lh->kuc_transport = transport; | |
1814 | lh->kuc_msgtype = type; | |
1815 | lh->kuc_msglen = len; | |
1816 | ||
1817 | return (void *)(lh + 1); | |
1818 | } | |
1819 | EXPORT_SYMBOL(kuc_alloc); | |
1820 | ||
1821 | /* Takes pointer to payload area */ | |
1822 | inline void kuc_free(void *p, int payload_len) | |
1823 | { | |
1824 | struct kuc_hdr *lh = kuc_ptr(p); | |
1825 | OBD_FREE(lh, kuc_len(payload_len)); | |
1826 | } | |
1827 | EXPORT_SYMBOL(kuc_free); |