Viewing: lod_dev.c
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved
* Use is subject to license terms.
*
* Copyright (c) 2012, 2017, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
*
* Lustre Logical Object Device
*
* Author: Alex Zhuravlev <alexey.zhuravlev@intel.com>
* Author: Mikhail Pershin <mike.pershin@intel.com>
*/
/*
* The Logical Object Device (LOD) layer manages access to striped
* objects (both regular files and directories). It implements the DT
* device and object APIs and is responsible for creating, storing,
* and loading striping information as an extended attribute of the
* underlying OSD object. LOD is the server side analog of the LOV and
* LMV layers on the client side.
*
* Metadata LU object stack (layers of the same compound LU object,
* all have the same FID):
*
* MDT
* | MD API
* MDD
* | DT API
* LOD
* / \ DT API
* OSD OSP
*
* During LOD object initialization the localness or remoteness of the
* object FID dictates the choice between OSD and OSP.
*
* An LOD object (file or directory) with N stripes (each has a
* different FID):
*
* LOD
* |
* +---+---+---+...+
* | | | | |
* S0 S1 S2 S3 S(N-1) OS[DP] objects, seen as DT objects by LOD
*
* When upper layers must access an object's stripes (which are
* themselves OST or MDT LU objects) LOD finds these objects by their
* FIDs and stores them as an array of DT object pointers on the
* object. Declarations and operations on LOD objects are received by
* LOD (as DT object operations) and performed on the underlying
* OS[DP] object and (as needed) on the stripes. From the perspective
* of LOD, a stripe-less file (created by mknod() or open with
* O_LOV_DELAY_CREATE) is an object which does not yet have stripes,
* while a non-striped directory (created by mkdir()) is an object
* which will never have stripes.
*
* The LOD layer also implements a small subset of the OBD device API
* to support MDT stack initialization and finalization (an MDD device
* connects and disconnects itself to and from the underlying LOD
* device), and pool management. In turn LOD uses the OBD device API
* to connect it self to the underlying OSD, and to connect itself to
* OSP devices representing the MDTs and OSTs that bear the stripes of
* its objects.
*/
#define DEBUG_SUBSYSTEM S_MDS
#include <linux/kthread.h>
#include <obd_class.h>
#include <md_object.h>
#include <lustre_fid.h>
#include <uapi/linux/lustre/lustre_param.h>
#include <uapi/linux/lustre/lustre_ioctl.h>
#include <lustre_update.h>
#include <lustre_log.h>
#include <lustre_lmv.h>
#include <llog_swab.h>
#include "lod_internal.h"
static const char lod_update_log_name[] = "update_log";
static const char lod_update_log_dir_name[] = "update_log_dir";
/**
* lod_fld_lookup() - Lookup target by FID within LOD
* @env: LU environment provided by the caller
* @lod: lod device
* @fid: FID
* @tgt: result target index
* @type: expected type of the target (LU_SEQ_RANGE_{MDT,OST,ANY})
*
* Lookup MDT/OST target index by FID. Type of the target can be
* specific or any.
*
* Return:
* * %0 on success
* * %Negative negated errno on error
*/
int lod_fld_lookup(const struct lu_env *env, struct lod_device *lod,
const struct lu_fid *fid, u32 *tgt, int *type)
{
struct lu_seq_range range = { 0 };
struct lu_server_fld *server_fld;
int rc;
ENTRY;
if (!fid_is_sane(fid)) {
CERROR("%s: invalid FID "DFID"\n", lod2obd(lod)->obd_name,
PFID(fid));
RETURN(-EIO);
}
if (fid_is_idif(fid)) {
*tgt = fid_idif_ost_idx(fid);
*type = LU_SEQ_RANGE_OST;
RETURN(0);
}
if (fid_is_update_log(fid) || fid_is_update_log_dir(fid)) {
*tgt = fid_oid(fid);
*type = LU_SEQ_RANGE_MDT;
RETURN(0);
}
if (!lod->lod_initialized || (!fid_seq_in_fldb(fid_seq(fid)))) {
LASSERT(lu_site2seq(lod2lu_dev(lod)->ld_site) != NULL);
*tgt = lu_site2seq(lod2lu_dev(lod)->ld_site)->ss_node_id;
*type = LU_SEQ_RANGE_MDT;
RETURN(0);
}
server_fld = lu_site2seq(lod2lu_dev(lod)->ld_site)->ss_server_fld;
if (!server_fld)
RETURN(-EIO);
fld_range_set_type(&range, *type);
rc = fld_server_lookup(env, server_fld, fid_seq(fid), &range);
if (rc != 0)
RETURN(rc);
*tgt = range.lsr_index;
*type = range.lsr_flags;
CDEBUG(D_INFO, "%s: got tgt %x for sequence: %#llx\n",
lod2obd(lod)->obd_name, *tgt, fid_seq(fid));
RETURN(0);
}
/* Slab for OSD object allocation */
struct kmem_cache *lod_object_kmem;
/* Slab for dt_txn_callback */
static struct kmem_cache *lod_txn_callback_kmem;
static struct lu_kmem_descr lod_caches[] = {
{
.ckd_cache = &lod_object_kmem,
.ckd_name = "lod_obj",
.ckd_size = sizeof(struct lod_object)
},
{
.ckd_cache = &lod_txn_callback_kmem,
.ckd_name = "lod_txn_callback",
.ckd_size = sizeof(struct dt_txn_callback)
},
{
.ckd_cache = NULL
}
};
static struct lu_device *lod_device_fini(const struct lu_env *env,
struct lu_device *d);
/**
* lod_object_alloc() - Implementation of
* lu_device_operations::ldo_object_alloc() for LOD
* @env: Execution environment
* @hdr: metadata about (LOD)object being allocated
* @dev: LOD device for which the object is being allocated
*
* Allocates and initializes LOD's slice in the given object.
* see include/lu_object.h for the details.
*
* Return pointer to a lu_object structure on success else error pointer
*/
static struct lu_object *lod_object_alloc(const struct lu_env *env,
const struct lu_object_header *hdr,
struct lu_device *dev)
{
struct lod_object *lod_obj;
struct lu_object *lu_obj;
ENTRY;
OBD_SLAB_ALLOC_PTR_GFP(lod_obj, lod_object_kmem, GFP_NOFS);
if (!lod_obj)
RETURN(ERR_PTR(-ENOMEM));
mutex_init(&lod_obj->ldo_layout_mutex);
lu_obj = lod2lu_obj(lod_obj);
dt_object_init(&lod_obj->ldo_obj, NULL, dev);
lod_obj->ldo_obj.do_ops = &lod_obj_ops;
lu_obj->lo_ops = &lod_lu_obj_ops;
lod_obj->ldo_dir_layout_version = 1;
RETURN(lu_obj);
}
/**
* lod_sub_process_config() - Process the config log for all sub device.
* @env: LU environment provided by the caller
* @lod: lod device
* @ltd: target's table to go through
* @lcfg: configuration command to apply
*
* The function goes through all the targets in the given table
* and apply given configuration command on to the targets.
* Used to cleanup the targets at unmount.
*
* Return:
* * %0 on success
* * %Negative negated errno on error
*/
static int lod_sub_process_config(const struct lu_env *env,
struct lod_device *lod,
struct lod_tgt_descs *ltd,
struct lustre_cfg *lcfg)
{
struct lu_device *next;
struct lu_tgt_desc *tgt;
int rc = 0;
lod_getref(ltd);
ltd_foreach_tgt(ltd, tgt) {
int rc1;
LASSERT(tgt && tgt->ltd_tgt);
next = &tgt->ltd_tgt->dd_lu_dev;
rc1 = next->ld_ops->ldo_process_config(env, next, lcfg);
if (rc1) {
CERROR("%s: error cleaning up LOD index %u: cmd %#x : rc = %d\n",
lod2obd(lod)->obd_name, tgt->ltd_index,
lcfg->lcfg_command, rc1);
rc = rc1;
}
}
lod_putref(lod, ltd);
return rc;
}
struct lod_recovery_data {
struct lod_device *lrd_lod;
struct lod_tgt_desc *lrd_ltd;
struct task_struct **lrd_task;
u32 lrd_idx;
struct lu_env lrd_env;
struct completion *lrd_started;
};
/**
* lod_process_recovery_updates() - process update recovery record
* @env: execution environment
* @llh: log handle of update record
* @rec: update record to be replayed
* @data: update recovery data which holds the necessary arguments for recovery
* (see struct lod_recovery_data)
*
* Add the update recovery recode to the update recovery list in
* lod_recovery_data. Then the recovery thread (target_recovery_thread)
* will redo these updates.
*
* Return:
* * %0 on success
* * %Negative negated errno on error
*/
static int lod_process_recovery_updates(const struct lu_env *env,
struct llog_handle *llh,
struct llog_rec_hdr *rec,
void *data)
{
struct lod_recovery_data *lrd = data;
struct llog_cookie *cookie = &lod_env_info(env)->lti_cookie;
struct lu_target *lut;
u32 index = 0;
ENTRY;
if (!lrd->lrd_ltd) {
int rc;
rc = lodname2mdt_index(lod2obd(lrd->lrd_lod)->obd_name, &index);
if (rc != 0)
return rc;
} else {
index = lrd->lrd_ltd->ltd_index;
}
if (rec->lrh_len !=
llog_update_record_size((struct llog_update_record *)rec)) {
CERROR("%s: broken update record! index %u "DFID".%u: rc = %d\n",
lod2obd(lrd->lrd_lod)->obd_name, index,
PLOGID(&llh->lgh_id), rec->lrh_index, -EIO);
return -EINVAL;
}
cookie->lgc_lgl = llh->lgh_id;
cookie->lgc_index = rec->lrh_index;
cookie->lgc_subsys = LLOG_UPDATELOG_ORIG_CTXT;
CDEBUG(D_HA, "%s: process recovery updates "DFID".%u\n",
lod2obd(lrd->lrd_lod)->obd_name,
PLOGID(&llh->lgh_id), rec->lrh_index);
lut = lod2lu_dev(lrd->lrd_lod)->ld_site->ls_tgt;
if (obd_mdt_recovery_abort(lut->lut_obd))
return -ESHUTDOWN;
return insert_update_records_to_replay_list(lut->lut_tdtd,
(struct llog_update_record *)rec,
cookie, index);
}
/* retain old catalog, create new catalog and update catlist */
static int lod_sub_recreate_llog(const struct lu_env *env,
struct lod_device *lod, struct dt_device *dt,
int index)
{
struct lod_thread_info *lti = lod_env_info(env);
struct llog_ctxt *ctxt;
struct llog_handle *lgh;
struct llog_catid *cid = <i->lti_cid;
struct lu_fid *fid = <i->lti_fid;
struct obd_device *obd;
int rc;
ENTRY;
lu_update_log_fid(fid, index);
rc = lodname2mdt_index(lod2obd(lod)->obd_name, (__u32 *)&index);
if (rc < 0)
RETURN(rc);
rc = llog_osd_get_cat_list(env, dt, index, 1, NULL, fid);
if (rc < 0) {
CERROR("%s: can't access update_log: rc = %d\n",
lod2obd(lod)->obd_name, rc);
RETURN(rc);
}
obd = dt->dd_lu_dev.ld_obd;
ctxt = llog_get_context(obd, LLOG_UPDATELOG_ORIG_CTXT);
LASSERT(ctxt != NULL);
if (ctxt->loc_handle) {
/* retain old catalog */
llog_retain(env, ctxt->loc_handle);
llog_cat_close(env, ctxt->loc_handle);
LASSERT(!ctxt->loc_handle);
}
ctxt->loc_flags |= LLOG_CTXT_FLAG_NORMAL_FID;
ctxt->loc_chunk_size = LLOG_MIN_CHUNK_SIZE * 4;
rc = llog_open_create(env, ctxt, &lgh, NULL, NULL);
if (rc < 0)
GOTO(out_put, rc);
LASSERT(lgh != NULL);
rc = llog_init_handle(env, lgh, LLOG_F_IS_CAT, NULL);
if (rc != 0)
GOTO(out_close, rc);
cid->lci_logid = lgh->lgh_id;
rc = llog_osd_put_cat_list(env, dt, index, 1, cid, fid);
if (rc != 0)
GOTO(out_close, rc);
ctxt->loc_handle = lgh;
CDEBUG(D_INFO, "%s: recreate catalog "DFID"\n",
obd->obd_name, PLOGID(&cid->lci_logid));
out_close:
if (rc)
llog_cat_close(env, lgh);
out_put:
llog_ctxt_put(ctxt);
RETURN(rc);
}
/* retain update catalog and llogs, and create a new catalog */
static int lod_sub_cancel_llog(const struct lu_env *env,
struct lod_device *lod, struct dt_device *dt,
int index)
{
struct llog_ctxt *ctxt;
int rc = 0;
ctxt = llog_get_context(dt->dd_lu_dev.ld_obd,
LLOG_UPDATELOG_ORIG_CTXT);
if (!ctxt)
return 0;
if (ctxt->loc_handle) {
LCONSOLE(D_INFO, "%s: cancel update llog "DFID"\n",
dt->dd_lu_dev.ld_obd->obd_name,
PLOGID(&ctxt->loc_handle->lgh_id));
/* set retention on logs to simplify reclamation */
llog_process_or_fork(env, ctxt->loc_handle, llog_cat_retain_cb,
NULL, NULL, false);
}
/* retain old catalog and create a new one */
lod_sub_recreate_llog(env, lod, dt, index);
llog_ctxt_put(ctxt);
return rc;
}
/**
* lod_sub_recovery_thread() - recovery thread for update log
* @arg: pointer to the recovery data
*
* Start recovery thread and prepare the sub llog, then it will retrieve
* the update records from the correpondent MDT and do recovery.
*
* Return:
* * %0 if recovery succeeds
* * %Negative negative errno if recovery failed.
*/
static int lod_sub_recovery_thread(void *arg)
{
struct lod_recovery_data *lrd = arg;
struct lod_device *lod = lrd->lrd_lod;
struct dt_device *dt;
struct llog_ctxt *ctxt = NULL;
struct lu_env *env = &lrd->lrd_env;
struct lu_target *lut;
struct lu_tgt_desc *mdt = NULL;
struct lu_device *top_device;
time64_t start;
int retries = 0;
int rc;
ENTRY;
lut = lod2lu_dev(lod)->ld_site->ls_tgt;
atomic_inc(&lut->lut_tdtd->tdtd_recovery_threads_count);
if (!lrd->lrd_ltd)
dt = lod->lod_child;
else
dt = lrd->lrd_ltd->ltd_tgt;
start = ktime_get_real_seconds();
complete(lrd->lrd_started);
again:
if (unlikely(CFS_FAIL_PRECHECK(OBD_FAIL_TGT_RECOVERY_CONNECT)) &&
lrd->lrd_ltd) {
CFS_FAIL_TIMEOUT(OBD_FAIL_TGT_RECOVERY_CONNECT, cfs_fail_val);
rc = -EIO;
} else {
rc = lod_sub_prep_llog(env, lod, dt, lrd->lrd_idx);
}
if (!rc && !lod->lod_child->dd_rdonly) {
/* Process the recovery record */
ctxt = llog_get_context(dt->dd_lu_dev.ld_obd,
LLOG_UPDATELOG_ORIG_CTXT);
LASSERT(ctxt != NULL);
LASSERT(ctxt->loc_handle != NULL);
rc = llog_cat_process(env, ctxt->loc_handle,
lod_process_recovery_updates, lrd, 0, 0);
}
top_device = lod->lod_dt_dev.dd_lu_dev.ld_site->ls_top_dev;
if (rc < 0 && dt != lod->lod_child &&
!obd_mdt_recovery_abort(top_device->ld_obd)) {
if (rc == -EBADR) {
/* remote update llog is shorter than expected from
* local header. Cached copy could be de-synced during
* recovery, trust remote llog data
*/
CDEBUG(D_HA, "%s update log data de-sync\n",
dt->dd_lu_dev.ld_obd->obd_name);
rc = 0;
} else if (rc == -ETIMEDOUT || rc == -EAGAIN || rc == -EIO) {
/*
* the remote target might failover at the same time,
* let's retry here
*/
if (ctxt) {
if (ctxt->loc_handle)
llog_cat_close(env, ctxt->loc_handle);
llog_ctxt_put(ctxt);
ctxt = NULL;
}
retries++;
CDEBUG(D_HA, "%s get update log failed %d, retry\n",
dt->dd_lu_dev.ld_obd->obd_name, rc);
goto again;
}
}
llog_ctxt_put(ctxt);
if (rc < 0) {
CERROR("%s: get update log duration %lld, retries %d, failed: rc = %d\n",
dt->dd_lu_dev.ld_obd->obd_name,
ktime_get_real_seconds() - start, retries, rc);
/* abort MDT recovery of this target, but not all targets,
* because recovery still has chance to succeed.
*/
if (!obd_mdt_recovery_abort(top_device->ld_obd))
lod_sub_cancel_llog(env, lod, dt, lrd->lrd_idx);
} else {
CDEBUG(D_HA,
"%s retrieved update log, duration %lld, retries %d\n",
dt->dd_lu_dev.ld_obd->obd_name,
ktime_get_real_seconds() - start, retries);
}
spin_lock(&lod->lod_lock);
if (!lrd->lrd_ltd)
lod->lod_child_got_update_log = 1;
else
lrd->lrd_ltd->ltd_got_update_log = 1;
if (!lod->lod_child_got_update_log) {
spin_unlock(&lod->lod_lock);
GOTO(out, rc);
}
lod_foreach_mdt(lod, mdt) {
if (!mdt->ltd_got_update_log) {
spin_unlock(&lod->lod_lock);
GOTO(out, rc);
}
}
lut->lut_tdtd->tdtd_replay_ready = 1;
spin_unlock(&lod->lod_lock);
CDEBUG(D_HA, "%s got update logs from all MDTs.\n",
lut->lut_obd->obd_name);
wake_up(&lut->lut_obd->obd_next_transno_waitq);
EXIT;
out:
atomic_dec(&lut->lut_tdtd->tdtd_recovery_threads_count);
wake_up(&lut->lut_tdtd->tdtd_recovery_threads_waitq);
if (xchg(lrd->lrd_task, NULL) == NULL)
/* Someone is waiting for us to finish, need
* to synchronize cleanly.
*/
wait_var_event(lrd, kthread_should_stop());
lu_env_fini(env);
OBD_FREE_PTR(lrd);
return rc;
}
/**
* lod_sub_fini_llog() - finish sub llog context
* @env: execution environment
* @dt: device for which log context is being finalized. (sub-device could
* be OSD or MDT (which is part of LOD))
* @thread: recovery thread on this sub device
*
* Stop update recovery thread for the sub device, then cleanup the
* correspondent llog ctxt.
*/
void lod_sub_fini_llog(const struct lu_env *env,
struct dt_device *dt, struct task_struct **thread)
{
struct obd_device *obd;
struct llog_ctxt *ctxt;
struct task_struct *task = NULL;
ENTRY;
obd = dt->dd_lu_dev.ld_obd;
CDEBUG(D_INFO, "%s: finish sub llog\n", obd->obd_name);
/* Wait for recovery thread to complete */
if (thread)
task = xchg(thread, NULL);
if (task)
kthread_stop(task);
ctxt = llog_get_context(obd, LLOG_UPDATELOG_ORIG_CTXT);
if (!ctxt)
RETURN_EXIT;
if (ctxt->loc_handle)
llog_cat_close(env, ctxt->loc_handle);
llog_cleanup(env, ctxt);
RETURN_EXIT;
}
/**
* lodname2mdt_index() - Extract MDT target index from a device name.
* @lodname: device name
* @mdt_index: extracted index (out prameter)
*
* A helper function to extract index from the given device name
* like "fsname-MDTxxxx-mdtlov"
*
* Return:
* * %0 on success
* * %-EINVAL if the name is invalid
*/
int lodname2mdt_index(char *lodname, u32 *mdt_index)
{
u32 index;
const char *ptr, *tmp;
int rc;
/* 1.8 configs don't have "-MDT0000" at the end */
ptr = strstr(lodname, "-MDT");
if (!ptr) {
*mdt_index = 0;
return 0;
}
ptr = strrchr(lodname, '-');
if (!ptr) {
rc = -EINVAL;
CERROR("invalid MDT index in '%s': rc = %d\n", lodname, rc);
return rc;
}
if (strncmp(ptr, "-mdtlov", 7) != 0) {
rc = -EINVAL;
CERROR("invalid MDT index in '%s': rc = %d\n", lodname, rc);
return rc;
}
if ((unsigned long)ptr - (unsigned long)lodname <= 8) {
rc = -EINVAL;
CERROR("invalid MDT index in '%s': rc = %d\n", lodname, rc);
return rc;
}
if (strncmp(ptr - 8, "-MDT", 4) != 0) {
rc = -EINVAL;
CERROR("invalid MDT index in '%s': rc = %d\n", lodname, rc);
return rc;
}
rc = target_name2index(ptr - 7, &index, &tmp);
if (rc < 0 || rc & LDD_F_SV_ALL || *tmp != '-') {
rc = -EINVAL;
CERROR("invalid MDT index in '%s': rc = %d\n", lodname, rc);
return rc;
}
*mdt_index = index;
return 0;
}
/**
* lod_sub_init_llog() - Init sub llog context
* @env: execution environment
* @lod: lod device to do update recovery
* @dt: sub dt device for which the recovery thread is
*
* Setup update llog ctxt for update recovery threads, then start the
* recovery thread (lod_sub_recovery_thread) to read update llog from
* the correspondent MDT to do update recovery.
*
* Return:
* * %0 if initialization succeeds.
* * %negative errno if initialization fails.
*/
int lod_sub_init_llog(const struct lu_env *env, struct lod_device *lod,
struct dt_device *dt)
{
struct lod_recovery_data *lrd = NULL;
DECLARE_COMPLETION_ONSTACK(started);
struct lod_tgt_desc *subtgt = NULL;
struct task_struct **taskp;
struct task_struct *task;
struct obd_device *obd;
u32 master_index;
u32 index = 0;
int rc;
ENTRY;
rc = lodname2mdt_index(lod2obd(lod)->obd_name, &master_index);
if (rc != 0)
RETURN(rc);
OBD_ALLOC_PTR(lrd);
if (!lrd)
RETURN(-ENOMEM);
if (lod->lod_child == dt) {
taskp = &lod->lod_child_recovery_task;
index = master_index;
} else {
struct lu_tgt_desc *mdt;
lod_foreach_mdt(lod, mdt) {
if (mdt->ltd_tgt == dt) {
index = mdt->ltd_index;
subtgt = mdt;
break;
}
}
LASSERT(subtgt != NULL);
taskp = &subtgt->ltd_recovery_task;
}
CDEBUG(D_INFO, "%s init sub log %s\n", lod2obd(lod)->obd_name,
dt->dd_lu_dev.ld_obd->obd_name);
lrd->lrd_lod = lod;
lrd->lrd_ltd = subtgt;
lrd->lrd_task = taskp;
lrd->lrd_idx = index;
lrd->lrd_started = &started;
obd = dt->dd_lu_dev.ld_obd;
obd->obd_lvfs_ctxt.dt = dt;
rc = llog_setup(env, obd, &obd->obd_olg, LLOG_UPDATELOG_ORIG_CTXT,
NULL, &llog_common_cat_ops);
if (rc < 0) {
CERROR("%s: cannot setup updatelog llog: rc = %d\n",
obd->obd_name, rc);
GOTO(free_lrd, rc);
}
rc = lu_env_init(&lrd->lrd_env, LCT_LOCAL | LCT_MD_THREAD);
if (rc != 0) {
CERROR("%s: can't initialize env: rc = %d\n",
lod2obd(lod)->obd_name, rc);
GOTO(free_lrd, rc);
}
/* Start the recovery thread */
task = kthread_create(lod_sub_recovery_thread, lrd, "lod%04x_rec%04x",
master_index, index);
if (IS_ERR(task)) {
rc = PTR_ERR(task);
CERROR("%s: cannot start recovery thread: rc = %d\n",
obd->obd_name, rc);
lu_env_fini(&lrd->lrd_env);
GOTO(out_llog, rc);
}
*taskp = task;
wake_up_process(task);
wait_for_completion(&started);
RETURN(0);
out_llog:
lod_sub_fini_llog(env, dt, taskp);
free_lrd:
OBD_FREE_PTR(lrd);
RETURN(rc);
}
/**
* lod_sub_stop_recovery_threads() - Stop sub recovery thread
* @env: execution environment
* @lod: lod device to do update recovery
*
* Stop sub recovery thread on all subs.
*/
static void lod_sub_stop_recovery_threads(const struct lu_env *env,
struct lod_device *lod)
{
struct task_struct *task;
struct lu_tgt_desc *mdt;
/*
* Stop the update log commit cancel threads and finish master
* llog ctxt
*/
task = xchg(&lod->lod_child_recovery_task, NULL);
if (task)
kthread_stop(task);
lod_getref(&lod->lod_mdt_descs);
lod_foreach_mdt(lod, mdt) {
task = xchg(&mdt->ltd_recovery_task, NULL);
if (task)
kthread_stop(task);
}
lod_putref(lod, &lod->lod_mdt_descs);
}
/**
* lod_sub_fini_all_llogs() - finish all sub llog
* @env: execution environment
* @lod: lod device to do update recovery
*
* cleanup all of sub llog ctxt on the LOD.
*/
static void lod_sub_fini_all_llogs(const struct lu_env *env,
struct lod_device *lod)
{
struct lu_tgt_desc *mdt;
/*
* Stop the update log commit cancel threads and finish master
* llog ctxt
*/
lod_sub_fini_llog(env, lod->lod_child,
&lod->lod_child_recovery_task);
lod_getref(&lod->lod_mdt_descs);
lod_foreach_mdt(lod, mdt)
lod_sub_fini_llog(env, mdt->ltd_tgt,
&mdt->ltd_recovery_task);
lod_putref(lod, &lod->lod_mdt_descs);
}
static char *lod_show_update_logs_retrievers(void *data, int *size, int *count)
{
struct lod_device *lod = (struct lod_device *)data;
struct lu_target *lut = lod2lu_dev(lod)->ld_site->ls_tgt;
struct lu_tgt_desc *mdt = NULL;
char *buf;
int len = 0;
int rc;
int i;
*count = atomic_read(&lut->lut_tdtd->tdtd_recovery_threads_count);
if (*count == 0) {
*size = 0;
return NULL;
}
*size = 5 * *count + 1;
OBD_ALLOC(buf, *size);
if (!buf)
return NULL;
*count = 0;
memset(buf, 0, *size);
if (!lod->lod_child_got_update_log) {
rc = lodname2mdt_index(lod2obd(lod)->obd_name, &i);
LASSERTF(rc == 0, "Fail to parse target index: rc = %d\n", rc);
rc = scnprintf(buf + len, *size - len, " %04x", i);
LASSERT(rc > 0);
len += rc;
(*count)++;
}
lod_foreach_mdt(lod, mdt) {
if (!mdt->ltd_got_update_log) {
rc = scnprintf(buf + len, *size - len, " %04x",
mdt->ltd_index);
if (unlikely(rc <= 0))
break;
len += rc;
(*count)++;
}
}
return buf;
}
/**
* lod_prepare_distribute_txn() - Prepare distribute txn structure for LOD
* @env: execution environment
* @lod: LOD device
*
* Return:
* * %0 if preparation succeeds.
* * %negative errno if preparation fails.
*/
static int lod_prepare_distribute_txn(const struct lu_env *env,
struct lod_device *lod)
{
struct target_distribute_txn_data *tdtd;
struct lu_target *lut;
int rc;
ENTRY;
/* Init update recovery data */
OBD_ALLOC_PTR(tdtd);
if (!tdtd)
RETURN(-ENOMEM);
lut = lod2lu_dev(lod)->ld_site->ls_tgt;
tdtd->tdtd_dt = &lod->lod_dt_dev;
rc = distribute_txn_init(env, lut, tdtd,
lu_site2seq(lod2lu_dev(lod)->ld_site)->ss_node_id);
if (rc < 0) {
CERROR("%s: cannot init distribute txn: rc = %d\n",
lod2obd(lod)->obd_name, rc);
OBD_FREE_PTR(tdtd);
RETURN(rc);
}
tdtd->tdtd_show_update_logs_retrievers =
lod_show_update_logs_retrievers;
tdtd->tdtd_show_retrievers_cbdata = lod;
lut->lut_tdtd = tdtd;
RETURN(0);
}
/*
* lod_fini_distribute_txn() - Finish distribute txn
* @env: execution environment
* @lod: lod device
*
* Release the resource holding by distribute txn, i.e. stop distribute
* txn thread.
*/
static void lod_fini_distribute_txn(const struct lu_env *env,
struct lod_device *lod)
{
struct lu_target *lut;
lut = lod2lu_dev(lod)->ld_site->ls_tgt;
target_recovery_fini(lut->lut_obd);
if (!lut->lut_tdtd)
return;
distribute_txn_fini(env, lut->lut_tdtd);
OBD_FREE_PTR(lut->lut_tdtd);
lut->lut_tdtd = NULL;
}
/**
* lod_process_config() - Implementation of
* lu_device_operations::ldo_process_config() for LOD
* @env: LU environment provided by the caller
* @dev: lod device
* @lcfg: configuration command to apply
*
* The method is called by the configuration subsystem during setup,
* cleanup and when the configuration changes. The method processes
* few specific commands like adding/removing the targets, changing
* the runtime parameters.
*
* The examples are below.
*
* Add osc config log:
* -------------------
* marker 20 (flags=0x01, v2.2.49.56) lustre-OST0001 'add osc'
* add_uuid nid=192.168.122.162@tcp(0x20000c0a87aa2) 0: 1:nidxxx
* attach 0:lustre-OST0001-osc-MDT0001 1:osc 2:lustre-MDT0001-mdtlov_UUID
* setup 0:lustre-OST0001-osc-MDT0001 1:lustre-OST0001_UUID 2:nid
* lov_modify_tgts add 0:lustre-MDT0001-mdtlov 1:lustre-OST0001_UUID 2:1 3:1
* marker 20 (flags=0x02, v2.2.49.56) lustre-OST0001 'add osc'
*
* Add mdc config log:
* -------------------
* marker 10 (flags=0x01, v2.2.49.56) lustre-MDT0000 'add osp'
* add_uuid nid=192.168.122.162@tcp(0x20000c0a87aa2) 0: 1:nid
* attach 0:lustre-MDT0000-osp-MDT0001 1:osp 2:lustre-MDT0001-mdtlov_UUID
* setup 0:lustre-MDT0000-osp-MDT0001 1:lustre-MDT0000_UUID 2:nid
* modify_mdc_tgts add 0:lustre-MDT0001 1:lustre-MDT0000_UUID 2:0 3:1
* marker 10 (flags=0x02, v2.2.49.56) lustre-MDT0000_UUID 'add osp'
*
* Return:
* * %0 on success
* * %Negative negated errno on error
*/
static int lod_process_config(const struct lu_env *env,
struct lu_device *dev,
struct lustre_cfg *lcfg)
{
struct lod_device *lod = lu2lod_dev(dev);
struct lu_device *next = &lod->lod_child->dd_lu_dev;
char *arg1;
int rc = 0;
ENTRY;
switch (lcfg->lcfg_command) {
case LCFG_LOV_DEL_OBD:
case LCFG_LOV_ADD_INA:
case LCFG_LOV_ADD_OBD:
case LCFG_ADD_MDC: {
u32 index;
u32 mdt_index;
int gen;
/*
* lov_modify_tgts add 0:lov_mdsA 1:osp 2:0 3:1
* modify_mdc_tgts add 0:lustre-MDT0001
* 1:lustre-MDT0001-mdc0002
* 2:2 3:1
*/
arg1 = lustre_cfg_string(lcfg, 1);
if (sscanf(lustre_cfg_buf(lcfg, 2), "%d", &index) != 1)
GOTO(out, rc = -EINVAL);
if (sscanf(lustre_cfg_buf(lcfg, 3), "%d", &gen) != 1)
GOTO(out, rc = -EINVAL);
if (lcfg->lcfg_command == LCFG_LOV_ADD_OBD) {
u32 mdt_index;
rc = lodname2mdt_index(lustre_cfg_string(lcfg, 0),
&mdt_index);
if (rc != 0)
GOTO(out, rc);
rc = lod_add_device(env, lod, arg1, index, gen,
mdt_index, LUSTRE_OSC_NAME, 1);
} else if (lcfg->lcfg_command == LCFG_ADD_MDC) {
mdt_index = index;
rc = lod_add_device(env, lod, arg1, index, gen,
mdt_index, LUSTRE_MDC_NAME, 1);
} else if (lcfg->lcfg_command == LCFG_LOV_ADD_INA) {
/*FIXME: Add mdt_index for LCFG_LOV_ADD_INA*/
mdt_index = 0;
rc = lod_add_device(env, lod, arg1, index, gen,
mdt_index, LUSTRE_OSC_NAME, 0);
} else {
rc = lod_del_device(env, lod, &lod->lod_ost_descs,
arg1, index, gen);
}
break;
}
case LCFG_PARAM: {
struct obd_device *obd;
ssize_t count;
char *param;
/*
* Check if it is activate/deactivate mdc
* lustre-MDTXXXX-osp-MDTXXXX.active=1
*/
param = lustre_cfg_buf(lcfg, 1);
if (strstr(param, "osp") && strstr(param, ".active=")) {
struct lod_tgt_desc *sub_tgt = NULL;
struct lu_tgt_desc *mdt;
char *ptr;
char *tmp;
ptr = strstr(param, ".");
*ptr = '\0';
obd = class_name2obd(param);
if (!obd) {
CERROR("%s: can not find %s: rc = %d\n",
lod2obd(lod)->obd_name, param, -EINVAL);
*ptr = '.';
GOTO(out, rc);
}
lod_foreach_mdt(lod, mdt) {
if (mdt->ltd_tgt->dd_lu_dev.ld_obd == obd) {
sub_tgt = mdt;
break;
}
}
if (!sub_tgt) {
CERROR("%s: can not find %s: rc = %d\n",
lod2obd(lod)->obd_name, param, -EINVAL);
*ptr = '.';
GOTO(out, rc);
}
*ptr = '.';
tmp = strstr(param, "=");
tmp++;
if (*tmp == '1' && sub_tgt->ltd_active == 0) {
struct llog_ctxt *ctxt;
obd = sub_tgt->ltd_tgt->dd_lu_dev.ld_obd;
ctxt = llog_get_context(obd,
LLOG_UPDATELOG_ORIG_CTXT);
if (!ctxt) {
rc = llog_setup(env, obd, &obd->obd_olg,
LLOG_UPDATELOG_ORIG_CTXT,
NULL, &llog_common_cat_ops);
if (rc < 0)
GOTO(out, rc);
} else {
llog_ctxt_put(ctxt);
}
rc = lod_sub_prep_llog(env, lod,
sub_tgt->ltd_tgt,
sub_tgt->ltd_index);
sub_tgt->ltd_active = !rc;
} else if (*tmp == '0' && sub_tgt->ltd_active != 0) {
lod_sub_fini_llog(env, sub_tgt->ltd_tgt,
NULL);
sub_tgt->ltd_active = 0;
}
GOTO(out, rc);
}
if (strstr(param, PARAM_LOD) != NULL)
count = class_modify_config(lcfg, PARAM_LOD,
&lod->lod_dt_dev.dd_kobj);
else
count = class_modify_config(lcfg, PARAM_LOV,
&lod->lod_dt_dev.dd_kobj);
rc = count > 0 ? 0 : count;
GOTO(out, rc);
}
case LCFG_PRE_CLEANUP: {
lod_sub_process_config(env, lod, &lod->lod_mdt_descs, lcfg);
lod_sub_process_config(env, lod, &lod->lod_ost_descs, lcfg);
CFS_FAIL_TIMEOUT(OBD_FAIL_TGT_RECOVERY_CONNECT, cfs_fail_val * 2);
next = &lod->lod_child->dd_lu_dev;
rc = next->ld_ops->ldo_process_config(env, next, lcfg);
if (rc != 0)
CDEBUG(D_HA, "%s: can't process %u: %d\n",
lod2obd(lod)->obd_name, lcfg->lcfg_command, rc);
lod_sub_stop_recovery_threads(env, lod);
lod_fini_distribute_txn(env, lod);
lod_sub_fini_all_llogs(env, lod);
break;
}
case LCFG_CLEANUP: {
if (lod->lod_md_root) {
dt_object_put(env, &lod->lod_md_root->ldo_obj);
lod->lod_md_root = NULL;
}
/*
* do cleanup on underlying storage only when
* all OSPs are cleaned up, as they use that OSD as well
*/
lu_dev_del_linkage(dev->ld_site, dev);
lod_sub_process_config(env, lod, &lod->lod_mdt_descs, lcfg);
lod_sub_process_config(env, lod, &lod->lod_ost_descs, lcfg);
next = &lod->lod_child->dd_lu_dev;
rc = next->ld_ops->ldo_process_config(env, next, lcfg);
if (rc)
CERROR("%s: can't process %u: rc = %d\n",
lod2obd(lod)->obd_name, lcfg->lcfg_command, rc);
rc = obd_disconnect(lod->lod_child_exp);
if (rc)
CERROR("error in disconnect from storage: rc = %d\n",
rc);
break;
}
default:
CERROR("%s: unknown command %u\n", lod2obd(lod)->obd_name,
lcfg->lcfg_command);
rc = -EINVAL;
break;
}
out:
RETURN(rc);
}
/*
* Implementation of lu_device_operations::ldo_recovery_complete() for LOD
*
* The method is called once the recovery is complete. This implementation
* distributes the notification to all the known targets.
*
* see include/lu_object.h for the details
*/
static int lod_recovery_complete(const struct lu_env *env,
struct lu_device *dev)
{
struct lod_device *lod = lu2lod_dev(dev);
struct lu_device *next = &lod->lod_child->dd_lu_dev;
struct lod_tgt_desc *tgt;
int rc;
ENTRY;
LASSERT(lod->lod_recovery_completed == 0);
lod->lod_recovery_completed = 1;
rc = next->ld_ops->ldo_recovery_complete(env, next);
lod_getref(&lod->lod_ost_descs);
if (lod->lod_ost_descs.ltd_tgts_size > 0) {
lod_foreach_ost(lod, tgt) {
LASSERT(tgt && tgt->ltd_tgt);
next = &tgt->ltd_tgt->dd_lu_dev;
rc = next->ld_ops->ldo_recovery_complete(env, next);
if (rc)
CERROR("%s: can't complete recovery on #%d: rc = %d\n",
lod2obd(lod)->obd_name, tgt->ltd_index,
rc);
}
}
lod_putref(lod, &lod->lod_ost_descs);
RETURN(rc);
}
/**
* lod_sub_init_llogs() - Init update logs on all sub device
* @env: execution environment
* @lod: lod device
*
* LOD initialize update logs on all of sub devices. Because the initialization
* process might need FLD lookup, see llog_osd_open()->dt_locate()->...->
* lod_object_init(), this API has to be called after LOD is initialized.
*
* Return:
* * %0 if update log is initialized successfully
* * %Negative if initialization fails.
*/
static int lod_sub_init_llogs(const struct lu_env *env, struct lod_device *lod)
{
struct lu_tgt_desc *mdt;
int rc;
ENTRY;
/*
* llog must be setup after LOD is initialized, because llog
* initialization include FLD lookup
*/
LASSERT(lod->lod_initialized);
/* Init the llog in its own stack */
rc = lod_sub_init_llog(env, lod, lod->lod_child);
if (rc < 0)
RETURN(rc);
lod_foreach_mdt(lod, mdt) {
lod_sub_init_llog(env, lod, mdt->ltd_tgt);
}
RETURN(rc);
}
#define UPDATE_LOG_MAX_AGE (30 * 24 * 60 * 60) /* 30 days, in sec */
static int lod_update_log_stale(const struct lu_env *env, struct dt_object *dto,
struct lu_buf *buf)
{
struct lu_attr *attr = &lod_env_info(env)->lti_attr;
struct llog_log_hdr *hdr;
loff_t off = 0;
int rc;
ENTRY;
rc = dt_attr_get(env, dto, attr);
if (rc)
RETURN(rc);
if (!(attr->la_valid & (LA_CTIME | LA_SIZE)))
RETURN(-EFAULT);
/* by default update log ctime is not set */
if (attr->la_ctime == 0)
RETURN(0);
/* update log not expired yet */
if (attr->la_ctime + UPDATE_LOG_MAX_AGE > ktime_get_real_seconds())
RETURN(0);
if (attr->la_size == 0)
RETURN(-EFAULT);
rc = dt_read(env, dto, buf, &off);
if (rc < 0)
RETURN(rc);
hdr = (struct llog_log_hdr *)buf->lb_buf;
if (LLOG_REC_HDR_NEEDS_SWABBING(&hdr->llh_hdr))
lustre_swab_llog_hdr(hdr);
/* log header is sane and flag LLOG_F_MAX_AGE|LLOG_F_RM_ON_ERR is set */
if (rc >= sizeof(*hdr) &&
hdr->llh_hdr.lrh_type == LLOG_HDR_MAGIC &&
(hdr->llh_flags & (LLOG_F_MAX_AGE | LLOG_F_RM_ON_ERR)) ==
(LLOG_F_MAX_AGE | LLOG_F_RM_ON_ERR))
RETURN(1);
RETURN(0);
}
/*
* Reclaim stale update log.
*
* When update log is canceld (upon recovery abort), it's not destroy, but
* canceled from catlist, and set ctime and LLOG_F_MAX_AGE|LLOG_F_RM_ON_ERR,
* which is kept for debug. If it expired (more than UPDATE_LOG_MAX_AGE seconds
* passed), destroy it to save space.
*/
static int lod_update_log_gc(const struct lu_env *env, struct lod_device *lod,
struct dt_object *dir, struct dt_object *dto,
const char *name)
{
struct dt_device *dt = lod->lod_child;
struct thandle *th;
int rc;
ENTRY;
th = dt_trans_create(env, dt);
if (IS_ERR(th))
RETURN(PTR_ERR(th));
rc = dt_declare_delete(env, dir, (const struct dt_key *)name, th);
if (rc)
GOTO(out_trans, rc);
rc = dt_declare_ref_del(env, dto, th);
if (rc)
GOTO(out_trans, rc);
rc = dt_declare_destroy(env, dto, th);
if (rc)
GOTO(out_trans, rc);
rc = dt_trans_start_local(env, dt, th);
if (rc)
GOTO(out_trans, rc);
rc = dt_delete(env, dir, (const struct dt_key *)name, th);
if (rc)
GOTO(out_trans, rc);
dt_write_lock(env, dto, DT_TGT_CHILD);
rc = dt_ref_del(env, dto, th);
if (!rc)
rc = dt_destroy(env, dto, th);
dt_write_unlock(env, dto);
GOTO(out_trans, rc);
out_trans:
dt_trans_stop(env, dt, th);
return rc;
}
/* reclaim stale update llogs under "update_log_dir" */
static int lod_update_log_dir_gc(const struct lu_env *env,
struct lod_device *lod,
struct dt_object *dir)
{
struct lod_thread_info *info = lod_env_info(env);
struct lu_buf *buf = &info->lti_linkea_buf;
struct lu_dirent *ent = (struct lu_dirent *)info->lti_key;
struct lu_fid *fid = &info->lti_fid;
struct dt_it *it;
const struct dt_it_ops *iops;
struct dt_object *dto;
int rc;
ENTRY;
if (unlikely(!dt_try_as_dir(env, dir, true)))
RETURN(-ENOTDIR);
lu_buf_alloc(buf, sizeof(struct llog_log_hdr));
if (!buf->lb_buf)
RETURN(-ENOMEM);
iops = &dir->do_index_ops->dio_it;
it = iops->init(env, dir, LUDA_64BITHASH);
if (IS_ERR(it))
GOTO(out, rc = PTR_ERR(it));
rc = iops->load(env, it, 0);
if (rc == 0)
rc = iops->next(env, it);
else if (rc > 0)
rc = 0;
while (rc == 0) {
rc = iops->rec(env, it, (struct dt_rec *)ent, LUDA_64BITHASH);
if (rc != 0)
break;
ent->lde_namelen = le16_to_cpu(ent->lde_namelen);
if (ent->lde_name[0] == '.') {
if (ent->lde_namelen == 1)
goto next;
if (ent->lde_namelen == 2 && ent->lde_name[1] == '.')
goto next;
}
fid_le_to_cpu(fid, &ent->lde_fid);
dto = dt_locate(env, lod->lod_child, fid);
if (IS_ERR(dto))
goto next;
buf->lb_len = sizeof(struct llog_log_hdr);
if (lod_update_log_stale(env, dto, buf) == 1)
lod_update_log_gc(env, lod, dir, dto, ent->lde_name);
dt_object_put(env, dto);
next:
rc = iops->next(env, it);
}
iops->put(env, it);
iops->fini(env, it);
out:
buf->lb_len = sizeof(struct llog_log_hdr);
lu_buf_free(buf);
RETURN(rc > 0 ? 0 : rc);
}
/*
* Implementation of lu_device_operations::ldo_prepare() for LOD
*
* see include/lu_object.h for the details.
*/
static int lod_prepare(const struct lu_env *env, struct lu_device *pdev,
struct lu_device *cdev)
{
struct lod_device *lod = lu2lod_dev(cdev);
struct lu_device *next = &lod->lod_child->dd_lu_dev;
struct lu_fid *fid = &lod_env_info(env)->lti_fid;
int rc;
struct dt_object *root;
struct dt_object *dto;
u32 index;
ENTRY;
rc = next->ld_ops->ldo_prepare(env, pdev, next);
if (rc != 0) {
CERROR("%s: prepare bottom error: rc = %d\n",
lod2obd(lod)->obd_name, rc);
RETURN(rc);
}
lod->lod_initialized = 1;
rc = dt_root_get(env, lod->lod_child, fid);
if (rc < 0)
RETURN(rc);
root = dt_locate(env, lod->lod_child, fid);
if (IS_ERR(root))
RETURN(PTR_ERR(root));
/* Create update log object */
index = lu_site2seq(lod2lu_dev(lod)->ld_site)->ss_node_id;
lu_update_log_fid(fid, index);
dto = local_file_find_or_create_with_fid(env, lod->lod_child,
fid, root,
lod_update_log_name,
S_IFREG | 0644);
if (IS_ERR(dto))
GOTO(out_put, rc = PTR_ERR(dto));
dt_object_put(env, dto);
/* Create update log dir */
lu_update_log_dir_fid(fid, index);
dto = local_file_find_or_create_with_fid(env, lod->lod_child,
fid, root,
lod_update_log_dir_name,
S_IFDIR | 0644);
if (IS_ERR(dto))
GOTO(out_put, rc = PTR_ERR(dto));
lod_update_log_dir_gc(env, lod, dto);
dt_object_put(env, dto);
rc = lod_prepare_distribute_txn(env, lod);
if (rc != 0)
GOTO(out_put, rc);
rc = lod_sub_init_llogs(env, lod);
if (rc != 0)
GOTO(out_put, rc);
out_put:
dt_object_put(env, root);
RETURN(rc);
}
/*
* Implementation of lu_device_operations::ldo_fid_alloc() for LOD
*
* Find corresponding device by passed parent and name, and allocate FID from
* there.
*
* see include/lu_object.h for the details.
*/
static int lod_fid_alloc(const struct lu_env *env, struct lu_device *d,
struct lu_fid *fid, struct lu_object *parent,
const struct lu_name *name)
{
struct lod_device *lod = lu2lod_dev(d);
struct lod_object *lo = lu2lod_obj(parent);
struct dt_device *next;
int rc;
ENTRY;
/* if @parent is remote, we don't know whether its layout was changed,
* always reload layout.
*/
if (lu_object_remote(parent))
lod_striping_free(env, lo);
rc = lod_striping_load(env, lo);
if (rc)
RETURN(rc);
if (lo->ldo_dir_stripe_count > 0 && name) {
struct dt_object *stripe;
int idx;
idx = __lmv_name_to_stripe_index(lo->ldo_dir_hash_type,
lo->ldo_dir_stripe_count,
lo->ldo_dir_migrate_hash,
lo->ldo_dir_migrate_offset,
name->ln_name,
name->ln_namelen, true);
if (idx < 0)
RETURN(idx);
stripe = lo->ldo_stripe[idx];
if (!stripe || !dt_object_exists(stripe))
RETURN(-ENODEV);
next = lu2dt_dev(stripe->do_lu.lo_dev);
} else {
next = lod->lod_child;
}
rc = dt_fid_alloc(env, next, fid, parent, name);
RETURN(rc);
}
const struct lu_device_operations lod_lu_ops = {
.ldo_object_alloc = lod_object_alloc,
.ldo_process_config = lod_process_config,
.ldo_recovery_complete = lod_recovery_complete,
.ldo_prepare = lod_prepare,
.ldo_fid_alloc = lod_fid_alloc,
};
/*
* Implementation of dt_device_operations::dt_root_get() for LOD
*
* see include/dt_object.h for the details.
*/
static int lod_root_get(const struct lu_env *env,
struct dt_device *dev, struct lu_fid *f)
{
return dt_root_get(env, dt2lod_dev(dev)->lod_child, f);
}
static void lod_statfs_sum(struct obd_statfs *sfs,
struct obd_statfs *ost_sfs, int *bs)
{
while (ost_sfs->os_bsize < *bs) {
*bs >>= 1;
sfs->os_bsize >>= 1;
sfs->os_bavail <<= 1;
sfs->os_blocks <<= 1;
sfs->os_bfree <<= 1;
sfs->os_granted <<= 1;
}
while (ost_sfs->os_bsize > *bs) {
ost_sfs->os_bsize >>= 1;
ost_sfs->os_bavail <<= 1;
ost_sfs->os_blocks <<= 1;
ost_sfs->os_bfree <<= 1;
ost_sfs->os_granted <<= 1;
}
sfs->os_bavail += ost_sfs->os_bavail;
sfs->os_blocks += ost_sfs->os_blocks;
sfs->os_bfree += ost_sfs->os_bfree;
sfs->os_granted += ost_sfs->os_granted;
}
/*
* Implementation of dt_device_operations::dt_statfs() for LOD
*
* see include/dt_object.h for the details.
*/
static int lod_statfs(const struct lu_env *env, struct dt_device *dev,
struct obd_statfs *sfs, struct obd_statfs_info *info)
{
struct lod_device *lod = dt2lod_dev(dev);
struct lu_tgt_desc *tgt;
struct obd_statfs ost_sfs;
u64 ost_files = 0;
u64 ost_ffree = 0;
int rc, bs;
rc = dt_statfs(env, dt2lod_dev(dev)->lod_child, sfs);
if (rc)
GOTO(out, rc);
bs = sfs->os_bsize;
sfs->os_bavail = 0;
sfs->os_blocks = 0;
sfs->os_bfree = 0;
sfs->os_granted = 0;
lod_getref(&lod->lod_mdt_descs);
lod_foreach_mdt(lod, tgt) {
rc = dt_statfs(env, tgt->ltd_tgt, &ost_sfs);
/* ignore errors */
/* skip uninitialized sub-MDT, prevent divide-by-zero */
if (rc || ost_sfs.os_bsize == 0)
continue;
sfs->os_files += ost_sfs.os_files;
sfs->os_ffree += ost_sfs.os_ffree;
lod_statfs_sum(sfs, &ost_sfs, &bs);
/* only update MDT os_namelen, OSTs do not store filenames */
sfs->os_namelen = min(sfs->os_namelen, ost_sfs.os_namelen);
}
lod_putref(lod, &lod->lod_mdt_descs);
/*
* at some point we can check whether DoM is enabled and
* decide how to account MDT space. for simplicity let's
* just fallback to pre-DoM policy if any OST is alive
*/
lod_getref(&lod->lod_ost_descs);
lod_foreach_ost(lod, tgt) {
rc = dt_statfs(env, tgt->ltd_tgt, &ost_sfs);
/* ignore errors */
if (rc || ost_sfs.os_bsize == 0)
continue;
if (!ost_files) {
/*
* if only MDTs with DoM then report only MDT blocks,
* otherwise show only OST blocks, and DoM is "free"
*/
sfs->os_bavail = 0;
sfs->os_blocks = 0;
sfs->os_bfree = 0;
sfs->os_granted = 0;
}
ost_files += ost_sfs.os_files;
ost_ffree += ost_sfs.os_ffree;
ost_sfs.os_bavail += ost_sfs.os_granted;
lod_statfs_sum(sfs, &ost_sfs, &bs);
LASSERTF(bs == ost_sfs.os_bsize, "%u != %u\n",
sfs->os_bsize, ost_sfs.os_bsize);
/* only update OST os_maxbytes, DoM files are small */
sfs->os_maxbytes = min(sfs->os_maxbytes, ost_sfs.os_maxbytes);
}
lod_putref(lod, &lod->lod_ost_descs);
sfs->os_state |= OS_STATFS_SUM;
/* If we have _some_ OSTs, but don't have as many free objects on the
* OSTs as inodes on the MDTs, reduce the reported number of inodes
* to compensate, so that the "inodes in use" number is correct.
* This should be kept in sync with ll_statfs_internal().
*/
if (ost_files && ost_ffree < sfs->os_ffree) {
sfs->os_files = (sfs->os_files - sfs->os_ffree) + ost_ffree;
sfs->os_ffree = ost_ffree;
}
/* a single successful statfs should be enough */
rc = 0;
out:
RETURN(rc);
}
/*
* Implementation of dt_device_operations::dt_trans_create() for LOD
*
* Creates a transaction using local (to this node) OSD.
*
* see include/dt_object.h for the details.
*/
static struct thandle *lod_trans_create(const struct lu_env *env,
struct dt_device *dt)
{
struct lod_thread_info *info = lod_env_info(env);
struct thandle *th;
th = top_trans_create(env, dt2lod_dev(dt)->lod_child);
if (IS_ERR(th))
return th;
th->th_dev = dt;
/* initialize some lod_thread_info members */
info->lti_obj_count = 0;
return th;
}
/* distributed transaction failure may cause object missing or disconnected
* directories, check space before transaction start.
*/
static int lod_trans_space_check(const struct lu_env *env,
struct lod_device *lod,
struct thandle *th)
{
struct lod_thread_info *info = lod_env_info(env);
struct obd_statfs *sfs = &info->lti_osfs;
struct top_thandle *top_th = container_of(th, struct top_thandle,
tt_super);
struct top_multiple_thandle *tmt = top_th->tt_multiple_thandle;
struct sub_thandle *st;
int rc;
if (likely(!tmt))
return 0;
list_for_each_entry(st, &tmt->tmt_sub_thandle_list, st_sub_list) {
struct dt_device *sub_dt;
if (st->st_sub_th == NULL)
continue;
if (st->st_sub_th == top_th->tt_master_sub_thandle)
continue;
sub_dt = st->st_sub_th->th_dev;
rc = dt_statfs(env, sub_dt, sfs);
if (rc) {
CDEBUG(D_INFO, "%s: fail - statfs error: rc = %d\n",
sub_dt->dd_lu_dev.ld_obd->obd_name, rc);
/* statfs may fail during recovery, skip check */
if (!lod->lod_recovery_completed)
rc = 0;
return rc;
}
if (unlikely(sfs->os_state &
(OS_STATFS_ENOINO | OS_STATFS_ENOSPC))) {
CDEBUG(D_INFO, "%s: fail - target state %x: rc = %d\n",
sub_dt->dd_lu_dev.ld_obd->obd_name,
sfs->os_state, -ENOSPC);
return -ENOSPC;
}
}
return 0;
}
/*
* Implementation of dt_device_operations::dt_trans_start() for LOD
*
* Starts the set of local transactions using the targets involved
* in declare phase. Initial support for the distributed transactions.
*
* see include/dt_object.h for the details.
*/
static int lod_trans_start(const struct lu_env *env, struct dt_device *dt,
struct thandle *th)
{
struct lod_device *lod = dt2lod_dev(dt);
if (lod->lod_dist_txn_check_space) {
int rc;
rc = lod_trans_space_check(env, lod, th);
if (rc)
return rc;
}
return top_trans_start(env, lod->lod_child, th);
}
static int lod_trans_cb_add(struct thandle *th,
struct dt_txn_commit_cb *dcb)
{
struct top_thandle *top_th = container_of(th, struct top_thandle,
tt_super);
return dt_trans_cb_add(top_th->tt_master_sub_thandle, dcb);
}
/**
* lod_add_noop_records() - add noop update to the update records
* @env: execution environment
* @dt: dt device of lod
* @th: thandle
* @count: the count of update records to be added.
*
* Add noop updates to the update records, which is only used in
* test right now.
*
* Return:
* * %0 if adding succeeds.
* * %negative errno if adding fails.
*/
static int lod_add_noop_records(const struct lu_env *env,
struct dt_device *dt, struct thandle *th,
int count)
{
struct top_thandle *top_th;
struct lu_fid *fid = &lod_env_info(env)->lti_fid;
int i;
int rc = 0;
top_th = container_of(th, struct top_thandle, tt_super);
if (!top_th->tt_multiple_thandle)
return 0;
fid_zero(fid);
for (i = 0; i < count; i++) {
rc = update_record_pack(noop, th, fid);
if (rc < 0)
return rc;
}
return rc;
}
/*
* Implementation of dt_device_operations::dt_trans_stop() for LOD
*
* Stops the set of local transactions using the targets involved
* in declare phase. Initial support for the distributed transactions.
*
* see include/dt_object.h for the details.
*/
static int lod_trans_stop(const struct lu_env *env, struct dt_device *dt,
struct thandle *th)
{
if (CFS_FAIL_CHECK(OBD_FAIL_SPLIT_UPDATE_REC)) {
int rc;
rc = lod_add_noop_records(env, dt, th, 5000);
if (rc < 0)
RETURN(rc);
}
return top_trans_stop(env, dt2lod_dev(dt)->lod_child, th);
}
/*
* Implementation of dt_device_operations::dt_conf_get() for LOD
*
* Currently returns the configuration provided by the local OSD.
*
* see include/dt_object.h for the details.
*/
static void lod_conf_get(const struct lu_env *env,
const struct dt_device *dev,
struct dt_device_param *param)
{
dt_conf_get(env, dt2lod_dev((struct dt_device *)dev)->lod_child, param);
}
/*
* Implementation of dt_device_operations::dt_sync() for LOD
*
* Syncs all known OST targets. Very very expensive and used
* rarely by LFSCK now. Should not be used in general.
*
* see include/dt_object.h for the details.
*/
static int lod_sync(const struct lu_env *env, struct dt_device *dev)
{
struct lod_device *lod = dt2lod_dev(dev);
struct lu_tgt_desc *tgt;
int rc = 0;
int rc2;
ENTRY;
lod_getref(&lod->lod_ost_descs);
lod_foreach_ost(lod, tgt) {
if (tgt->ltd_discon)
continue;
rc2 = dt_sync(env, tgt->ltd_tgt);
if (rc2) {
int level;
if (rc2 == -ENOTCONN) {
rc2 = 0;
level = D_INFO;
} else {
level = D_ERROR;
}
CDEBUG_LIMIT(level,
"%s: cannot sync OST%04x: rc = %d\n",
lod2obd(lod)->obd_name, tgt->ltd_index,
rc2);
if (!rc)
rc = rc2;
}
}
lod_putref(lod, &lod->lod_ost_descs);
lod_getref(&lod->lod_mdt_descs);
lod_foreach_mdt(lod, tgt) {
if (tgt->ltd_discon)
continue;
rc2 = dt_sync(env, tgt->ltd_tgt);
if (rc2) {
int level;
if (rc2 == -ENOTCONN) {
rc2 = 0;
level = D_INFO;
} else {
level = D_ERROR;
}
CDEBUG_LIMIT(level,
"%s: cannot sync MDT%04x: rc = %d\n",
lod2obd(lod)->obd_name, tgt->ltd_index,
rc2);
if (!rc)
rc = rc2;
}
}
lod_putref(lod, &lod->lod_mdt_descs);
rc2 = dt_sync(env, lod->lod_child);
if (rc2 && !rc)
rc = rc2;
RETURN(rc);
}
/*
* Implementation of dt_device_operations::dt_ro() for LOD
*
* Turns local OSD read-only, used for the testing only.
*
* see include/dt_object.h for the details.
*/
static int lod_ro(const struct lu_env *env, struct dt_device *dev)
{
return dt_ro(env, dt2lod_dev(dev)->lod_child);
}
/*
* Implementation of dt_device_operations::dt_commit_async() for LOD
*
* Asks local OSD to commit sooner.
*
* see include/dt_object.h for the details.
*/
static int lod_commit_async(const struct lu_env *env, struct dt_device *dev)
{
return dt_commit_async(env, dt2lod_dev(dev)->lod_child);
}
static const struct dt_device_operations lod_dt_ops = {
.dt_root_get = lod_root_get,
.dt_statfs = lod_statfs,
.dt_trans_create = lod_trans_create,
.dt_trans_start = lod_trans_start,
.dt_trans_stop = lod_trans_stop,
.dt_conf_get = lod_conf_get,
.dt_sync = lod_sync,
.dt_ro = lod_ro,
.dt_commit_async = lod_commit_async,
.dt_trans_cb_add = lod_trans_cb_add,
};
/**
* lod_connect_to_osd() - Connect to a local OSD.
* @env: LU environment provided by the caller
* @lod: lod device
* @cfg: configuration command to apply
*
* Used to connect to the local OSD at mount. OSD name is taken from the
* configuration command passed. This connection is used to identify LU
* site and pin the OSD from early removal.
*
* Return:
* * %0 on success
* * %negative negated errno on error
*/
static int lod_connect_to_osd(const struct lu_env *env, struct lod_device *lod,
struct lustre_cfg *cfg)
{
struct obd_connect_data *data = NULL;
struct obd_device *obd;
char *nextdev = NULL, *p, *s;
int rc, len = 0;
ENTRY;
LASSERT(cfg);
LASSERT(lod->lod_child_exp == NULL);
/*
* compatibility hack: we still use old config logs
* which specify LOV, but we need to learn underlying
* OSD device, which is supposed to be:
* <fsname>-MDTxxxx-osd
*
* 2.x MGS generates lines like the following:
* #03 (176)lov_setup 0:lustre-MDT0000-mdtlov 1:(struct lov_desc)
* 1.8 MGS generates lines like the following:
* #03 (168)lov_setup 0:lustre-mdtlov 1:(struct lov_desc)
*
* we use "-MDT" to differentiate 2.x from 1.8
*/
p = lustre_cfg_string(cfg, 0);
if (p && strstr(p, "-mdtlov")) {
len = strlen(p) + 6;
OBD_ALLOC(nextdev, len);
if (!nextdev)
GOTO(out, rc = -ENOMEM);
strcpy(nextdev, p);
s = strstr(nextdev, "-mdtlov");
if (unlikely(!s)) {
CERROR("%s: unable to parse device name: rc = %d\n",
lustre_cfg_string(cfg, 0), -EINVAL);
GOTO(out, rc = -EINVAL);
}
if (strstr(nextdev, "-MDT")) {
/* 2.x config */
strcpy(s, "-osd");
} else {
/* 1.8 config */
strcpy(s, "-MDT0000-osd");
}
} else {
CERROR("%s: unable to parse device name: rc = %d\n",
lustre_cfg_string(cfg, 0), -EINVAL);
GOTO(out, rc = -EINVAL);
}
OBD_ALLOC_PTR(data);
if (!data)
GOTO(out, rc = -ENOMEM);
obd = class_name2obd(nextdev);
if (!obd) {
CERROR("%s: can not locate next device: rc = %d\n",
nextdev, -ENOTCONN);
GOTO(out, rc = -ENOTCONN);
}
data->ocd_connect_flags = OBD_CONNECT_VERSION;
data->ocd_version = LUSTRE_VERSION_CODE;
rc = obd_connect(env, &lod->lod_child_exp, obd, &obd->obd_uuid,
data, NULL);
if (rc) {
CERROR("%s: cannot connect to next dev: rc = %d\n",
nextdev, rc);
GOTO(out, rc);
}
lod->lod_dt_dev.dd_lu_dev.ld_site =
lod->lod_child_exp->exp_obd->obd_lu_dev->ld_site;
LASSERT(lod->lod_dt_dev.dd_lu_dev.ld_site);
lod->lod_child = lu2dt_dev(lod->lod_child_exp->exp_obd->obd_lu_dev);
out:
OBD_FREE_PTR(data);
OBD_FREE(nextdev, len);
RETURN(rc);
}
static int lod_lsfs_init(const struct lu_env *env, struct lod_device *d)
{
struct obd_statfs sfs;
int rc;
rc = dt_statfs(env, d->lod_child, &sfs);
if (rc) {
CDEBUG(D_LAYOUT, "%s: failed to get OSD statfs, rc = %d\n",
lod2obd(d)->obd_name, rc);
return rc;
}
/* udpate local OSD cached statfs data */
spin_lock_init(&d->lod_lsfs_lock);
d->lod_lsfs_age = ktime_get_seconds();
d->lod_lsfs_total_mb = (sfs.os_blocks * sfs.os_bsize) >> 20;
d->lod_lsfs_free_mb = (sfs.os_bfree * sfs.os_bsize) >> 20;
return 0;
}
/**
* lod_init0() - Initialize LOD device at setup.
* @env: LU environment provided by the caller
* @lod: lod device
* @ldt: not used
* @cfg: configuration command
*
* Initializes the given LOD device using the original configuration command.
* The function initiates a connection to the local OSD and initializes few
* internal structures like pools, target tables, etc.
*
* Return:
* * %0 on success
* * %negative negated errno on error
*/
static int lod_init0(const struct lu_env *env, struct lod_device *lod,
struct lu_device_type *ldt, struct lustre_cfg *cfg)
{
struct dt_device_param ddp;
struct obd_device *obd;
int rc;
ENTRY;
obd = class_name2obd(lustre_cfg_string(cfg, 0));
if (!obd) {
rc = -ENODEV;
CERROR("Cannot find obd with name '%s': rc = %d\n",
lustre_cfg_string(cfg, 0), rc);
RETURN(rc);
}
obd->obd_lu_dev = &lod->lod_dt_dev.dd_lu_dev;
lod->lod_dt_dev.dd_lu_dev.ld_obd = obd;
lod->lod_dt_dev.dd_lu_dev.ld_ops = &lod_lu_ops;
lod->lod_dt_dev.dd_ops = &lod_dt_ops;
rc = lod_connect_to_osd(env, lod, cfg);
if (rc)
RETURN(rc);
dt_conf_get(env, &lod->lod_dt_dev, &ddp);
lod->lod_osd_max_easize = ddp.ddp_max_ea_size;
lod->lod_dom_stripesize_max_kb = (1ULL << 10); /* 1Mb is default */
lod->lod_mirror_count_max = LUSTRE_MIRROR_COUNT_DEF;
lod->lod_max_stripecount = 0;
lod->lod_max_stripes_per_mdt = LMV_MAX_STRIPES_PER_MDT;
/* initialize local statfs cached values */
rc = lod_lsfs_init(env, lod);
if (rc)
GOTO(out_disconnect, rc);
/* default threshold as half of total space, in MiB */
lod->lod_dom_threshold_free_mb = lod->lod_lsfs_total_mb / 2;
/* set default DoM stripe size based on free space amount */
lod_dom_stripesize_recalc(lod);
/* setup obd to be used with old lov code */
rc = lod_pools_init(lod, cfg);
if (rc)
GOTO(out_disconnect, rc);
spin_lock_init(&lod->lod_lock);
spin_lock_init(&lod->lod_connects_lock);
lu_tgt_descs_init(&lod->lod_mdt_descs, true);
lu_tgt_descs_init(&lod->lod_ost_descs, false);
lu_qos_rr_init(&lod->lod_mdt_descs.ltd_qos.lq_rr);
lu_qos_rr_init(&lod->lod_ost_descs.ltd_qos.lq_rr);
lod->lod_dist_txn_check_space = 1;
rc = lod_procfs_init(lod);
if (rc)
GOTO(out_desc, rc);
RETURN(0);
out_desc:
lod_fini_tgt(env, lod, &lod->lod_ost_descs);
lod_fini_tgt(env, lod, &lod->lod_mdt_descs);
lod_pools_fini(lod);
out_disconnect:
obd_disconnect(lod->lod_child_exp);
RETURN(rc);
}
/**
* lod_device_free() - Implementation of
* lu_device_type_operations::ldto_device_free() for LOD
* @env: execution environment
* @lu: lu_device pointing to LOD
*
* Releases the memory allocated for LOD device.
* see include/lu_object.h for the details.
*
* Return pointer to lu_device on success
*/
static struct lu_device *lod_device_free(const struct lu_env *env,
struct lu_device *lu)
{
struct lod_device *lod = lu2lod_dev(lu);
struct lu_device *next = &lod->lod_child->dd_lu_dev;
ENTRY;
if (atomic_read(&lu->ld_site->ls_obj_hash.nelems)) {
lu_site_print(env, lu->ld_site, &lu->ld_ref, D_ERROR,
lu_cdebug_printer);
}
LASSERTF(atomic_read(&lu->ld_ref) == 0, "lu is %px\n", lu);
dt_device_fini(&lod->lod_dt_dev);
OBD_FREE_PTR(lod);
RETURN(next);
}
/*
* Implementation of lu_device_type_operations::ldto_device_alloc() for LOD
*
* Allocates LOD device and calls the helpers to initialize it.
*
* see include/lu_object.h for the details.
*/
static struct lu_device *lod_device_alloc(const struct lu_env *env,
struct lu_device_type *type,
struct lustre_cfg *lcfg)
{
struct lod_device *lod;
struct lu_device *lu_dev;
OBD_ALLOC_PTR(lod);
if (!lod) {
lu_dev = ERR_PTR(-ENOMEM);
} else {
int rc;
lu_dev = lod2lu_dev(lod);
dt_device_init(&lod->lod_dt_dev, type);
rc = lod_init0(env, lod, type, lcfg);
if (rc != 0) {
lod_device_free(env, lu_dev);
lu_dev = ERR_PTR(rc);
}
}
return lu_dev;
}
static void lod_avoid_guide_fini(struct lod_avoid_guide *lag)
{
if (lag->lag_oss_avoid_array)
OBD_FREE_PTR_ARRAY(lag->lag_oss_avoid_array,
lag->lag_oaa_size);
bitmap_free(lag->lag_ost_avoid_bitmap);
}
/*
* Implementation of lu_device_type_operations::ldto_device_fini() for LOD
*
* Releases the internal resources used by LOD device.
*
* see include/lu_object.h for the details.
*/
static struct lu_device *lod_device_fini(const struct lu_env *env,
struct lu_device *d)
{
struct lod_device *lod = lu2lod_dev(d);
int rc;
ENTRY;
lod_pools_fini(lod);
lod_procfs_fini(lod);
rc = lod_fini_tgt(env, lod, &lod->lod_ost_descs);
if (rc)
CERROR("%s: can not fini ost descriptors: rc = %d\n",
lod2obd(lod)->obd_name, rc);
rc = lod_fini_tgt(env, lod, &lod->lod_mdt_descs);
if (rc)
CERROR("%s: can not fini mdt descriptors: rc = %d\n",
lod2obd(lod)->obd_name, rc);
RETURN(NULL);
}
/**
* lod_obd_connect() - Implementation of obd_ops::o_connect() for LOD
* @env: LU environment provided by the caller
* @exp: export the caller will be using to access LOD
* @obd: OBD device representing LOD device
* @cluuid: unique identifier of the caller
* @data: not used
* @localdata: not used
*
* Used to track all the users of this specific LOD device,
* so the device stays up until the last user disconnected.
*
* Return:
* * %0 on success
* * %negative negated errno on error
**/
static int lod_obd_connect(const struct lu_env *env, struct obd_export **exp,
struct obd_device *obd, struct obd_uuid *cluuid,
struct obd_connect_data *data, void *localdata)
{
struct lod_device *lod = lu2lod_dev(obd->obd_lu_dev);
struct lustre_handle conn;
int rc;
ENTRY;
CDEBUG(D_CONFIG, "connect #%d\n", lod->lod_connects);
rc = class_connect(&conn, obd, cluuid);
if (rc)
RETURN(rc);
*exp = class_conn2export(&conn);
spin_lock(&lod->lod_connects_lock);
lod->lod_connects++;
/* at the moment we expect the only user */
LASSERT(lod->lod_connects == 1);
spin_unlock(&lod->lod_connects_lock);
RETURN(0);
}
/**
* lod_obd_disconnect() - Implementation of obd_ops::o_disconnect() for LOD
* @exp: export provided to the caller in obd_connect()
*
* When the caller doesn't need to use this LOD instance, it calls
* obd_disconnect() and LOD releases corresponding export/reference count.
* Once all the users gone, LOD device is released.
*
* Return:
* * %0: on success
* * %negative: negated errno on error
*/
static int lod_obd_disconnect(struct obd_export *exp)
{
struct obd_device *obd = exp->exp_obd;
struct lod_device *lod = lu2lod_dev(obd->obd_lu_dev);
int rc, release = 0;
ENTRY;
/* Only disconnect the underlying layers on the final disconnect. */
spin_lock(&lod->lod_connects_lock);
lod->lod_connects--;
if (lod->lod_connects != 0) {
/* why should there be more than 1 connect? */
spin_unlock(&lod->lod_connects_lock);
CERROR("%s: disconnect #%d\n", exp->exp_obd->obd_name,
lod->lod_connects);
goto out;
}
spin_unlock(&lod->lod_connects_lock);
/* the last user of lod has gone, let's release the device */
release = 1;
out:
rc = class_disconnect(exp); /* bz 9811 */
if (rc == 0 && release)
class_manual_cleanup(obd);
RETURN(rc);
}
LU_KEY_INIT(lod, struct lod_thread_info);
static void lod_key_fini(const struct lu_context *ctx,
struct lu_context_key *key, void *data)
{
struct lod_thread_info *info = data;
struct lod_layout_component *lds =
info->lti_def_striping.lds_def_comp_entries;
/*
* allocated in lod_get_lov_ea
* XXX: this is overload, a tread may have such store but used only
* once. Probably better would be pool of such stores per LOD.
*/
lu_buf_free(&info->lti_ea_buf);
lu_buf_free(&info->lti_linkea_buf);
if (lds)
lod_free_def_comp_entries(&info->lti_def_striping);
if (info->lti_comp_size > 0)
OBD_FREE_PTR_ARRAY(info->lti_comp_idx,
info->lti_comp_size);
lod_avoid_guide_fini(&info->lti_avoid);
OBD_FREE_PTR(info);
}
/* context key: lod_thread_key */
LU_CONTEXT_KEY_DEFINE(lod, LCT_MD_THREAD);
LU_TYPE_INIT_FINI(lod, &lod_thread_key);
static const struct lu_device_type_operations lod_device_type_ops = {
.ldto_init = lod_type_init,
.ldto_fini = lod_type_fini,
.ldto_start = lod_type_start,
.ldto_stop = lod_type_stop,
.ldto_device_alloc = lod_device_alloc,
.ldto_device_free = lod_device_free,
.ldto_device_fini = lod_device_fini
};
static struct lu_device_type lod_device_type = {
.ldt_tags = LU_DEVICE_DT,
.ldt_name = LUSTRE_LOD_NAME,
.ldt_ops = &lod_device_type_ops,
.ldt_ctx_tags = LCT_MD_THREAD,
};
/**
* lod_obd_get_info() - Implementation of obd_ops::o_get_info() for LOD
* @env: LU environment provided by the caller
* @exp: export of the caller
* @keylen: len of the key
* @key: the key
* @vallen: not used
* @val: not used
*
* Currently, there is only one supported key: KEY_OSP_CONNECTED , to provide
* the caller binary status whether LOD has seen connection to any OST target.
* It will also check if the MDT update log context being initialized (if
* needed).
*
* Return:
* * %0 if a connection was seen
* * %-EAGAIN if LOD isn't running yet or no connection has been seen yet
* * %-EINVAL if not supported key is requested
**/
static int lod_obd_get_info(const struct lu_env *env, struct obd_export *exp,
u32 keylen, void *key, u32 *vallen, void *val)
{
int rc = -EINVAL;
if (KEY_IS(KEY_OSP_CONNECTED)) {
struct obd_device *obd = exp->exp_obd;
struct lod_device *d;
struct lod_tgt_desc *tgt;
int rc = 1;
if (!test_bit(OBDF_SET_UP, obd->obd_flags) ||
test_bit(OBDF_STOPPING, obd->obd_flags))
RETURN(-EAGAIN);
d = lu2lod_dev(obd->obd_lu_dev);
lod_getref(&d->lod_ost_descs);
lod_foreach_ost(d, tgt) {
rc = obd_get_info(env, tgt->ltd_exp, keylen, key,
vallen, val);
/* one healthy device is enough */
if (rc == 0)
break;
}
lod_putref(d, &d->lod_ost_descs);
lod_getref(&d->lod_mdt_descs);
lod_foreach_mdt(d, tgt) {
struct llog_ctxt *ctxt;
struct obd_device *ld = tgt->ltd_tgt->dd_lu_dev.ld_obd;
if (!tgt->ltd_active)
continue;
ctxt = llog_get_context(ld, LLOG_UPDATELOG_ORIG_CTXT);
LASSERT(ctxt != NULL);
if (!ctxt->loc_handle) {
CDEBUG(D_INFO, "%s: %s is not ready(%p).\n",
obd->obd_name, ld->obd_name, ctxt);
llog_ctxt_put(ctxt);
rc = -EAGAIN;
break;
}
llog_ctxt_put(ctxt);
}
lod_putref(d, &d->lod_mdt_descs);
RETURN(rc);
}
RETURN(rc);
}
static int lod_obd_set_info_async(const struct lu_env *env,
struct obd_export *exp,
u32 keylen, void *key,
u32 vallen, void *val,
struct ptlrpc_request_set *set)
{
struct obd_device *obd = class_exp2obd(exp);
struct lod_device *d;
struct lod_tgt_desc *tgt;
int no_set = 0;
int rc = 0, rc2;
ENTRY;
if (!set) {
no_set = 1;
set = ptlrpc_prep_set();
if (!set)
RETURN(-ENOMEM);
}
d = lu2lod_dev(obd->obd_lu_dev);
lod_getref(&d->lod_ost_descs);
lod_foreach_ost(d, tgt) {
if (tgt->ltd_discon)
continue;
rc2 = obd_set_info_async(env, tgt->ltd_exp, keylen, key,
vallen, val, set);
if (rc2 != 0 && rc == 0)
rc = rc2;
}
lod_putref(d, &d->lod_ost_descs);
lod_getref(&d->lod_mdt_descs);
lod_foreach_mdt(d, tgt) {
if (tgt->ltd_discon)
continue;
rc2 = obd_set_info_async(env, tgt->ltd_exp, keylen, key,
vallen, val, set);
if (rc2 != 0 && rc == 0)
rc = rc2;
}
lod_putref(d, &d->lod_mdt_descs);
if (no_set) {
rc2 = ptlrpc_set_wait(env, set);
if (rc2 == 0 && rc == 0)
rc = rc2;
ptlrpc_set_destroy(set);
}
RETURN(rc);
}
#define QMT0_DEV_NAME_LEN (LUSTRE_MAXFSNAME + sizeof("-QMT0000"))
static struct obd_device *obd_find_qmt0(char *obd_name)
{
char qmt_name[QMT0_DEV_NAME_LEN];
struct obd_device *qmt = NULL;
if (!server_name2fsname(obd_name, qmt_name, NULL)) {
strlcat(qmt_name, "-QMT0000", QMT0_DEV_NAME_LEN);
qmt = class_name2obd(qmt_name);
}
return qmt;
}
/* Run QMT0000 pool operations only for MDT0000 */
static inline bool lod_pool_need_qmt0(const char *obd_name)
{
__u32 idx;
int type;
type = server_name2index(obd_name, &idx, NULL);
return type == LDD_F_SV_TYPE_MDT && idx == 0;
}
static int lod_pool_new_q(struct obd_device *obd, char *poolname)
{
int err = lod_pool_new(obd, poolname);
if (!err && lod_pool_need_qmt0(obd->obd_name)) {
obd = obd_find_qmt0(obd->obd_name);
if (obd)
obd_pool_new(obd, poolname);
}
return err;
}
static int lod_pool_remove_q(struct obd_device *obd, char *poolname,
char *ostname)
{
int err = lod_pool_remove(obd, poolname, ostname);
if (!err && lod_pool_need_qmt0(obd->obd_name)) {
obd = obd_find_qmt0(obd->obd_name);
if (obd)
obd_pool_rem(obd, poolname, ostname);
}
return err;
}
static int lod_pool_add_q(struct obd_device *obd, char *poolname, char *ostname)
{
int err = lod_pool_add(obd, poolname, ostname);
if (!err && lod_pool_need_qmt0(obd->obd_name)) {
obd = obd_find_qmt0(obd->obd_name);
if (obd)
obd_pool_add(obd, poolname, ostname);
}
return err;
}
static int lod_pool_del_q(struct obd_device *obd, char *poolname)
{
int err = lod_pool_del(obd, poolname);
if (!err && lod_pool_need_qmt0(obd->obd_name)) {
obd = obd_find_qmt0(obd->obd_name);
if (obd)
obd_pool_del(obd, poolname);
}
return err;
}
static int lod_sub_print_llog(const struct lu_env *env, struct dt_device *dt,
struct llog_print_data *lprd)
{
struct llog_ctxt *ctxt;
size_t len = 0;
int rc = 0;
ENTRY;
ctxt = llog_get_context(dt->dd_lu_dev.ld_obd,
LLOG_UPDATELOG_ORIG_CTXT);
if (!ctxt)
RETURN(0);
if (!ctxt->loc_handle)
GOTO(ctxt_put, rc = -EINVAL);
len = snprintf(lprd->lprd_out, lprd->lprd_left,
"%s [catalog]: "DFID"\n",
ctxt->loc_obd->obd_name,
PLOGID(&ctxt->loc_handle->lgh_id));
if (len >= lprd->lprd_left) {
lprd->lprd_out[lprd->lprd_left - 1] = '\0';
GOTO(ctxt_put, rc = -E2BIG);
}
lprd->lprd_out += len;
lprd->lprd_left -= len;
rc = llog_process_or_fork(env, ctxt->loc_handle, llog_print_cb,
lprd, NULL, false);
/* multiple iterations are not supported -> stop llog_print */
if (rc == -EOVERFLOW)
rc = -E2BIG;
GOTO(ctxt_put, rc);
ctxt_put:
llog_ctxt_put(ctxt);
return rc;
}
/* print update catalog and update logs FID of all sub devices */
static int lod_llog_print(const struct lu_env *env, struct lod_device *lod,
void *data)
{
struct lod_tgt_desc *mdt;
struct obd_ioctl_data *ioc_data = data;
struct llog_print_data lprd = {
.lprd_raw = false,
};
size_t bufs;
int rc = 0;
ENTRY;
LASSERT(ioc_data);
if (ioc_data->ioc_inllen2) {
rc = kstrtol(ioc_data->ioc_inlbuf2, 0, &lprd.lprd_from);
if (rc)
RETURN(rc);
/* multiple iterations are not supported -> stop llog_print */
if (lprd.lprd_from > 1)
RETURN(-E2BIG);
}
bufs = ioc_data->ioc_inllen4 +
ALIGN(ioc_data->ioc_inllen1, 8) +
ALIGN(ioc_data->ioc_inllen2, 8) +
ALIGN(ioc_data->ioc_inllen3, 8);
ioc_data->ioc_inllen1 = 0;
ioc_data->ioc_inllen2 = 0;
ioc_data->ioc_inllen3 = 0;
ioc_data->ioc_inllen4 = 0;
lprd.lprd_out = ioc_data->ioc_bulk;
lprd.lprd_left = bufs;
rc = lod_sub_print_llog(env, lod->lod_child, &lprd);
if (rc) {
CERROR("%s: llog_print failed: rc = %d\n",
lod2obd(lod)->obd_name, rc);
GOTO(out, rc);
}
lod_getref(&lod->lod_mdt_descs);
lod_foreach_mdt(lod, mdt) {
rc = lod_sub_print_llog(env, mdt->ltd_tgt, &lprd);
if (rc) {
CERROR("%s: llog_print of MDT %u failed: rc = %d\n",
lod2obd(lod)->obd_name, mdt->ltd_index, rc);
break;
}
}
lod_putref(lod, &lod->lod_mdt_descs);
out:
ioc_data->ioc_count = bufs - lprd.lprd_left;
ioc_data->ioc_u32_2 = 1;
RETURN((rc == LLOG_PROC_BREAK) ? 0 : rc);
}
/* cancel update catalog from update catlist */
static int lod_llog_cancel(const struct lu_env *env, struct lod_device *lod)
{
struct lod_tgt_desc *tgt;
int index;
int rc;
int rc2;
rc = lodname2mdt_index(lod2obd(lod)->obd_name, (__u32 *)&index);
if (rc < 0)
return rc;
rc = lod_sub_cancel_llog(env, lod, lod->lod_child, index);
lod_getref(&lod->lod_mdt_descs);
lod_foreach_mdt(lod, tgt) {
LASSERT(tgt && tgt->ltd_tgt);
rc2 = lod_sub_cancel_llog(env, lod, tgt->ltd_tgt,
tgt->ltd_index);
if (rc2 && !rc)
rc = rc2;
}
lod_putref(lod, &lod->lod_mdt_descs);
return rc;
}
static int lod_iocontrol(unsigned int cmd, struct obd_export *exp, int len,
void *karg, void __user *uarg)
{
struct obd_device *obd = exp->exp_obd;
struct lod_device *lod = lu2lod_dev(obd->obd_lu_dev);
struct obd_ioctl_data *data;
struct lu_env env;
int rc;
ENTRY;
CDEBUG(D_IOCTL, "%s: cmd=%x len=%u karg=%pK uarg=%pK\n",
obd->obd_name, cmd, len, karg, uarg);
if (unlikely(karg == NULL))
RETURN(OBD_IOC_ERROR(obd->obd_name, cmd, "karg=NULL", -EINVAL));
data = karg;
rc = lu_env_init(&env, LCT_LOCAL | LCT_MD_THREAD);
if (rc) {
CERROR("%s: can't initialize env: rc = %d\n",
lod2obd(lod)->obd_name, rc);
RETURN(rc);
}
switch (cmd) {
case OBD_IOC_LLOG_PRINT: {
char *logname;
if (!data->ioc_inllen1) {
rc = -EINVAL;
break;
}
logname = data->ioc_inlbuf1;
if (strcmp(logname, lod_update_log_name) != 0) {
rc = -EINVAL;
CERROR("%s: llog iocontrol support %s only: rc = %d\n",
lod2obd(lod)->obd_name, lod_update_log_name, rc);
break;
}
rc = lod_llog_print(&env, lod, data);
break;
}
case OBD_IOC_LLOG_CANCEL:
rc = lod_llog_cancel(&env, lod);
break;
default:
rc = OBD_IOC_ERROR(obd->obd_name, cmd, "unrecognized", -ENOTTY);
break;
}
lu_env_fini(&env);
RETURN(rc);
}
static const struct obd_ops lod_obd_device_ops = {
.o_owner = THIS_MODULE,
.o_connect = lod_obd_connect,
.o_disconnect = lod_obd_disconnect,
.o_get_info = lod_obd_get_info,
.o_set_info_async = lod_obd_set_info_async,
.o_pool_new = lod_pool_new_q,
.o_pool_rem = lod_pool_remove_q,
.o_pool_add = lod_pool_add_q,
.o_pool_del = lod_pool_del_q,
.o_iocontrol = lod_iocontrol,
};
static int __init lod_init(void)
{
struct obd_type *sym;
int rc;
rc = libcfs_setup();
if (rc)
return rc;
rc = lu_kmem_init(lod_caches);
if (rc)
return rc;
rc = class_register_type(&lod_obd_device_ops, NULL, true,
LUSTRE_LOD_NAME, &lod_device_type);
if (rc) {
lu_kmem_fini(lod_caches);
return rc;
}
/* create "lov" entry for compatibility purposes */
sym = class_add_symlinks(LUSTRE_LOV_NAME, true);
if (IS_ERR(sym)) {
rc = PTR_ERR(sym);
/* does real "lov" already exist ? */
if (rc == -EEXIST)
rc = 0;
}
return rc;
}
static void __exit lod_exit(void)
{
struct obd_type *sym = class_search_type(LUSTRE_LOV_NAME);
/* if this was never fully initialized by the lov layer
* then we are responsible for freeing this obd_type
*/
if (sym) {
/* final put if we manage this obd type */
if (sym->typ_sym_filter)
kobject_put(&sym->typ_kobj);
/* put reference taken by class_search_type */
kobject_put(&sym->typ_kobj);
}
class_unregister_type(LUSTRE_LOD_NAME);
lu_kmem_fini(lod_caches);
}
MODULE_AUTHOR("OpenSFS, Inc. <http://www.lustre.org/>");
MODULE_DESCRIPTION("Lustre Logical Object Device ("LUSTRE_LOD_NAME")");
MODULE_VERSION(LUSTRE_VERSION_STRING);
MODULE_LICENSE("GPL");
late_initcall_sync(lod_init);
module_exit(lod_exit);