Viewing: llog_cat_server.c
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. 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/
*
* OST<->MDS recovery logging infrastructure.
*
* Invariants in implementation:
* - we do not share logs among different OST<->MDS connections, so that
* if an OST or MDS fails it need only look at log(s) relevant to itself
*
* Author: Andreas Dilger <adilger@clusterfs.com>
* Author: Alexey Zhuravlev <alexey.zhuravlev@intel.com>
* Author: Mikhail Pershin <mike.pershin@intel.com>
*/
#define DEBUG_SUBSYSTEM S_LOG
#include <obd_class.h>
#include "llog_internal.h"
/* Create a new log handle and add it to the open list.
* This log handle will be closed when all of the records in it are removed.
*
* Assumes caller has already pushed us into the kernel context and is locking.
*/
static int llog_cat_new_log(const struct lu_env *env,
struct llog_handle *cathandle,
struct llog_handle *loghandle,
struct thandle *th)
{
struct llog_thread_info *lgi = llog_info(env);
struct llog_logid_rec *rec = &lgi->lgi_logid;
struct thandle *handle = NULL;
struct dt_device *dt = NULL;
struct llog_log_hdr *llh = cathandle->lgh_hdr;
int rc, index;
ENTRY;
index = (cathandle->lgh_last_idx + 1) % (llog_max_idx(cathandle) + 1);
/* check that new llog index will not overlap with the first one.
* - llh_cat_idx is the index just before the first/oldest still in-use
* index in catalog
* - lgh_last_idx is the last/newest used index in catalog
*
* When catalog is not wrapped yet then lgh_last_idx is always larger
* than llh_cat_idx. After the wrap around lgh_last_idx re-starts
* from 0 and llh_cat_idx becomes the upper limit for it
*
* Check if catalog has already wrapped around or not by comparing
* last_idx and cat_idx
*/
if ((index == llh->llh_cat_idx + 1 && llh->llh_count > 1) ||
(index == 0 && llh->llh_cat_idx == 0)) {
if (!cathandle->lgh_name) {
CWARN("%s: there are no more free slots in catalog "DFID"\n",
loghandle2name(loghandle),
PLOGID(&cathandle->lgh_id));
} else {
CWARN("%s: there are no more free slots in catalog %s\n",
loghandle2name(loghandle), cathandle->lgh_name);
}
RETURN(-ENOSPC);
}
if (CFS_FAIL_CHECK(OBD_FAIL_MDS_LLOG_CREATE_FAILED))
RETURN(-ENOSPC);
if (loghandle->lgh_hdr) {
/* If llog object is remote and creation is failed, lgh_hdr
* might be left over here, free it first
*/
LASSERT(!llog_exist(loghandle));
OBD_FREE_LARGE(loghandle->lgh_hdr, loghandle->lgh_hdr_size);
loghandle->lgh_hdr = NULL;
}
if (!th) {
dt = lu2dt_dev(cathandle->lgh_obj->do_lu.lo_dev);
if (IS_ERR(dt))
RETURN(PTR_ERR(dt));
handle = dt_trans_create(env, dt);
if (IS_ERR(handle))
RETURN(PTR_ERR(handle));
/* Create update llog object synchronously, which
* happens during inialization process see
* lod_sub_prep_llog(), to make sure the update
* llog object is created before corss-MDT writing
* updates into the llog object
*/
if (cathandle->lgh_ctxt->loc_flags & LLOG_CTXT_FLAG_NORMAL_FID)
handle->th_sync = 1;
handle->th_wait_submit = 1;
rc = llog_declare_create(env, loghandle, handle);
if (rc != 0)
GOTO(out, rc);
rec->lid_hdr.lrh_len = sizeof(*rec);
rec->lid_hdr.lrh_type = LLOG_LOGID_MAGIC;
rec->lid_id = loghandle->lgh_id;
rc = llog_declare_write_rec(env, cathandle, &rec->lid_hdr, -1,
handle);
if (rc != 0)
GOTO(out, rc);
dt_declare_attr_set(env, cathandle->lgh_obj, NULL, handle);
rc = dt_trans_start_local(env, dt, handle);
if (rc != 0)
GOTO(out, rc);
th = handle;
}
rc = llog_create(env, loghandle, th);
/* if llog is already created, no need to initialize it */
if (rc == -EEXIST) {
GOTO(out, rc);
} else if (rc != 0) {
CERROR("%s: can't create new plain llog in catalog: rc = %d\n",
loghandle2name(loghandle), rc);
GOTO(out, rc);
}
rc = llog_init_handle(env, loghandle, (cathandle->lgh_hdr->llh_flags &
LLOG_F_EXT_MASK) |
LLOG_F_IS_PLAIN | LLOG_F_ZAP_WHEN_EMPTY,
&cathandle->lgh_hdr->llh_tgtuuid);
if (rc < 0)
GOTO(out, rc);
/* build the record for this log in the catalog */
rec->lid_hdr.lrh_len = sizeof(*rec);
rec->lid_hdr.lrh_type = LLOG_LOGID_MAGIC;
rec->lid_id = loghandle->lgh_id;
/* append the new record into catalog. The new index will be
* assigned to the record and updated in rec header
*/
rc = llog_write_rec(env, cathandle, &rec->lid_hdr,
&loghandle->u.phd.phd_cookie, LLOG_NEXT_IDX, th);
if (rc < 0)
GOTO(out_destroy, rc);
/* update for catalog which doesn't happen very often */
lgi->lgi_attr.la_valid = LA_MTIME;
lgi->lgi_attr.la_mtime = ktime_get_real_seconds();
dt_attr_set(env, cathandle->lgh_obj, &lgi->lgi_attr, th);
CDEBUG(D_OTHER, "new plain log "DFID".%u of catalog "DFID"\n",
PLOGID(&loghandle->lgh_id), rec->lid_hdr.lrh_index,
PLOGID(&cathandle->lgh_id));
if (loghandle->lgh_hdr)
loghandle->lgh_hdr->llh_cat_idx = rec->lid_hdr.lrh_index;
/* limit max size of plain llog so that space can be
* released sooner, especially on small filesystems
* 2MB for the cases when free space hasn't been learned yet
*/
loghandle->lgh_max_size = 2 << 20;
dt = lu2dt_dev(cathandle->lgh_obj->do_lu.lo_dev);
if (IS_ERR(dt))
GOTO(out_destroy, rc = PTR_ERR(dt));
rc = dt_statfs(env, dt, &lgi->lgi_statfs);
if (rc == 0 && lgi->lgi_statfs.os_bfree > 0) {
u64 freespace = (lgi->lgi_statfs.os_bfree *
lgi->lgi_statfs.os_bsize) >> 6;
if (freespace < loghandle->lgh_max_size)
loghandle->lgh_max_size = freespace;
/* shouldn't be > 128MB in any case?
* it's 256K records of 512 bytes each
*/
if (freespace > (128 << 20))
loghandle->lgh_max_size = 128 << 20;
}
if (unlikely(CFS_FAIL_PRECHECK(OBD_FAIL_PLAIN_RECORDS) ||
CFS_FAIL_PRECHECK(OBD_FAIL_CATALOG_FULL_CHECK))) {
/* limit the numer of plain records for test */
loghandle->lgh_max_size = loghandle->lgh_hdr_size +
cfs_fail_val * 64;
}
rc = 0;
out:
if (handle) {
handle->th_result = (rc >= 0 || rc == -EEXIST) ? 0 : rc;
if (!IS_ERR_OR_NULL(dt))
dt_trans_stop(env, dt, handle);
}
RETURN(rc);
out_destroy:
/* to signal llog_cat_close() it shouldn't try to destroy the llog,
* we want to destroy it in this transaction, otherwise the object
* becomes an orphan
*/
loghandle->lgh_hdr->llh_flags &= ~LLOG_F_ZAP_WHEN_EMPTY;
/* this is to mimic full log, so another llog_cat_current_log()
* can skip it and ask for another one
*/
loghandle->lgh_last_idx = llog_max_idx(loghandle) + 1;
llog_trans_destroy(env, loghandle, th);
if (handle)
dt_trans_stop(env, dt, handle);
RETURN(rc);
}
static int llog_cat_refresh(const struct lu_env *env,
struct llog_handle *cathandle)
{
struct llog_handle *loghandle;
int rc;
LASSERT(rwsem_is_locked(&cathandle->lgh_lock));
list_for_each_entry(loghandle, &cathandle->u.chd.chd_head,
u.phd.phd_entry) {
if (!llog_exist(loghandle))
continue;
down_write(&loghandle->lgh_lock);
rc = llog_read_header(env, loghandle, NULL);
up_write(&loghandle->lgh_lock);
if (rc)
goto out;
}
rc = llog_read_header(env, cathandle, NULL);
out:
return rc;
}
static inline int llog_cat_declare_create(const struct lu_env *env,
struct llog_handle *cathandle,
struct llog_handle *loghandle,
struct thandle *th)
{
struct llog_thread_info *lgi = llog_info(env);
struct llog_logid_rec *lirec = &lgi->lgi_logid;
int rc;
if (dt_object_remote(cathandle->lgh_obj)) {
down_write(&loghandle->lgh_lock);
if (!llog_exist(loghandle))
rc = llog_cat_new_log(env, cathandle, loghandle, NULL);
else
rc = 0;
up_write(&loghandle->lgh_lock);
} else {
rc = llog_declare_create(env, loghandle, th);
if (rc)
return rc;
lirec->lid_hdr.lrh_len = sizeof(*lirec);
rc = llog_declare_write_rec(env, cathandle, &lirec->lid_hdr, -1,
th);
if (!rc)
dt_declare_attr_set(env, cathandle->lgh_obj, NULL, th);
}
return rc;
}
/*
* prepare current/next log for catalog.
*
* if \a *ploghandle is NULL, open it, and declare create, NB, if \a
* *ploghandle is remote, create it synchronously here, see comments
* below.
*
* \a cathandle->lgh_lock is down_read-ed, it gets down_write-ed if \a
* *ploghandle has to be opened.
*/
static int llog_cat_prep_log(const struct lu_env *env,
struct llog_handle *cathandle,
struct llog_handle **ploghandle,
struct thandle *th)
{
struct llog_handle *loghandle;
int rc;
rc = 0;
loghandle = *ploghandle;
if (!IS_ERR_OR_NULL(loghandle)) {
loghandle = llog_handle_get(loghandle);
if (loghandle && loghandle->lgh_destroyed) {
llog_handle_put(env, loghandle);
} else if (loghandle) {
if (!llog_exist(loghandle))
rc = llog_cat_declare_create(env, cathandle,
loghandle, th);
llog_handle_put(env, loghandle);
return rc;
}
}
down_write(&cathandle->lgh_lock);
if (!IS_ERR_OR_NULL(*ploghandle)) {
loghandle = llog_handle_get(*ploghandle);
if (loghandle && loghandle->lgh_destroyed) {
llog_handle_put(env, loghandle);
} else if (loghandle) {
up_write(&cathandle->lgh_lock);
if (!llog_exist(loghandle))
rc = llog_cat_declare_create(env, cathandle,
loghandle, th);
llog_handle_put(env, loghandle);
return rc;
}
}
/* Slow path with open/create declare, only one thread do all stuff
* and share loghandle at the end
*/
rc = llog_open(env, cathandle->lgh_ctxt, &loghandle, NULL, NULL,
LLOG_OPEN_NEW);
if (rc) {
up_write(&cathandle->lgh_lock);
CDEBUG(D_OTHER, "%s: failed to open log, catalog "DFID" %d\n",
loghandle2name(cathandle), PLOGID(&cathandle->lgh_id),
rc);
return rc;
}
rc = llog_cat_declare_create(env, cathandle, loghandle, th);
if (!rc) {
list_add(&loghandle->u.phd.phd_entry,
&cathandle->u.chd.chd_head);
*ploghandle = loghandle;
}
up_write(&cathandle->lgh_lock);
CDEBUG(D_OTHER, "%s: open log "DFID" for catalog "DFID" rc=%d\n",
loghandle2name(cathandle), PLOGID(&loghandle->lgh_id),
PLOGID(&cathandle->lgh_id), rc);
if (rc)
llog_close(env, loghandle);
return rc;
}
/** Return the currently active log handle. If the current log handle doesn't
* have enough space left for the current record, start a new one.
*
* If reclen is 0, we only want to know what the currently active log is,
* otherwise we get a lock on this log so nobody can steal our space.
*
* Assumes caller has already pushed us into the kernel context and is locking.
*
* NOTE: loghandle is write-locked and referenced upon successful return
*/
static struct llog_handle *llog_cat_current_log(const struct lu_env *env,
struct llog_handle *cathandle,
struct thandle *th)
{
struct llog_handle *loghandle;
struct llog_logid lid = {
.lgl_oi.oi.oi_id = 0,
.lgl_oi.oi.oi_seq = 0,
.lgl_ogen = 0
};
ENTRY;
if (CFS_FAIL_CHECK(OBD_FAIL_MDS_LLOG_CREATE_FAILED2)) {
loghandle = cathandle->u.chd.chd_current_log;
GOTO(next, loghandle);
}
retry:
loghandle = cathandle->u.chd.chd_current_log;
if (!IS_ERR_OR_NULL(loghandle) && llog_handle_get(loghandle)) {
down_write_nested(&loghandle->lgh_lock, LLOGH_LOG);
if (!loghandle->lgh_destroyed && !llog_is_full(loghandle))
RETURN(loghandle);
up_write(&loghandle->lgh_lock);
lid = loghandle->lgh_id;
llog_handle_put(env, loghandle);
}
/* time to use next log */
next:
/* first, we have to make sure the state hasn't changed */
down_write_nested(&cathandle->lgh_lock, LLOGH_CAT);
if (unlikely(loghandle == cathandle->u.chd.chd_current_log)) {
/* Sigh, the chd_next_log and chd_current_log is initialized
* in declare phase, and we do not serialize the catlog
* accessing, so it might be possible the llog creation
* thread (see llog_cat_declare_add_rec()) did not create
* llog successfully, then the following thread might
* meet this situation.
*/
if (IS_ERR_OR_NULL(cathandle->u.chd.chd_next_log)) {
CERROR("%s: next log does not exist, catalog "DFID" rc=%d\n",
loghandle2name(cathandle),
PLOGID(&cathandle->lgh_id), -EIO);
loghandle = ERR_PTR(-EIO);
if (!cathandle->u.chd.chd_next_log) {
/* Store the error in chd_next_log, so
* the following process can get correct
* failure value
*/
cathandle->u.chd.chd_next_log = loghandle;
}
GOTO(out_unlock, loghandle);
}
CDEBUG(D_OTHER,
"%s: use next log "DFID"->"DFID" catalog "DFID"\n",
loghandle2name(cathandle), PLOGID(&lid),
PLOGID(&cathandle->u.chd.chd_next_log->lgh_id),
PLOGID(&cathandle->lgh_id));
loghandle = cathandle->u.chd.chd_next_log;
cathandle->u.chd.chd_current_log = loghandle;
cathandle->u.chd.chd_next_log = NULL;
}
up_write(&cathandle->lgh_lock);
GOTO(retry, loghandle);
out_unlock:
up_write(&cathandle->lgh_lock);
RETURN(loghandle);
}
/* Add a single record to the recovery log(s) using a catalog
* Returns as llog_write_record
*
* Assumes caller has already pushed us into the kernel context.
*/
int llog_cat_add_rec(const struct lu_env *env, struct llog_handle *cathandle,
struct llog_rec_hdr *rec, struct llog_cookie *reccookie,
struct thandle *th)
{
struct llog_handle *loghandle;
struct llog_thread_info *lgi = llog_info(env);
int rc, retried = 0;
ENTRY;
LASSERT(rec->lrh_len <= cathandle->lgh_ctxt->loc_chunk_size);
retry:
loghandle = llog_cat_current_log(env, cathandle, th);
if (IS_ERR(loghandle))
RETURN(PTR_ERR(loghandle));
LASSERT(loghandle);
LASSERT(!loghandle->lgh_destroyed);
/* loghandle is already locked by llog_cat_current_log() for us */
if (!llog_exist(loghandle)) {
rc = llog_cat_new_log(env, cathandle, loghandle, th);
if (rc < 0) {
up_write(&loghandle->lgh_lock);
llog_handle_put(env, loghandle);
if (rc == -EEXIST && retried++ == 0)
goto retry;
/* When ENOSPC happened no need to drop loghandle
* a new one would be allocated anyway for next llog_add
* so better to stay with the old.
*/
if (rc != -ENOSPC) {
/* nobody should be trying to use this llog */
down_write(&cathandle->lgh_lock);
if (cathandle->u.chd.chd_current_log ==
loghandle)
cathandle->u.chd.chd_current_log = NULL;
list_del_init(&loghandle->u.phd.phd_entry);
up_write(&cathandle->lgh_lock);
llog_close(env, loghandle);
}
CERROR("%s: initialization error: rc = %d\n",
loghandle2name(cathandle), rc);
RETURN(rc);
}
}
/* now let's try to add the record */
rc = llog_write_rec(env, loghandle, rec, reccookie, LLOG_NEXT_IDX, th);
if (rc < 0) {
CDEBUG_LIMIT(rc == -ENOSPC ? D_HA : D_ERROR,
"llog_write_rec %d: lh=%p\n", rc, loghandle);
/* -ENOSPC is returned if no empty records left
* and when it's lack of space on the stogage.
* there is no point to try again if it's the second
* case. many callers (like llog test) expect ENOSPC,
* so we preserve this error code, but look for the
* actual cause here
*/
if (rc == -ENOSPC && llog_is_full(loghandle))
rc = -ENOBUFS;
} else {
unsigned long timestamp = ktime_get_real_seconds();
if (timestamp != loghandle->lgh_timestamp) {
loghandle->lgh_timestamp = timestamp;
lgi->lgi_attr.la_valid = LA_MTIME;
lgi->lgi_attr.la_mtime = timestamp;
dt_attr_set(env, loghandle->lgh_obj, &lgi->lgi_attr,
th);
}
}
/* llog_write_rec could unlock a semaphore */
if (!(loghandle->lgh_hdr->llh_flags & LLOG_F_UNLCK_SEM))
up_write(&loghandle->lgh_lock);
llog_handle_put(env, loghandle);
if (rc == -ENOBUFS) {
if (retried++ == 0)
GOTO(retry, rc);
CERROR("%s: error on 2nd llog: rc = %d\n",
loghandle2name(cathandle), rc);
}
RETURN(rc);
}
EXPORT_SYMBOL(llog_cat_add_rec);
int llog_cat_declare_add_rec(const struct lu_env *env,
struct llog_handle *cathandle,
struct llog_rec_hdr *rec, struct thandle *th)
{
struct llog_handle *loghandle = NULL;
int retries = 5;
int rc;
ENTRY;
start:
CDEBUG(D_INFO, "Declare adding to "DOSTID" flags %x count %d\n",
POSTID(&cathandle->lgh_id.lgl_oi),
cathandle->lgh_hdr->llh_flags, cathandle->lgh_hdr->llh_count);
rc = llog_cat_prep_log(env, cathandle,
&cathandle->u.chd.chd_current_log, th);
if (rc)
GOTO(estale, rc);
loghandle = cathandle->u.chd.chd_current_log;
if (IS_ERR_OR_NULL(loghandle)) { /* low chance race, repeat */
GOTO(estale, rc = -ESTALE);
} else {
loghandle = llog_handle_get(loghandle);
if (!loghandle)
GOTO(estale, rc = -ESTALE);
}
/* For local llog this would always reserves credits for creation */
rc = llog_cat_prep_log(env, cathandle, &cathandle->u.chd.chd_next_log,
th);
if (!rc) {
rc = llog_declare_write_rec(env, loghandle, rec, -1, th);
if (!rc)
dt_declare_attr_set(env, loghandle->lgh_obj, NULL, th);
}
llog_handle_put(env, loghandle);
estale:
if (rc == -ESTALE) {
if (dt_object_remote(cathandle->lgh_obj)) {
down_write(&cathandle->lgh_lock);
rc = llog_cat_refresh(env, cathandle);
up_write(&cathandle->lgh_lock);
if (rc)
RETURN(rc);
}
retries--;
if (retries > 0)
goto start;
}
if (rc)
CWARN("%s: declaration failed, catalog "DFID": rc = %d\n",
loghandle2name(cathandle),
PLOGID(&cathandle->lgh_id), rc);
RETURN(rc);
}
EXPORT_SYMBOL(llog_cat_declare_add_rec);
int llog_cat_add(const struct lu_env *env, struct llog_handle *cathandle,
struct llog_rec_hdr *rec, struct llog_cookie *reccookie)
{
struct llog_ctxt *ctxt;
struct dt_device *dt;
struct thandle *th = NULL;
int rc;
ctxt = cathandle->lgh_ctxt;
LASSERT(ctxt);
LASSERT(ctxt->loc_exp);
LASSERT(cathandle->lgh_obj);
dt = lu2dt_dev(cathandle->lgh_obj->do_lu.lo_dev);
if (IS_ERR(dt))
RETURN(PTR_ERR(dt));
th = dt_trans_create(env, dt);
if (IS_ERR(th))
RETURN(PTR_ERR(th));
rc = llog_cat_declare_add_rec(env, cathandle, rec, th);
if (rc)
GOTO(out_trans, rc);
rc = dt_trans_start_local(env, dt, th);
if (rc)
GOTO(out_trans, rc);
rc = llog_cat_add_rec(env, cathandle, rec, reccookie, th);
out_trans:
dt_trans_stop(env, dt, th);
RETURN(rc);
}
EXPORT_SYMBOL(llog_cat_add);
int llog_cat_cancel_arr_rec(const struct lu_env *env,
struct llog_handle *cathandle,
struct llog_logid *lgl, int count, int *index)
{
struct llog_handle *loghandle;
int rc;
ENTRY;
rc = llog_cat_id2handle(env, cathandle, &loghandle, lgl);
if (rc) {
CDEBUG(D_HA, "%s: can't find llog handle for "DFID": rc = %d\n",
loghandle2name(cathandle), PLOGID(lgl), rc);
RETURN(rc);
}
if ((cathandle->lgh_ctxt->loc_flags &
LLOG_CTXT_FLAG_NORMAL_FID) && !llog_exist(loghandle)) {
/* For update log, some of loghandles of cathandle
* might not exist because remote llog creation might
* be failed, so let's skip the record cancellation
* for these non-exist llogs.
*/
rc = -ENOENT;
CDEBUG(D_HA, "%s: llog "DFID" does not exist: rc = %d\n",
loghandle2name(cathandle), PLOGID(lgl), rc);
llog_handle_put(env, loghandle);
RETURN(rc);
}
rc = llog_cancel_arr_rec(env, loghandle, count, index);
if (rc == LLOG_DEL_PLAIN) { /* log has been destroyed */
int cat_index;
cat_index = loghandle->u.phd.phd_cookie.lgc_index;
rc = llog_cat_cleanup(env, cathandle, loghandle, cat_index);
if (rc)
CDEBUG(D_HA,
"%s: fail to cancel catalog record: rc = %d\n",
loghandle2name(cathandle), rc);
rc = 0;
}
llog_handle_put(env, loghandle);
if (rc && rc != -ENOENT && rc != -ESTALE && rc != -EIO)
CWARN("%s: fail to cancel %d records in "DFID": rc = %d\n",
loghandle2name(cathandle), count, PLOGID(lgl), rc);
RETURN(rc);
}
EXPORT_SYMBOL(llog_cat_cancel_arr_rec);
/* For each cookie in the cookie array, we clear the log in-use bit and either:
* - the log is empty, so mark it free in the catalog header and delete it
* - the log is not empty, just write out the log header
*
* The cookies may be in different log files, so we need to get new logs
* each time.
*
* Assumes caller has already pushed us into the kernel context.
*/
int llog_cat_cancel_records(const struct lu_env *env,
struct llog_handle *cathandle, int count,
struct llog_cookie *cookies)
{
int i, rc = 0;
ENTRY;
for (i = 0; i < count; i++, cookies++) {
int lrc;
lrc = llog_cat_cancel_arr_rec(env, cathandle, &cookies->lgc_lgl,
1, &cookies->lgc_index);
if (lrc && !rc)
rc = lrc;
}
RETURN(rc);
}
EXPORT_SYMBOL(llog_cat_cancel_records);
/* Get size of llog */
static u64 llog_size(const struct lu_env *env, struct llog_handle *llh)
{
struct lu_attr la;
int rc;
rc = llh->lgh_obj->do_ops->do_attr_get(env, llh->lgh_obj, &la);
if (rc) {
CERROR("%s: attr_get failed for "DFID": rc = %d\n",
loghandle2name(llh), PLOGID(&llh->lgh_id), rc);
return 0;
}
return la.la_size;
}
static int llog_cat_size_cb(const struct lu_env *env,
struct llog_handle *cat_llh,
struct llog_rec_hdr *rec, void *data)
{
struct llog_process_data *d = data;
struct llog_handle *llh = NULL;
u64 *cum_size = d->lpd_data;
u64 size;
int rc;
ENTRY;
rc = llog_cat_process_common(env, cat_llh, rec, &llh);
if (rc == LLOG_DEL_PLAIN) {
/* empty log was deleted, don't count it */
rc = llog_cat_cleanup(env, cat_llh, llh,
llh->u.phd.phd_cookie.lgc_index);
} else if (rc == LLOG_DEL_RECORD) {
/* clear wrong catalog entry */
rc = llog_cat_cleanup(env, cat_llh, NULL, rec->lrh_index);
} else {
size = llog_size(env, llh);
*cum_size += size;
CDEBUG(D_INFO, "Add llog entry "DFID" size=%llu, tot=%llu\n",
PLOGID(&llh->lgh_id), size, *cum_size);
}
if (llh)
llog_handle_put(env, llh);
RETURN(0);
}
u64 llog_cat_size(const struct lu_env *env, struct llog_handle *cat_llh)
{
u64 size = llog_size(env, cat_llh);
llog_cat_process_or_fork(env, cat_llh, llog_cat_size_cb,
NULL, &size, 0, 0, false);
return size;
}
EXPORT_SYMBOL(llog_cat_size);
/* currently returns the number of "free" entries in catalog,
* ie the available entries for a new plain LLOG file creation,
* even if catalog has wrapped
*/
u32 llog_cat_free_space(struct llog_handle *cat_llh)
{
/* simulate almost full Catalog */
if (CFS_FAIL_CHECK(OBD_FAIL_CAT_FREE_RECORDS))
return cfs_fail_val;
if (cat_llh->lgh_hdr->llh_count == 1)
return llog_max_idx(cat_llh);
if (cat_llh->lgh_last_idx > cat_llh->lgh_hdr->llh_cat_idx)
return llog_max_idx(cat_llh) +
cat_llh->lgh_hdr->llh_cat_idx - cat_llh->lgh_last_idx;
/* catalog is presently wrapped */
return cat_llh->lgh_hdr->llh_cat_idx - cat_llh->lgh_last_idx;
}
EXPORT_SYMBOL(llog_cat_free_space);
static int llog_cat_reverse_process_cb(const struct lu_env *env,
struct llog_handle *cat_llh,
struct llog_rec_hdr *rec, void *data)
{
struct llog_process_data *d = data;
struct llog_handle *llh = NULL;
int rc;
ENTRY;
rc = llog_cat_process_common(env, cat_llh, rec, &llh);
if (rc)
GOTO(out, rc);
rc = llog_reverse_process(env, llh, d->lpd_cb, d->lpd_data, NULL);
out:
/* The empty plain log was destroyed while processing */
if (rc == LLOG_DEL_PLAIN) {
rc = llog_cat_cleanup(env, cat_llh, llh,
llh->u.phd.phd_cookie.lgc_index);
} else if (rc == LLOG_DEL_RECORD) {
/* clear wrong catalog entry */
rc = llog_cat_cleanup(env, cat_llh, NULL, rec->lrh_index);
} else if (rc == LLOG_SKIP_PLAIN) {
/* processing callback ask to skip the llog -> continue */
rc = 0;
}
if (llh)
llog_handle_put(env, llh);
RETURN(rc);
}
int llog_cat_reverse_process(const struct lu_env *env,
struct llog_handle *cat_llh,
llog_cb_t cb, void *data)
{
struct llog_process_data d;
struct llog_process_cat_data cd;
struct llog_log_hdr *llh = cat_llh->lgh_hdr;
int rc;
ENTRY;
LASSERT(llh->llh_flags & LLOG_F_IS_CAT);
cd.lpcd_read_mode = LLOG_READ_MODE_NORMAL;
d.lpd_data = data;
d.lpd_cb = cb;
if (llh->llh_cat_idx >= cat_llh->lgh_last_idx &&
llh->llh_count > 1) {
CWARN("%s: catalog "DFID" crosses index zero\n",
loghandle2name(cat_llh),
PLOGID(&cat_llh->lgh_id));
cd.lpcd_first_idx = 0;
cd.lpcd_last_idx = cat_llh->lgh_last_idx;
rc = llog_reverse_process(env, cat_llh,
llog_cat_reverse_process_cb,
&d, &cd);
if (rc != 0)
RETURN(rc);
cd.lpcd_first_idx = le32_to_cpu(llh->llh_cat_idx);
cd.lpcd_last_idx = 0;
rc = llog_reverse_process(env, cat_llh,
llog_cat_reverse_process_cb,
&d, &cd);
} else {
rc = llog_reverse_process(env, cat_llh,
llog_cat_reverse_process_cb,
&d, NULL);
}
RETURN(rc);
}
EXPORT_SYMBOL(llog_cat_reverse_process);
/* Cleanup deleted plain llog traces from catalog */
int llog_cat_cleanup(const struct lu_env *env, struct llog_handle *cathandle,
struct llog_handle *loghandle, int index)
{
int rc;
LASSERT(index);
if (loghandle) {
/* remove destroyed llog from catalog list and
* chd_current_log variable
*/
down_write(&cathandle->lgh_lock);
if (cathandle->u.chd.chd_current_log == loghandle)
cathandle->u.chd.chd_current_log = NULL;
list_del_init(&loghandle->u.phd.phd_entry);
up_write(&cathandle->lgh_lock);
LASSERT(index == loghandle->u.phd.phd_cookie.lgc_index ||
loghandle->u.phd.phd_cookie.lgc_index == 0);
/* llog was opened and keep in a list, close it now */
llog_close(env, loghandle);
}
/* do not attempt to cleanup on-disk llog if on client side */
if (!cathandle->lgh_obj)
return 0;
/* cancel record and decrease count, then move llh_cat_idx
* llog_cat_set_first_idx() is called inside llog_cancel_arr_rec()
*/
/* remove plain llog entry from catalog by index */
rc = llog_cancel_rec(env, cathandle, index);
if (rc < 0)
return rc;
if (loghandle)
CDEBUG(D_HA,
"cancel plain log "DFID" at index %u of catalog "DFID"\n",
PLOGID(&loghandle->lgh_id), index,
PLOGID(&cathandle->lgh_id));
return rc;
}
/* retain log in catalog, and zap it if log is empty */
int llog_cat_retain_cb(const struct lu_env *env, struct llog_handle *cat,
struct llog_rec_hdr *rec, void *data)
{
struct llog_handle *log = NULL;
int rc;
rc = llog_cat_process_common(env, cat, rec, &log);
/* The empty plain log was destroyed while processing */
if (rc == LLOG_DEL_PLAIN || rc == LLOG_DEL_RECORD)
/* clear wrong catalog entry */
rc = llog_cat_cleanup(env, cat, log, rec->lrh_index);
else if (!rc)
llog_retain(env, log);
if (log)
llog_handle_put(env, log);
return rc;
}
EXPORT_SYMBOL(llog_cat_retain_cb);
/* Modify a llog record base on llog_logid and record cookie,
* with valid offset.
*/
int llog_cat_modify_rec(const struct lu_env *env, struct llog_handle *cathandle,
struct llog_logid *lid, struct llog_rec_hdr *rec,
struct llog_cookie *cookie)
{
struct llog_handle *llh;
int rc;
ENTRY;
rc = llog_cat_id2handle(env, cathandle, &llh, lid);
if (rc) {
CDEBUG(D_OTHER, "%s: failed to find log file "DFID": rc = %d\n",
loghandle2name(llh), PLOGID(lid), rc);
RETURN(rc);
}
rc = llog_write_cookie(env, llh, rec, cookie, rec->lrh_index);
if (rc < 0) {
CDEBUG(D_OTHER,
"%s: failed to modify record "DFID".%d: rc = %d\n",
loghandle2name(llh), PLOGID(lid), rec->lrh_index, rc);
} else {
rc = 0;
}
llog_handle_put(env, llh);
RETURN(rc);
}
EXPORT_SYMBOL(llog_cat_modify_rec);