| /* |
| * GPL HEADER START |
| * |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 only, |
| * as published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, but |
| * WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * General Public License version 2 for more details (a copy is included |
| * in the LICENSE file that accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License |
| * version 2 along with this program; If not, see |
| * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf |
| * |
| * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
| * CA 95054 USA or visit www.sun.com if you need additional information or |
| * have any questions. |
| * |
| * GPL HEADER END |
| */ |
| /* |
| * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. |
| * Use is subject to license terms. |
| * |
| * Copyright (c) 2012, Intel Corporation. |
| */ |
| /* |
| * This file is part of Lustre, http://www.lustre.org/ |
| * Lustre is a trademark of Sun Microsystems, Inc. |
| * |
| * Implementation of cl_device and cl_device_type for LOV layer. |
| * |
| * Author: Nikita Danilov <nikita.danilov@sun.com> |
| */ |
| |
| #define DEBUG_SUBSYSTEM S_LOV |
| |
| /* class_name2obd() */ |
| #include "../include/obd_class.h" |
| |
| #include "lov_cl_internal.h" |
| #include "lov_internal.h" |
| |
| |
| struct kmem_cache *lov_lock_kmem; |
| struct kmem_cache *lov_object_kmem; |
| struct kmem_cache *lov_thread_kmem; |
| struct kmem_cache *lov_session_kmem; |
| struct kmem_cache *lov_req_kmem; |
| |
| struct kmem_cache *lovsub_lock_kmem; |
| struct kmem_cache *lovsub_object_kmem; |
| struct kmem_cache *lovsub_req_kmem; |
| |
| struct kmem_cache *lov_lock_link_kmem; |
| |
| /** Lock class of lov_device::ld_mutex. */ |
| static struct lock_class_key cl_lov_device_mutex_class; |
| |
| struct lu_kmem_descr lov_caches[] = { |
| { |
| .ckd_cache = &lov_lock_kmem, |
| .ckd_name = "lov_lock_kmem", |
| .ckd_size = sizeof(struct lov_lock) |
| }, |
| { |
| .ckd_cache = &lov_object_kmem, |
| .ckd_name = "lov_object_kmem", |
| .ckd_size = sizeof(struct lov_object) |
| }, |
| { |
| .ckd_cache = &lov_thread_kmem, |
| .ckd_name = "lov_thread_kmem", |
| .ckd_size = sizeof(struct lov_thread_info) |
| }, |
| { |
| .ckd_cache = &lov_session_kmem, |
| .ckd_name = "lov_session_kmem", |
| .ckd_size = sizeof(struct lov_session) |
| }, |
| { |
| .ckd_cache = &lov_req_kmem, |
| .ckd_name = "lov_req_kmem", |
| .ckd_size = sizeof(struct lov_req) |
| }, |
| { |
| .ckd_cache = &lovsub_lock_kmem, |
| .ckd_name = "lovsub_lock_kmem", |
| .ckd_size = sizeof(struct lovsub_lock) |
| }, |
| { |
| .ckd_cache = &lovsub_object_kmem, |
| .ckd_name = "lovsub_object_kmem", |
| .ckd_size = sizeof(struct lovsub_object) |
| }, |
| { |
| .ckd_cache = &lovsub_req_kmem, |
| .ckd_name = "lovsub_req_kmem", |
| .ckd_size = sizeof(struct lovsub_req) |
| }, |
| { |
| .ckd_cache = &lov_lock_link_kmem, |
| .ckd_name = "lov_lock_link_kmem", |
| .ckd_size = sizeof(struct lov_lock_link) |
| }, |
| { |
| .ckd_cache = NULL |
| } |
| }; |
| |
| /***************************************************************************** |
| * |
| * Lov transfer operations. |
| * |
| */ |
| |
| static void lov_req_completion(const struct lu_env *env, |
| const struct cl_req_slice *slice, int ioret) |
| { |
| struct lov_req *lr; |
| |
| lr = cl2lov_req(slice); |
| OBD_SLAB_FREE_PTR(lr, lov_req_kmem); |
| } |
| |
| static const struct cl_req_operations lov_req_ops = { |
| .cro_completion = lov_req_completion |
| }; |
| |
| /***************************************************************************** |
| * |
| * Lov device and device type functions. |
| * |
| */ |
| |
| static void *lov_key_init(const struct lu_context *ctx, |
| struct lu_context_key *key) |
| { |
| struct lov_thread_info *info; |
| |
| OBD_SLAB_ALLOC_PTR_GFP(info, lov_thread_kmem, GFP_NOFS); |
| if (info != NULL) |
| INIT_LIST_HEAD(&info->lti_closure.clc_list); |
| else |
| info = ERR_PTR(-ENOMEM); |
| return info; |
| } |
| |
| static void lov_key_fini(const struct lu_context *ctx, |
| struct lu_context_key *key, void *data) |
| { |
| struct lov_thread_info *info = data; |
| LINVRNT(list_empty(&info->lti_closure.clc_list)); |
| OBD_SLAB_FREE_PTR(info, lov_thread_kmem); |
| } |
| |
| struct lu_context_key lov_key = { |
| .lct_tags = LCT_CL_THREAD, |
| .lct_init = lov_key_init, |
| .lct_fini = lov_key_fini |
| }; |
| |
| static void *lov_session_key_init(const struct lu_context *ctx, |
| struct lu_context_key *key) |
| { |
| struct lov_session *info; |
| |
| OBD_SLAB_ALLOC_PTR_GFP(info, lov_session_kmem, GFP_NOFS); |
| if (info == NULL) |
| info = ERR_PTR(-ENOMEM); |
| return info; |
| } |
| |
| static void lov_session_key_fini(const struct lu_context *ctx, |
| struct lu_context_key *key, void *data) |
| { |
| struct lov_session *info = data; |
| OBD_SLAB_FREE_PTR(info, lov_session_kmem); |
| } |
| |
| struct lu_context_key lov_session_key = { |
| .lct_tags = LCT_SESSION, |
| .lct_init = lov_session_key_init, |
| .lct_fini = lov_session_key_fini |
| }; |
| |
| /* type constructor/destructor: lov_type_{init,fini,start,stop}() */ |
| LU_TYPE_INIT_FINI(lov, &lov_key, &lov_session_key); |
| |
| static struct lu_device *lov_device_fini(const struct lu_env *env, |
| struct lu_device *d) |
| { |
| int i; |
| struct lov_device *ld = lu2lov_dev(d); |
| |
| LASSERT(ld->ld_lov != NULL); |
| if (ld->ld_target == NULL) |
| return NULL; |
| |
| lov_foreach_target(ld, i) { |
| struct lovsub_device *lsd; |
| |
| lsd = ld->ld_target[i]; |
| if (lsd != NULL) { |
| cl_stack_fini(env, lovsub2cl_dev(lsd)); |
| ld->ld_target[i] = NULL; |
| } |
| } |
| return NULL; |
| } |
| |
| static int lov_device_init(const struct lu_env *env, struct lu_device *d, |
| const char *name, struct lu_device *next) |
| { |
| struct lov_device *ld = lu2lov_dev(d); |
| int i; |
| int rc = 0; |
| |
| LASSERT(d->ld_site != NULL); |
| if (ld->ld_target == NULL) |
| return rc; |
| |
| lov_foreach_target(ld, i) { |
| struct lovsub_device *lsd; |
| struct cl_device *cl; |
| struct lov_tgt_desc *desc; |
| |
| desc = ld->ld_lov->lov_tgts[i]; |
| if (desc == NULL) |
| continue; |
| |
| cl = cl_type_setup(env, d->ld_site, &lovsub_device_type, |
| desc->ltd_obd->obd_lu_dev); |
| if (IS_ERR(cl)) { |
| rc = PTR_ERR(cl); |
| break; |
| } |
| lsd = cl2lovsub_dev(cl); |
| lsd->acid_idx = i; |
| lsd->acid_super = ld; |
| ld->ld_target[i] = lsd; |
| } |
| |
| if (rc) |
| lov_device_fini(env, d); |
| else |
| ld->ld_flags |= LOV_DEV_INITIALIZED; |
| |
| return rc; |
| } |
| |
| static int lov_req_init(const struct lu_env *env, struct cl_device *dev, |
| struct cl_req *req) |
| { |
| struct lov_req *lr; |
| int result; |
| |
| OBD_SLAB_ALLOC_PTR_GFP(lr, lov_req_kmem, GFP_NOFS); |
| if (lr != NULL) { |
| cl_req_slice_add(req, &lr->lr_cl, dev, &lov_req_ops); |
| result = 0; |
| } else |
| result = -ENOMEM; |
| return result; |
| } |
| |
| static const struct cl_device_operations lov_cl_ops = { |
| .cdo_req_init = lov_req_init |
| }; |
| |
| static void lov_emerg_free(struct lov_device_emerg **emrg, int nr) |
| { |
| int i; |
| |
| for (i = 0; i < nr; ++i) { |
| struct lov_device_emerg *em; |
| |
| em = emrg[i]; |
| if (em != NULL) { |
| LASSERT(em->emrg_page_list.pl_nr == 0); |
| if (em->emrg_env != NULL) |
| cl_env_put(em->emrg_env, &em->emrg_refcheck); |
| kfree(em); |
| } |
| } |
| kfree(emrg); |
| } |
| |
| static struct lu_device *lov_device_free(const struct lu_env *env, |
| struct lu_device *d) |
| { |
| struct lov_device *ld = lu2lov_dev(d); |
| const int nr = ld->ld_target_nr; |
| |
| cl_device_fini(lu2cl_dev(d)); |
| kfree(ld->ld_target); |
| if (ld->ld_emrg != NULL) |
| lov_emerg_free(ld->ld_emrg, nr); |
| kfree(ld); |
| return NULL; |
| } |
| |
| static void lov_cl_del_target(const struct lu_env *env, struct lu_device *dev, |
| __u32 index) |
| { |
| struct lov_device *ld = lu2lov_dev(dev); |
| |
| if (ld->ld_target[index] != NULL) { |
| cl_stack_fini(env, lovsub2cl_dev(ld->ld_target[index])); |
| ld->ld_target[index] = NULL; |
| } |
| } |
| |
| static struct lov_device_emerg **lov_emerg_alloc(int nr) |
| { |
| struct lov_device_emerg **emerg; |
| int i; |
| int result; |
| |
| emerg = kcalloc(nr, sizeof(emerg[0]), GFP_NOFS); |
| if (emerg == NULL) |
| return ERR_PTR(-ENOMEM); |
| for (result = i = 0; i < nr && result == 0; i++) { |
| struct lov_device_emerg *em; |
| |
| em = kzalloc(sizeof(*em), GFP_NOFS); |
| if (em != NULL) { |
| emerg[i] = em; |
| cl_page_list_init(&em->emrg_page_list); |
| em->emrg_env = cl_env_alloc(&em->emrg_refcheck, |
| LCT_REMEMBER|LCT_NOREF); |
| if (!IS_ERR(em->emrg_env)) |
| em->emrg_env->le_ctx.lc_cookie = 0x2; |
| else { |
| result = PTR_ERR(em->emrg_env); |
| em->emrg_env = NULL; |
| } |
| } else |
| result = -ENOMEM; |
| } |
| if (result != 0) { |
| lov_emerg_free(emerg, nr); |
| emerg = ERR_PTR(result); |
| } |
| return emerg; |
| } |
| |
| static int lov_expand_targets(const struct lu_env *env, struct lov_device *dev) |
| { |
| int result; |
| __u32 tgt_size; |
| __u32 sub_size; |
| |
| result = 0; |
| tgt_size = dev->ld_lov->lov_tgt_size; |
| sub_size = dev->ld_target_nr; |
| if (sub_size < tgt_size) { |
| struct lovsub_device **newd; |
| struct lov_device_emerg **emerg; |
| const size_t sz = sizeof(newd[0]); |
| |
| emerg = lov_emerg_alloc(tgt_size); |
| if (IS_ERR(emerg)) |
| return PTR_ERR(emerg); |
| |
| newd = kcalloc(tgt_size, sz, GFP_NOFS); |
| if (newd != NULL) { |
| mutex_lock(&dev->ld_mutex); |
| if (sub_size > 0) { |
| memcpy(newd, dev->ld_target, sub_size * sz); |
| kfree(dev->ld_target); |
| } |
| dev->ld_target = newd; |
| dev->ld_target_nr = tgt_size; |
| |
| if (dev->ld_emrg != NULL) |
| lov_emerg_free(dev->ld_emrg, sub_size); |
| dev->ld_emrg = emerg; |
| mutex_unlock(&dev->ld_mutex); |
| } else { |
| lov_emerg_free(emerg, tgt_size); |
| result = -ENOMEM; |
| } |
| } |
| return result; |
| } |
| |
| static int lov_cl_add_target(const struct lu_env *env, struct lu_device *dev, |
| __u32 index) |
| { |
| struct obd_device *obd = dev->ld_obd; |
| struct lov_device *ld = lu2lov_dev(dev); |
| struct lov_tgt_desc *tgt; |
| struct lovsub_device *lsd; |
| struct cl_device *cl; |
| int rc; |
| |
| obd_getref(obd); |
| |
| tgt = obd->u.lov.lov_tgts[index]; |
| LASSERT(tgt != NULL); |
| LASSERT(tgt->ltd_obd != NULL); |
| |
| if (!tgt->ltd_obd->obd_set_up) { |
| CERROR("Target %s not set up\n", obd_uuid2str(&tgt->ltd_uuid)); |
| return -EINVAL; |
| } |
| |
| rc = lov_expand_targets(env, ld); |
| if (rc == 0 && ld->ld_flags & LOV_DEV_INITIALIZED) { |
| LASSERT(dev->ld_site != NULL); |
| |
| cl = cl_type_setup(env, dev->ld_site, &lovsub_device_type, |
| tgt->ltd_obd->obd_lu_dev); |
| if (!IS_ERR(cl)) { |
| lsd = cl2lovsub_dev(cl); |
| lsd->acid_idx = index; |
| lsd->acid_super = ld; |
| ld->ld_target[index] = lsd; |
| } else { |
| CERROR("add failed (%d), deleting %s\n", rc, |
| obd_uuid2str(&tgt->ltd_uuid)); |
| lov_cl_del_target(env, dev, index); |
| rc = PTR_ERR(cl); |
| } |
| } |
| obd_putref(obd); |
| return rc; |
| } |
| |
| static int lov_process_config(const struct lu_env *env, |
| struct lu_device *d, struct lustre_cfg *cfg) |
| { |
| struct obd_device *obd = d->ld_obd; |
| int cmd; |
| int rc; |
| int gen; |
| __u32 index; |
| |
| obd_getref(obd); |
| |
| cmd = cfg->lcfg_command; |
| rc = lov_process_config_base(d->ld_obd, cfg, &index, &gen); |
| if (rc == 0) { |
| switch (cmd) { |
| case LCFG_LOV_ADD_OBD: |
| case LCFG_LOV_ADD_INA: |
| rc = lov_cl_add_target(env, d, index); |
| if (rc != 0) |
| lov_del_target(d->ld_obd, index, NULL, 0); |
| break; |
| case LCFG_LOV_DEL_OBD: |
| lov_cl_del_target(env, d, index); |
| break; |
| } |
| } |
| obd_putref(obd); |
| return rc; |
| } |
| |
| static const struct lu_device_operations lov_lu_ops = { |
| .ldo_object_alloc = lov_object_alloc, |
| .ldo_process_config = lov_process_config, |
| }; |
| |
| static struct lu_device *lov_device_alloc(const struct lu_env *env, |
| struct lu_device_type *t, |
| struct lustre_cfg *cfg) |
| { |
| struct lu_device *d; |
| struct lov_device *ld; |
| struct obd_device *obd; |
| int rc; |
| |
| ld = kzalloc(sizeof(*ld), GFP_NOFS); |
| if (!ld) |
| return ERR_PTR(-ENOMEM); |
| |
| cl_device_init(&ld->ld_cl, t); |
| d = lov2lu_dev(ld); |
| d->ld_ops = &lov_lu_ops; |
| ld->ld_cl.cd_ops = &lov_cl_ops; |
| |
| mutex_init(&ld->ld_mutex); |
| lockdep_set_class(&ld->ld_mutex, &cl_lov_device_mutex_class); |
| |
| /* setup the LOV OBD */ |
| obd = class_name2obd(lustre_cfg_string(cfg, 0)); |
| LASSERT(obd != NULL); |
| rc = lov_setup(obd, cfg); |
| if (rc) { |
| lov_device_free(env, d); |
| return ERR_PTR(rc); |
| } |
| |
| ld->ld_lov = &obd->u.lov; |
| return d; |
| } |
| |
| static const struct lu_device_type_operations lov_device_type_ops = { |
| .ldto_init = lov_type_init, |
| .ldto_fini = lov_type_fini, |
| |
| .ldto_start = lov_type_start, |
| .ldto_stop = lov_type_stop, |
| |
| .ldto_device_alloc = lov_device_alloc, |
| .ldto_device_free = lov_device_free, |
| |
| .ldto_device_init = lov_device_init, |
| .ldto_device_fini = lov_device_fini |
| }; |
| |
| struct lu_device_type lov_device_type = { |
| .ldt_tags = LU_DEVICE_CL, |
| .ldt_name = LUSTRE_LOV_NAME, |
| .ldt_ops = &lov_device_type_ops, |
| .ldt_ctx_tags = LCT_CL_THREAD |
| }; |
| EXPORT_SYMBOL(lov_device_type); |
| |
| /** @} lov */ |