Whamcloud - gitweb
LU-4974 lod: Change pool_desc to "[lod|lov]_pool_desc"
[fs/lustre-release.git] / lustre / lov / lov_dev.c
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.gnu.org/licenses/gpl-2.0.html
19  *
20  * GPL HEADER END
21  */
22 /*
23  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
24  * Use is subject to license terms.
25  *
26  * Copyright (c) 2012, 2017, Intel Corporation.
27  */
28 /*
29  * This file is part of Lustre, http://www.lustre.org/
30  *
31  * Implementation of cl_device and cl_device_type for LOV layer.
32  *
33  *   Author: Nikita Danilov <nikita.danilov@sun.com>
34  */
35
36 #define DEBUG_SUBSYSTEM S_LOV
37
38 /* class_name2obd() */
39 #include <obd_class.h>
40
41 #include "lov_cl_internal.h"
42
43 struct kmem_cache *lov_lock_kmem;
44 struct kmem_cache *lov_object_kmem;
45 struct kmem_cache *lov_thread_kmem;
46 struct kmem_cache *lov_session_kmem;
47
48 struct kmem_cache *lovsub_object_kmem;
49
50 struct lu_kmem_descr lov_caches[] = {
51         {
52                 .ckd_cache = &lov_lock_kmem,
53                 .ckd_name  = "lov_lock_kmem",
54                 .ckd_size  = sizeof(struct lov_lock)
55         },
56         {
57                 .ckd_cache = &lov_object_kmem,
58                 .ckd_name  = "lov_object_kmem",
59                 .ckd_size  = sizeof(struct lov_object)
60         },
61         {
62                 .ckd_cache = &lov_thread_kmem,
63                 .ckd_name  = "lov_thread_kmem",
64                 .ckd_size  = sizeof(struct lov_thread_info)
65         },
66         {
67                 .ckd_cache = &lov_session_kmem,
68                 .ckd_name  = "lov_session_kmem",
69                 .ckd_size  = sizeof(struct lov_session)
70         },
71         {
72                 .ckd_cache = &lovsub_object_kmem,
73                 .ckd_name  = "lovsub_object_kmem",
74                 .ckd_size  = sizeof(struct lovsub_object)
75         },
76         {
77                 .ckd_cache = NULL
78         }
79 };
80
81 /**
82  * Lov device and device type functions.
83  */
84
85 /**
86  * Initilize and associate key(per context data) with
87  * lu_context(execution context)
88  *
89  * \param[in] ctx       Execution context
90  * \param[in] key       Describes Data associated with this context
91  *
92  */
93 static void *lov_key_init(const struct lu_context *ctx,
94                           struct lu_context_key *key)
95 {
96         struct lov_thread_info *info;
97
98         OBD_SLAB_ALLOC_PTR_GFP(info, lov_thread_kmem, GFP_NOFS);
99         if (!info)
100                 info = ERR_PTR(-ENOMEM);
101         return info;
102 }
103
104 /**
105  * Release execution context and disassociate key
106  *
107  * \param[in] ctx       execution environment
108  * \param[in] key       Key
109  * \param[in] data      Data associated with this context
110  *
111  */
112 static void lov_key_fini(const struct lu_context *ctx,
113                          struct lu_context_key *key, void *data)
114 {
115         struct lov_thread_info *info = data;
116         OBD_SLAB_FREE_PTR(info, lov_thread_kmem);
117 }
118
119 struct lu_context_key lov_key = {
120         .lct_tags = LCT_CL_THREAD,
121         .lct_init = lov_key_init,
122         .lct_fini = lov_key_fini
123 };
124
125 /**
126  * Initilize and associate key(per context data) with
127  * lu_context(execution context) for a session
128  *
129  * \param[in] ctx       Execution context
130  * \param[in] key       Describes Data associated with this context
131  */
132 static void *lov_session_key_init(const struct lu_context *ctx,
133                                   struct lu_context_key *key)
134 {
135         struct lov_session *info;
136
137         OBD_SLAB_ALLOC_PTR_GFP(info, lov_session_kmem, GFP_NOFS);
138         if (!info)
139                 info = ERR_PTR(-ENOMEM);
140         return info;
141 }
142
143 /**
144  * Release execution context and disassociate key for a session
145  *
146  * \param[in] ctx       execution environment
147  * \param[in] key       Key
148  * \param[in] data      Data associated with this context
149  */
150 static void lov_session_key_fini(const struct lu_context *ctx,
151                                  struct lu_context_key *key, void *data)
152 {
153         struct lov_session *info = data;
154
155         OBD_SLAB_FREE_PTR(info, lov_session_kmem);
156 }
157
158 struct lu_context_key lov_session_key = {
159         .lct_tags = LCT_SESSION,
160         .lct_init = lov_session_key_init,
161         .lct_fini = lov_session_key_fini
162 };
163
164 /* type constructor/destructor: lov_type_{init,fini,start,stop}() */
165 LU_TYPE_INIT_FINI(lov, &lov_key, &lov_session_key);
166
167
168 /**
169  * Add new MDC target device in LOV.
170  *
171  * This function is part of the configuration log processing. It adds new MDC
172  * device to the MDC device array indexed by their indexes.
173  *
174  * \param[in] env       local execution environment
175  * \param[in] ld        LU device of LOV device
176  * \param[in] mdc_dev   MDC device to add
177  * \param[in] idx       MDC device index
178  * \param[in] nr        MDC device index
179  *
180  * \retval              0 if successful
181  * \retval              Non zero value on error
182  */
183 static int lov_mdc_dev_init(const struct lu_env *env, struct lov_device *ld,
184                             struct lu_device *mdc_dev, __u32 idx, __u32 nr)
185 {
186         struct cl_device *cl;
187
188         ENTRY;
189         cl = cl_type_setup(env, &ld->ld_site, &lovsub_device_type,
190                            mdc_dev);
191         if (IS_ERR(cl))
192                 RETURN(PTR_ERR(cl));
193
194         ld->ld_md_tgts[nr].ldm_mdc = cl;
195         ld->ld_md_tgts[nr].ldm_idx = idx;
196         RETURN(0);
197 }
198
199 /**
200  * Free Resource associated with lov device context
201  *
202  * \param[in] env       execution environment
203  * \param[in] d         LU device of LOV device
204  *
205  * \retval              Returns NULL
206  */
207 static struct lu_device *lov_device_fini(const struct lu_env *env,
208                                          struct lu_device *d)
209 {
210         struct lov_device *ld = lu2lov_dev(d);
211         int i;
212
213         LASSERT(ld->ld_lov != NULL);
214
215         if (ld->ld_lmv) {
216                 class_decref(ld->ld_lmv, "lov", d);
217                 ld->ld_lmv = NULL;
218         }
219
220         if (ld->ld_md_tgts) {
221                 for (i = 0; i < ld->ld_md_tgts_nr; i++) {
222                         if (!ld->ld_md_tgts[i].ldm_mdc)
223                                 continue;
224
225                         cl_stack_fini(env, ld->ld_md_tgts[i].ldm_mdc);
226                         ld->ld_md_tgts[i].ldm_mdc = NULL;
227                         ld->ld_lov->lov_mdc_tgts[i].lmtd_mdc = NULL;
228                 }
229         }
230
231         if (ld->ld_target) {
232                 lov_foreach_target(ld, i) {
233                         struct lovsub_device *lsd;
234
235                         lsd = ld->ld_target[i];
236                         if (lsd) {
237                                 cl_stack_fini(env, lovsub2cl_dev(lsd));
238                                 ld->ld_target[i] = NULL;
239                         }
240                 }
241         }
242         RETURN(NULL);
243 }
244
245 /**
246  * Initilize lov_device
247  *
248  * \param[in] env       execution environment
249  * \param[in] d         LU device of LOV device
250  * \param[in] name      Device name
251  * \param[in] next      Pointer to next lu_device
252  *
253  * \retval              0 if successful
254  * \retval              negative value on error
255  */
256 static int lov_device_init(const struct lu_env *env, struct lu_device *d,
257                            const char *name, struct lu_device *next)
258 {
259         struct lov_device *ld = lu2lov_dev(d);
260         int i;
261         int rc = 0;
262
263         /* check all added already MDC subdevices and initialize them */
264         for (i = 0; i < ld->ld_md_tgts_nr; i++) {
265                 struct obd_device *mdc;
266                 __u32 idx;
267
268                 mdc = ld->ld_lov->lov_mdc_tgts[i].lmtd_mdc;
269                 idx = ld->ld_lov->lov_mdc_tgts[i].lmtd_index;
270
271                 if (!mdc)
272                         continue;
273
274                 rc = lov_mdc_dev_init(env, ld, mdc->obd_lu_dev, idx, i);
275                 if (rc) {
276                         CERROR("%s: failed to add MDC %s as target: rc = %d\n",
277                                d->ld_obd->obd_name,
278                                obd_uuid2str(&mdc->obd_uuid), rc);
279                         GOTO(out_err, rc);
280                 }
281         }
282
283         if (!ld->ld_target)
284                 RETURN(0);
285
286         lov_foreach_target(ld, i) {
287                 struct lovsub_device *lsd;
288                 struct cl_device *cl;
289                 struct lov_tgt_desc *desc;
290
291                 desc = ld->ld_lov->lov_tgts[i];
292                 if (!desc)
293                         continue;
294
295                 cl = cl_type_setup(env, &ld->ld_site, &lovsub_device_type,
296                                    desc->ltd_obd->obd_lu_dev);
297                 if (IS_ERR(cl))
298                         GOTO(out_err, rc = PTR_ERR(cl));
299
300                 lsd = cl2lovsub_dev(cl);
301                 ld->ld_target[i] = lsd;
302         }
303         ld->ld_flags |= LOV_DEV_INITIALIZED;
304         RETURN(0);
305
306 out_err:
307         lu_device_fini(d);
308         RETURN(rc);
309 }
310
311 /**
312  * Free the lov specific data created for the back end lu_device.
313  *
314  * \param[in] env       execution environment
315  * \param[in] d         Backend lu_device
316  *
317  * \retval              Free data and return NULL
318  */
319 static struct lu_device *lov_device_free(const struct lu_env *env,
320                                          struct lu_device *d)
321 {
322         struct lov_device *ld = lu2lov_dev(d);
323         const int nr = ld->ld_target_nr;
324
325         lu_site_fini(&ld->ld_site);
326
327         cl_device_fini(lu2cl_dev(d));
328         if (ld->ld_target) {
329                 OBD_FREE_PTR_ARRAY(ld->ld_target, nr);
330                 ld->ld_target = NULL;
331         }
332         if (ld->ld_md_tgts) {
333                 OBD_FREE_PTR_ARRAY(ld->ld_md_tgts, LOV_MDC_TGT_MAX);
334                 ld->ld_md_tgts = NULL;
335         }
336         /* free array of MDCs */
337         if (ld->ld_lov->lov_mdc_tgts) {
338                 OBD_FREE_PTR_ARRAY(ld->ld_lov->lov_mdc_tgts, LOV_MDC_TGT_MAX);
339                 ld->ld_lov->lov_mdc_tgts = NULL;
340         }
341
342         OBD_FREE_PTR(ld);
343         return NULL;
344 }
345
346 /**
347  * Delete cl_object(osc) target from lov
348  *
349  * \param[in] env       execution environment
350  * \param[in] dev       LU device of LOV device
351  * \param[in] index     index of backend lu device
352  *
353  */
354 static void lov_cl_del_target(const struct lu_env *env, struct lu_device *dev,
355                               __u32 index)
356 {
357         struct lov_device *ld = lu2lov_dev(dev);
358
359         ENTRY;
360
361         if (ld->ld_target[index]) {
362                 cl_stack_fini(env, lovsub2cl_dev(ld->ld_target[index]));
363                 ld->ld_target[index] = NULL;
364         }
365         EXIT;
366 }
367
368 static int lov_expand_targets(const struct lu_env *env, struct lov_device *dev)
369 {
370         int result;
371         __u32 tgt_size;
372         __u32 sub_size;
373
374         ENTRY;
375         result = 0;
376         tgt_size = dev->ld_lov->lov_tgt_size;
377         sub_size = dev->ld_target_nr;
378         if (sub_size < tgt_size) {
379                 struct lovsub_device **newd;
380                 const size_t sz = sizeof(newd[0]);
381
382                 OBD_ALLOC_PTR_ARRAY(newd, tgt_size);
383                 if (newd) {
384                         if (sub_size > 0) {
385                                 memcpy(newd, dev->ld_target, sub_size * sz);
386                                 OBD_FREE(dev->ld_target, sub_size * sz);
387                         }
388
389                         dev->ld_target = newd;
390                         dev->ld_target_nr = tgt_size;
391                 } else {
392                         result = -ENOMEM;
393                 }
394         }
395
396         RETURN(result);
397 }
398
399 /**
400  * Add cl_object(osc) target to lov
401  *
402  * \param[in] env       execution environment
403  * \param[in] dev       LU device of LOV device
404  * \param[in] index     index of lu backend device
405  *
406  * \retval              0 if successful
407  * \retval              negative value on error
408  */
409 static int lov_cl_add_target(const struct lu_env *env, struct lu_device *dev,
410                              __u32 index)
411 {
412         struct obd_device    *obd = dev->ld_obd;
413         struct lov_device    *ld  = lu2lov_dev(dev);
414         struct lov_tgt_desc  *tgt;
415         struct lovsub_device *lsd;
416         struct cl_device     *cl;
417         int rc;
418
419         ENTRY;
420
421         lov_tgts_getref(obd);
422
423         tgt = obd->u.lov.lov_tgts[index];
424         LASSERT(tgt != NULL);
425         LASSERT(tgt->ltd_obd != NULL);
426
427         if (!tgt->ltd_obd->obd_set_up) {
428                 CERROR("Target %s not set up\n", obd_uuid2str(&tgt->ltd_uuid));
429                 RETURN(-EINVAL);
430         }
431
432         rc = lov_expand_targets(env, ld);
433         if (rc == 0 && ld->ld_flags & LOV_DEV_INITIALIZED) {
434                 cl = cl_type_setup(env, &ld->ld_site, &lovsub_device_type,
435                                    tgt->ltd_obd->obd_lu_dev);
436                 if (!IS_ERR(cl)) {
437                         lsd = cl2lovsub_dev(cl);
438                         ld->ld_target[index] = lsd;
439                 } else {
440                         CERROR("add failed (%d), deleting %s\n", rc,
441                                obd_uuid2str(&tgt->ltd_uuid));
442                         lov_cl_del_target(env, dev, index);
443                         rc = PTR_ERR(cl);
444                 }
445         }
446
447         lov_tgts_putref(obd);
448
449         RETURN(rc);
450 }
451
452 /**
453  * Add new MDC target device in LOV.
454  *
455  * This function is part of the configuration log processing. It adds new MDC
456  * device to the MDC device array indexed by their indexes.
457  *
458  * \param[in] env       execution environment
459  * \param[in] d         LU device of LOV device
460  * \param[in] mdc       MDC device to add
461  * \param[in] idx       MDC device index
462  *
463  * \retval              0 if successful
464  * \retval              negative value on error
465  */
466 static int lov_add_mdc_target(const struct lu_env *env, struct lu_device *d,
467                               struct obd_device *mdc, __u32 idx)
468 {
469         struct lov_device *ld = lu2lov_dev(d);
470         struct obd_device *lov_obd = d->ld_obd;
471         struct obd_device *lmv_obd = NULL;
472         unsigned long dev_no = 0;
473         int rc = 0;
474
475         ENTRY;
476
477         LASSERT(mdc != NULL);
478         if (ld->ld_md_tgts_nr == LOV_MDC_TGT_MAX) {
479                 /*
480                  * If the maximum value of LOV_MDC_TGT_MAX will become too
481                  * small then all MD target handling must be rewritten in LOD
482                  * manner, check lod_add_device() and related functionality.
483                  */
484                 CERROR("%s: cannot serve more than %d MDC devices\n",
485                        lov_obd->obd_name, LOV_MDC_TGT_MAX);
486                 RETURN(-ERANGE);
487         }
488
489         /*
490          * grab FLD from lmv, do that here, when first MDC is added
491          * to be sure LMV is set up and can be found
492          */
493         if (!ld->ld_lmv) {
494                 obd_device_lock();
495                 obd_device_for_each_uuid(dev_no, lmv_obd,
496                                          &lov_obd->obd_uuid) {
497                         if ((strncmp(lmv_obd->obd_type->typ_name,
498                                      LUSTRE_LMV_NAME,
499                                      strlen(LUSTRE_LMV_NAME)) == 0)) {
500                                 spin_lock(&lmv_obd->obd_dev_lock);
501                                 class_incref(lmv_obd, "lov", ld);
502                                 spin_unlock(&lmv_obd->obd_dev_lock);
503                                 break;
504                         }
505                 }
506                 obd_device_unlock();
507
508                 if (!lmv_obd) {
509                         CERROR("%s: cannot find LMV OBD by UUID (%s)\n",
510                                lov_obd->obd_name,
511                                obd_uuid2str(&lmv_obd->obd_uuid));
512                         RETURN(-ENODEV);
513                 }
514                 ld->ld_lmv = lmv_obd;
515         }
516
517         LASSERT(lov_obd->u.lov.lov_mdc_tgts[ld->ld_md_tgts_nr].lmtd_mdc ==
518                 NULL);
519
520         if (ld->ld_flags & LOV_DEV_INITIALIZED) {
521                 rc = lov_mdc_dev_init(env, ld, mdc->obd_lu_dev, idx,
522                                       ld->ld_md_tgts_nr);
523                 if (rc) {
524                         CERROR("%s: failed to add MDC %s as target: rc = %d\n",
525                                lov_obd->obd_name, obd_uuid2str(&mdc->obd_uuid),
526                                rc);
527                         RETURN(rc);
528                 }
529         }
530
531         lov_obd->u.lov.lov_mdc_tgts[ld->ld_md_tgts_nr].lmtd_mdc = mdc;
532         lov_obd->u.lov.lov_mdc_tgts[ld->ld_md_tgts_nr].lmtd_index = idx;
533         ld->ld_md_tgts_nr++;
534
535         RETURN(rc);
536 }
537
538 /**
539  * Called when lov configuration changes are needed
540  *
541  * \param[in] env       execution environment
542  * \param[in] d         LU device of LOV device
543  * \param[in] cfg       setup configuration commands and arguments
544  *
545  * \retval              Return a new lu_device on success
546  * \retval              Error pointer on failure
547  */
548 static int lov_process_config(const struct lu_env *env,
549                               struct lu_device *d, struct lustre_cfg *cfg)
550 {
551         struct obd_device *obd = d->ld_obd;
552         int cmd;
553         int rc;
554         int gen;
555         u32 index;
556
557         lov_tgts_getref(obd);
558
559         cmd = cfg->lcfg_command;
560
561         rc = lov_process_config_base(d->ld_obd, cfg, &index, &gen);
562         if (rc < 0)
563                 GOTO(out, rc);
564
565         switch (cmd) {
566         case LCFG_LOV_ADD_OBD:
567         case LCFG_LOV_ADD_INA:
568                 rc = lov_cl_add_target(env, d, index);
569                 if (rc != 0)
570                         lov_del_target(d->ld_obd, index, NULL, 0);
571                 break;
572         case LCFG_LOV_DEL_OBD:
573                 lov_cl_del_target(env, d, index);
574                 break;
575         case LCFG_ADD_MDC:
576         {
577                 struct obd_device *mdc;
578                 struct obd_uuid tgt_uuid;
579
580                 /*
581                  * modify_mdc_tgts add 0:lustre-clilmv  1:lustre-MDT0000_UUID
582                  * 2:0  3:1  4:lustre-MDT0000-mdc_UUID
583                  */
584                 if (LUSTRE_CFG_BUFLEN(cfg, 1) > sizeof(tgt_uuid.uuid))
585                         GOTO(out, rc = -EINVAL);
586
587                 obd_str2uuid(&tgt_uuid, lustre_cfg_buf(cfg, 1));
588
589                 rc = kstrtou32(lustre_cfg_buf(cfg, 2), 10, &index);
590                 if (rc)
591                         GOTO(out, rc);
592
593                 mdc = class_find_client_obd(&tgt_uuid, LUSTRE_MDC_NAME,
594                                             &obd->obd_uuid);
595                 if (!mdc)
596                         GOTO(out, rc = -ENODEV);
597                 rc = lov_add_mdc_target(env, d, mdc, index);
598                 break;
599         }
600         }
601 out:
602         lov_tgts_putref(obd);
603         RETURN(rc);
604 }
605
606 static const struct lu_device_operations lov_lu_ops = {
607         .ldo_object_alloc      = lov_object_alloc,
608         .ldo_process_config    = lov_process_config,
609 };
610
611 /**
612  * Allocate a new lov lu_device
613  *
614  * \param[in] env       execution environment
615  * \param[in] t         Backend OSD device type (ldiskfs,zfs)
616  * \param[in] cfg       setup configuration commands and arguments
617  *
618  * \retval              Return a new lu_device on success
619  * \retval              Error pointer on failure
620  */
621 static struct lu_device *lov_device_alloc(const struct lu_env *env,
622                                           struct lu_device_type *t,
623                                           struct lustre_cfg *cfg)
624 {
625         struct lu_device *d;
626         struct lov_device *ld;
627         struct obd_device *obd;
628         int rc;
629
630         OBD_ALLOC_PTR(ld);
631         if (!ld)
632                 RETURN(ERR_PTR(-ENOMEM));
633
634         cl_device_init(&ld->ld_cl, t);
635         d = lov2lu_dev(ld);
636         d->ld_ops = &lov_lu_ops;
637
638         /* setup the LOV OBD */
639         obd = class_name2obd(lustre_cfg_string(cfg, 0));
640         LASSERT(obd != NULL);
641         rc = lov_setup(obd, cfg);
642         if (rc)
643                 GOTO(out, rc);
644
645         /* Alloc MDC devices array */
646         /* XXX: need dynamic allocation at some moment */
647         OBD_ALLOC_PTR_ARRAY(ld->ld_md_tgts, LOV_MDC_TGT_MAX);
648         if (!ld->ld_md_tgts)
649                 GOTO(out, rc = -ENOMEM);
650
651         ld->ld_md_tgts_nr = 0;
652
653         ld->ld_lov = &obd->u.lov;
654         OBD_ALLOC_PTR_ARRAY(ld->ld_lov->lov_mdc_tgts, LOV_MDC_TGT_MAX);
655         if (!ld->ld_lov->lov_mdc_tgts)
656                 GOTO(out_md_tgts, rc = -ENOMEM);
657
658         rc = lu_site_init(&ld->ld_site, d);
659         if (rc != 0)
660                 GOTO(out_mdc_tgts, rc);
661
662         rc = lu_site_init_finish(&ld->ld_site);
663         if (rc != 0)
664                 GOTO(out_site, rc);
665
666         RETURN(d);
667 out_site:
668         lu_site_fini(&ld->ld_site);
669 out_mdc_tgts:
670         OBD_FREE_PTR_ARRAY(ld->ld_lov->lov_mdc_tgts, LOV_MDC_TGT_MAX);
671         ld->ld_lov->lov_mdc_tgts = NULL;
672 out_md_tgts:
673         OBD_FREE_PTR_ARRAY(ld->ld_md_tgts, LOV_MDC_TGT_MAX);
674         ld->ld_md_tgts = NULL;
675 out:
676         OBD_FREE_PTR(ld);
677
678         return ERR_PTR(rc);
679 }
680
681 static const struct lu_device_type_operations lov_device_type_ops = {
682         .ldto_init = lov_type_init,
683         .ldto_fini = lov_type_fini,
684
685         .ldto_start = lov_type_start,
686         .ldto_stop  = lov_type_stop,
687
688         .ldto_device_alloc = lov_device_alloc,
689         .ldto_device_free = lov_device_free,
690
691         .ldto_device_init = lov_device_init,
692         .ldto_device_fini = lov_device_fini
693 };
694
695 struct lu_device_type lov_device_type = {
696         .ldt_tags     = LU_DEVICE_CL,
697         .ldt_name     = LUSTRE_LOV_NAME,
698         .ldt_ops      = &lov_device_type_ops,
699         .ldt_ctx_tags = LCT_CL_THREAD
700 };
701
702 /** @} lov */