| /* |
| * GPL HEADER START |
| * |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 only, |
| * as published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, but |
| * WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * General Public License version 2 for more details (a copy is included |
| * in the LICENSE file that accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License |
| * version 2 along with this program; If not, see |
| * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf |
| * |
| * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
| * CA 95054 USA or visit www.sun.com if you need additional information or |
| * have any questions. |
| * |
| * GPL HEADER END |
| */ |
| /* |
| * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. |
| * Use is subject to license terms. |
| * |
| * Copyright (c) 2012, Intel Corporation. |
| */ |
| /* |
| * This file is part of Lustre, http://www.lustre.org/ |
| * Lustre is a trademark of Sun Microsystems, Inc. |
| * |
| * lustre/obdclass/llog_test.c |
| * |
| * Author: Phil Schwan <phil@clusterfs.com> |
| * Author: Mikhail Pershin <mike.pershin@intel.com> |
| */ |
| |
| #define DEBUG_SUBSYSTEM S_CLASS |
| |
| #include <linux/module.h> |
| #include <linux/init.h> |
| |
| #include <obd_class.h> |
| #include <lustre_fid.h> |
| #include <lustre_log.h> |
| |
| /* This is slightly more than the number of records that can fit into a |
| * single llog file, because the llog_log_header takes up some of the |
| * space in the first block that cannot be used for the bitmap. */ |
| #define LLOG_TEST_RECNUM (LLOG_CHUNK_SIZE * 8) |
| |
| static int llog_test_rand; |
| static struct obd_uuid uuid = { .uuid = "test_uuid" }; |
| static struct llog_logid cat_logid; |
| |
| struct llog_mini_rec { |
| struct llog_rec_hdr lmr_hdr; |
| struct llog_rec_tail lmr_tail; |
| } __attribute__((packed)); |
| |
| static int verify_handle(char *test, struct llog_handle *llh, int num_recs) |
| { |
| int i; |
| int last_idx = 0; |
| int active_recs = 0; |
| |
| for (i = 0; i < LLOG_BITMAP_BYTES * 8; i++) { |
| if (ext2_test_bit(i, llh->lgh_hdr->llh_bitmap)) { |
| last_idx = i; |
| active_recs++; |
| } |
| } |
| |
| if (active_recs != num_recs) { |
| CERROR("%s: expected %d active recs after write, found %d\n", |
| test, num_recs, active_recs); |
| return -ERANGE; |
| } |
| |
| if (llh->lgh_hdr->llh_count != num_recs) { |
| CERROR("%s: handle->count is %d, expected %d after write\n", |
| test, llh->lgh_hdr->llh_count, num_recs); |
| return -ERANGE; |
| } |
| |
| if (llh->lgh_last_idx < last_idx) { |
| CERROR("%s: handle->last_idx is %d, expected %d after write\n", |
| test, llh->lgh_last_idx, last_idx); |
| return -ERANGE; |
| } |
| |
| return 0; |
| } |
| |
| /* Test named-log create/open, close */ |
| static int llog_test_1(const struct lu_env *env, |
| struct obd_device *obd, char *name) |
| { |
| struct llog_handle *llh; |
| struct llog_ctxt *ctxt; |
| int rc; |
| int rc2; |
| |
| CWARN("1a: create a log with name: %s\n", name); |
| ctxt = llog_get_context(obd, LLOG_TEST_ORIG_CTXT); |
| LASSERT(ctxt); |
| |
| rc = llog_open_create(env, ctxt, &llh, NULL, name); |
| if (rc) { |
| CERROR("1a: llog_create with name %s failed: %d\n", name, rc); |
| GOTO(out, rc); |
| } |
| rc = llog_init_handle(env, llh, LLOG_F_IS_PLAIN, &uuid); |
| if (rc) { |
| CERROR("1a: can't init llog handle: %d\n", rc); |
| GOTO(out_close, rc); |
| } |
| |
| rc = verify_handle("1", llh, 1); |
| |
| CWARN("1b: close newly-created log\n"); |
| out_close: |
| rc2 = llog_close(env, llh); |
| if (rc2) { |
| CERROR("1b: close log %s failed: %d\n", name, rc2); |
| if (rc == 0) |
| rc = rc2; |
| } |
| out: |
| llog_ctxt_put(ctxt); |
| return rc; |
| } |
| |
| /* Test named-log reopen; returns opened log on success */ |
| static int llog_test_2(const struct lu_env *env, struct obd_device *obd, |
| char *name, struct llog_handle **llh) |
| { |
| struct llog_ctxt *ctxt; |
| struct llog_handle *loghandle; |
| struct llog_logid logid; |
| int rc; |
| |
| CWARN("2a: re-open a log with name: %s\n", name); |
| ctxt = llog_get_context(obd, LLOG_TEST_ORIG_CTXT); |
| LASSERT(ctxt); |
| |
| rc = llog_open(env, ctxt, llh, NULL, name, LLOG_OPEN_EXISTS); |
| if (rc) { |
| CERROR("2a: re-open log with name %s failed: %d\n", name, rc); |
| GOTO(out_put, rc); |
| } |
| |
| rc = llog_init_handle(env, *llh, LLOG_F_IS_PLAIN, &uuid); |
| if (rc) { |
| CERROR("2a: can't init llog handle: %d\n", rc); |
| GOTO(out_close_llh, rc); |
| } |
| |
| rc = verify_handle("2", *llh, 1); |
| if (rc) |
| GOTO(out_close_llh, rc); |
| |
| /* XXX: there is known issue with tests 2b, MGS is not able to create |
| * anonymous llog, exit now to allow following tests run. |
| * It is fixed in upcoming llog over OSD code */ |
| GOTO(out_put, rc); |
| |
| CWARN("2b: create a log without specified NAME & LOGID\n"); |
| rc = llog_open_create(env, ctxt, &loghandle, NULL, NULL); |
| if (rc) { |
| CERROR("2b: create log failed\n"); |
| GOTO(out_close_llh, rc); |
| } |
| rc = llog_init_handle(env, loghandle, LLOG_F_IS_PLAIN, &uuid); |
| if (rc) { |
| CERROR("2b: can't init llog handle: %d\n", rc); |
| GOTO(out_close, rc); |
| } |
| |
| logid = loghandle->lgh_id; |
| llog_close(env, loghandle); |
| |
| CWARN("2c: re-open the log by LOGID\n"); |
| rc = llog_open(env, ctxt, &loghandle, &logid, NULL, LLOG_OPEN_EXISTS); |
| if (rc) { |
| CERROR("2c: re-open log by LOGID failed\n"); |
| GOTO(out_close_llh, rc); |
| } |
| |
| rc = llog_init_handle(env, loghandle, LLOG_F_IS_PLAIN, &uuid); |
| if (rc) { |
| CERROR("2c: can't init llog handle: %d\n", rc); |
| GOTO(out_close, rc); |
| } |
| |
| CWARN("2b: destroy this log\n"); |
| rc = llog_destroy(env, loghandle); |
| if (rc) |
| CERROR("2d: destroy log failed\n"); |
| out_close: |
| llog_close(env, loghandle); |
| out_close_llh: |
| if (rc) |
| llog_close(env, *llh); |
| out_put: |
| llog_ctxt_put(ctxt); |
| |
| return rc; |
| } |
| |
| /* Test record writing, single and in bulk */ |
| static int llog_test_3(const struct lu_env *env, struct obd_device *obd, |
| struct llog_handle *llh) |
| { |
| struct llog_gen_rec lgr; |
| int rc, i; |
| int num_recs = 1; /* 1 for the header */ |
| |
| lgr.lgr_hdr.lrh_len = lgr.lgr_tail.lrt_len = sizeof(lgr); |
| lgr.lgr_hdr.lrh_type = LLOG_GEN_REC; |
| |
| CWARN("3a: write one create_rec\n"); |
| rc = llog_write(env, llh, &lgr.lgr_hdr, NULL, 0, NULL, -1); |
| num_recs++; |
| if (rc < 0) { |
| CERROR("3a: write one log record failed: %d\n", rc); |
| return rc; |
| } |
| |
| rc = verify_handle("3a", llh, num_recs); |
| if (rc) |
| return rc; |
| |
| CWARN("3b: write 10 cfg log records with 8 bytes bufs\n"); |
| for (i = 0; i < 10; i++) { |
| struct llog_rec_hdr hdr; |
| char buf[8]; |
| |
| hdr.lrh_len = 8; |
| hdr.lrh_type = OBD_CFG_REC; |
| memset(buf, 0, sizeof(buf)); |
| rc = llog_write(env, llh, &hdr, NULL, 0, buf, -1); |
| if (rc < 0) { |
| CERROR("3b: write 10 records failed at #%d: %d\n", |
| i + 1, rc); |
| return rc; |
| } |
| num_recs++; |
| } |
| |
| rc = verify_handle("3b", llh, num_recs); |
| if (rc) |
| return rc; |
| |
| CWARN("3c: write 1000 more log records\n"); |
| for (i = 0; i < 1000; i++) { |
| rc = llog_write(env, llh, &lgr.lgr_hdr, NULL, 0, NULL, -1); |
| if (rc < 0) { |
| CERROR("3c: write 1000 records failed at #%d: %d\n", |
| i + 1, rc); |
| return rc; |
| } |
| num_recs++; |
| } |
| |
| rc = verify_handle("3c", llh, num_recs); |
| if (rc) |
| return rc; |
| |
| CWARN("3d: write log more than BITMAP_SIZE, return -ENOSPC\n"); |
| for (i = 0; i < LLOG_BITMAP_SIZE(llh->lgh_hdr) + 1; i++) { |
| struct llog_rec_hdr hdr; |
| char buf_even[24]; |
| char buf_odd[32]; |
| |
| memset(buf_odd, 0, sizeof(buf_odd)); |
| memset(buf_even, 0, sizeof(buf_even)); |
| if ((i % 2) == 0) { |
| hdr.lrh_len = 24; |
| hdr.lrh_type = OBD_CFG_REC; |
| rc = llog_write(env, llh, &hdr, NULL, 0, buf_even, -1); |
| } else { |
| hdr.lrh_len = 32; |
| hdr.lrh_type = OBD_CFG_REC; |
| rc = llog_write(env, llh, &hdr, NULL, 0, buf_odd, -1); |
| } |
| if (rc == -ENOSPC) { |
| break; |
| } else if (rc < 0) { |
| CERROR("3d: write recs failed at #%d: %d\n", |
| i + 1, rc); |
| return rc; |
| } |
| num_recs++; |
| } |
| if (rc != -ENOSPC) { |
| CWARN("3d: write record more than BITMAP size!\n"); |
| return -EINVAL; |
| } |
| CWARN("3d: wrote %d more records before end of llog is reached\n", |
| num_recs); |
| |
| rc = verify_handle("3d", llh, num_recs); |
| |
| return rc; |
| } |
| |
| /* Test catalogue additions */ |
| static int llog_test_4(const struct lu_env *env, struct obd_device *obd) |
| { |
| struct llog_handle *cath; |
| char name[10]; |
| int rc, rc2, i, buflen; |
| struct llog_mini_rec lmr; |
| struct llog_cookie cookie; |
| struct llog_ctxt *ctxt; |
| int num_recs = 0; |
| char *buf; |
| struct llog_rec_hdr rec; |
| |
| ctxt = llog_get_context(obd, LLOG_TEST_ORIG_CTXT); |
| LASSERT(ctxt); |
| |
| lmr.lmr_hdr.lrh_len = lmr.lmr_tail.lrt_len = LLOG_MIN_REC_SIZE; |
| lmr.lmr_hdr.lrh_type = 0xf00f00; |
| |
| sprintf(name, "%x", llog_test_rand + 1); |
| CWARN("4a: create a catalog log with name: %s\n", name); |
| rc = llog_open_create(env, ctxt, &cath, NULL, name); |
| if (rc) { |
| CERROR("4a: llog_create with name %s failed: %d\n", name, rc); |
| GOTO(ctxt_release, rc); |
| } |
| rc = llog_init_handle(env, cath, LLOG_F_IS_CAT, &uuid); |
| if (rc) { |
| CERROR("4a: can't init llog handle: %d\n", rc); |
| GOTO(out, rc); |
| } |
| |
| num_recs++; |
| cat_logid = cath->lgh_id; |
| |
| CWARN("4b: write 1 record into the catalog\n"); |
| rc = llog_cat_add(env, cath, &lmr.lmr_hdr, &cookie, NULL); |
| if (rc != 1) { |
| CERROR("4b: write 1 catalog record failed at: %d\n", rc); |
| GOTO(out, rc); |
| } |
| num_recs++; |
| rc = verify_handle("4b", cath, 2); |
| if (rc) |
| GOTO(out, rc); |
| |
| rc = verify_handle("4b", cath->u.chd.chd_current_log, num_recs); |
| if (rc) |
| GOTO(out, rc); |
| |
| CWARN("4c: cancel 1 log record\n"); |
| rc = llog_cat_cancel_records(env, cath, 1, &cookie); |
| if (rc) { |
| CERROR("4c: cancel 1 catalog based record failed: %d\n", rc); |
| GOTO(out, rc); |
| } |
| num_recs--; |
| |
| rc = verify_handle("4c", cath->u.chd.chd_current_log, num_recs); |
| if (rc) |
| GOTO(out, rc); |
| |
| CWARN("4d: write %d more log records\n", LLOG_TEST_RECNUM); |
| for (i = 0; i < LLOG_TEST_RECNUM; i++) { |
| rc = llog_cat_add(env, cath, &lmr.lmr_hdr, NULL, NULL); |
| if (rc) { |
| CERROR("4d: write %d records failed at #%d: %d\n", |
| LLOG_TEST_RECNUM, i + 1, rc); |
| GOTO(out, rc); |
| } |
| num_recs++; |
| } |
| |
| /* make sure new plain llog appears */ |
| rc = verify_handle("4d", cath, 3); |
| if (rc) |
| GOTO(out, rc); |
| |
| CWARN("4e: add 5 large records, one record per block\n"); |
| buflen = LLOG_CHUNK_SIZE - sizeof(struct llog_rec_hdr) - |
| sizeof(struct llog_rec_tail); |
| OBD_ALLOC(buf, buflen); |
| if (buf == NULL) |
| GOTO(out, rc = -ENOMEM); |
| for (i = 0; i < 5; i++) { |
| rec.lrh_len = buflen; |
| rec.lrh_type = OBD_CFG_REC; |
| rc = llog_cat_add(env, cath, &rec, NULL, buf); |
| if (rc) { |
| CERROR("4e: write 5 records failed at #%d: %d\n", |
| i + 1, rc); |
| GOTO(out_free, rc); |
| } |
| num_recs++; |
| } |
| out_free: |
| OBD_FREE(buf, buflen); |
| out: |
| CWARN("4f: put newly-created catalog\n"); |
| rc2 = llog_cat_close(env, cath); |
| if (rc2) { |
| CERROR("4: close log %s failed: %d\n", name, rc2); |
| if (rc == 0) |
| rc = rc2; |
| } |
| ctxt_release: |
| llog_ctxt_put(ctxt); |
| return rc; |
| } |
| |
| static int cat_counter; |
| |
| static int cat_print_cb(const struct lu_env *env, struct llog_handle *llh, |
| struct llog_rec_hdr *rec, void *data) |
| { |
| struct llog_logid_rec *lir = (struct llog_logid_rec *)rec; |
| struct lu_fid fid = {0}; |
| |
| if (rec->lrh_type != LLOG_LOGID_MAGIC) { |
| CERROR("invalid record in catalog\n"); |
| return -EINVAL; |
| } |
| |
| logid_to_fid(&lir->lid_id, &fid); |
| |
| CWARN("seeing record at index %d - "DFID" in log "DFID"\n", |
| rec->lrh_index, PFID(&fid), |
| PFID(lu_object_fid(&llh->lgh_obj->do_lu))); |
| |
| cat_counter++; |
| |
| return 0; |
| } |
| |
| static int plain_counter; |
| |
| static int plain_print_cb(const struct lu_env *env, struct llog_handle *llh, |
| struct llog_rec_hdr *rec, void *data) |
| { |
| struct lu_fid fid = {0}; |
| |
| if (!(llh->lgh_hdr->llh_flags & LLOG_F_IS_PLAIN)) { |
| CERROR("log is not plain\n"); |
| return -EINVAL; |
| } |
| |
| logid_to_fid(&llh->lgh_id, &fid); |
| |
| CDEBUG(D_INFO, "seeing record at index %d in log "DFID"\n", |
| rec->lrh_index, PFID(&fid)); |
| |
| plain_counter++; |
| |
| return 0; |
| } |
| |
| static int cancel_count; |
| |
| static int llog_cancel_rec_cb(const struct lu_env *env, |
| struct llog_handle *llh, |
| struct llog_rec_hdr *rec, void *data) |
| { |
| struct llog_cookie cookie; |
| |
| if (!(llh->lgh_hdr->llh_flags & LLOG_F_IS_PLAIN)) { |
| CERROR("log is not plain\n"); |
| return -EINVAL; |
| } |
| |
| cookie.lgc_lgl = llh->lgh_id; |
| cookie.lgc_index = rec->lrh_index; |
| |
| llog_cat_cancel_records(env, llh->u.phd.phd_cat_handle, 1, &cookie); |
| cancel_count++; |
| if (cancel_count == LLOG_TEST_RECNUM) |
| return -LLOG_EEMPTY; |
| return 0; |
| } |
| |
| /* Test log and catalogue processing */ |
| static int llog_test_5(const struct lu_env *env, struct obd_device *obd) |
| { |
| struct llog_handle *llh = NULL; |
| char name[10]; |
| int rc, rc2; |
| struct llog_mini_rec lmr; |
| struct llog_ctxt *ctxt; |
| |
| ctxt = llog_get_context(obd, LLOG_TEST_ORIG_CTXT); |
| LASSERT(ctxt); |
| |
| lmr.lmr_hdr.lrh_len = lmr.lmr_tail.lrt_len = LLOG_MIN_REC_SIZE; |
| lmr.lmr_hdr.lrh_type = 0xf00f00; |
| |
| CWARN("5a: re-open catalog by id\n"); |
| rc = llog_open(env, ctxt, &llh, &cat_logid, NULL, LLOG_OPEN_EXISTS); |
| if (rc) { |
| CERROR("5a: llog_create with logid failed: %d\n", rc); |
| GOTO(out_put, rc); |
| } |
| |
| rc = llog_init_handle(env, llh, LLOG_F_IS_CAT, &uuid); |
| if (rc) { |
| CERROR("5a: can't init llog handle: %d\n", rc); |
| GOTO(out, rc); |
| } |
| |
| CWARN("5b: print the catalog entries.. we expect 2\n"); |
| cat_counter = 0; |
| rc = llog_process(env, llh, cat_print_cb, "test 5", NULL); |
| if (rc) { |
| CERROR("5b: process with cat_print_cb failed: %d\n", rc); |
| GOTO(out, rc); |
| } |
| if (cat_counter != 2) { |
| CERROR("5b: %d entries in catalog\n", cat_counter); |
| GOTO(out, rc = -EINVAL); |
| } |
| |
| CWARN("5c: Cancel %d records, see one log zapped\n", LLOG_TEST_RECNUM); |
| cancel_count = 0; |
| rc = llog_cat_process(env, llh, llog_cancel_rec_cb, "foobar", 0, 0); |
| if (rc != -LLOG_EEMPTY) { |
| CERROR("5c: process with cat_cancel_cb failed: %d\n", rc); |
| GOTO(out, rc); |
| } |
| |
| CWARN("5c: print the catalog entries.. we expect 1\n"); |
| cat_counter = 0; |
| rc = llog_process(env, llh, cat_print_cb, "test 5", NULL); |
| if (rc) { |
| CERROR("5c: process with cat_print_cb failed: %d\n", rc); |
| GOTO(out, rc); |
| } |
| if (cat_counter != 1) { |
| CERROR("5c: %d entries in catalog\n", cat_counter); |
| GOTO(out, rc = -EINVAL); |
| } |
| |
| CWARN("5d: add 1 record to the log with many canceled empty pages\n"); |
| rc = llog_cat_add(env, llh, &lmr.lmr_hdr, NULL, NULL); |
| if (rc) { |
| CERROR("5d: add record to the log with many canceled empty " |
| "pages failed\n"); |
| GOTO(out, rc); |
| } |
| |
| CWARN("5e: print plain log entries.. expect 6\n"); |
| plain_counter = 0; |
| rc = llog_cat_process(env, llh, plain_print_cb, "foobar", 0, 0); |
| if (rc) { |
| CERROR("5e: process with plain_print_cb failed: %d\n", rc); |
| GOTO(out, rc); |
| } |
| if (plain_counter != 6) { |
| CERROR("5e: found %d records\n", plain_counter); |
| GOTO(out, rc = -EINVAL); |
| } |
| |
| CWARN("5f: print plain log entries reversely.. expect 6\n"); |
| plain_counter = 0; |
| rc = llog_cat_reverse_process(env, llh, plain_print_cb, "foobar"); |
| if (rc) { |
| CERROR("5f: reversely process with plain_print_cb failed:" |
| "%d\n", rc); |
| GOTO(out, rc); |
| } |
| if (plain_counter != 6) { |
| CERROR("5f: found %d records\n", plain_counter); |
| GOTO(out, rc = -EINVAL); |
| } |
| |
| out: |
| CWARN("5g: close re-opened catalog\n"); |
| rc2 = llog_cat_close(env, llh); |
| if (rc2) { |
| CERROR("5g: close log %s failed: %d\n", name, rc2); |
| if (rc == 0) |
| rc = rc2; |
| } |
| out_put: |
| llog_ctxt_put(ctxt); |
| |
| return rc; |
| } |
| |
| /* Test client api; open log by name and process */ |
| static int llog_test_6(const struct lu_env *env, struct obd_device *obd, |
| char *name) |
| { |
| struct obd_device *mgc_obd; |
| struct llog_ctxt *ctxt; |
| struct obd_uuid *mgs_uuid; |
| struct obd_export *exp; |
| struct obd_uuid uuid = { "LLOG_TEST6_UUID" }; |
| struct llog_handle *llh = NULL; |
| struct llog_ctxt *nctxt; |
| int rc, rc2; |
| |
| ctxt = llog_get_context(obd, LLOG_TEST_ORIG_CTXT); |
| LASSERT(ctxt); |
| mgs_uuid = &ctxt->loc_exp->exp_obd->obd_uuid; |
| |
| CWARN("6a: re-open log %s using client API\n", name); |
| mgc_obd = class_find_client_obd(mgs_uuid, LUSTRE_MGC_NAME, NULL); |
| if (mgc_obd == NULL) { |
| CERROR("6a: no MGC devices connected to %s found.\n", |
| mgs_uuid->uuid); |
| GOTO(ctxt_release, rc = -ENOENT); |
| } |
| |
| rc = obd_connect(NULL, &exp, mgc_obd, &uuid, |
| NULL /* obd_connect_data */, NULL); |
| if (rc != -EALREADY) { |
| CERROR("6a: connect on connected MGC (%s) failed to return" |
| " -EALREADY", mgc_obd->obd_name); |
| if (rc == 0) |
| obd_disconnect(exp); |
| GOTO(ctxt_release, rc = -EINVAL); |
| } |
| |
| nctxt = llog_get_context(mgc_obd, LLOG_CONFIG_REPL_CTXT); |
| rc = llog_open(env, nctxt, &llh, NULL, name, LLOG_OPEN_EXISTS); |
| if (rc) { |
| CERROR("6a: llog_open failed %d\n", rc); |
| GOTO(nctxt_put, rc); |
| } |
| |
| rc = llog_init_handle(env, llh, LLOG_F_IS_PLAIN, NULL); |
| if (rc) { |
| CERROR("6a: llog_init_handle failed %d\n", rc); |
| GOTO(parse_out, rc); |
| } |
| |
| plain_counter = 1; /* llog header is first record */ |
| CWARN("6b: process log %s using client API\n", name); |
| rc = llog_process(env, llh, plain_print_cb, NULL, NULL); |
| if (rc) |
| CERROR("6b: llog_process failed %d\n", rc); |
| CWARN("6b: processed %d records\n", plain_counter); |
| |
| rc = verify_handle("6b", llh, plain_counter); |
| if (rc) |
| GOTO(parse_out, rc); |
| |
| plain_counter = 1; /* llog header is first record */ |
| CWARN("6c: process log %s reversely using client API\n", name); |
| rc = llog_reverse_process(env, llh, plain_print_cb, NULL, NULL); |
| if (rc) |
| CERROR("6c: llog_reverse_process failed %d\n", rc); |
| CWARN("6c: processed %d records\n", plain_counter); |
| |
| rc = verify_handle("6c", llh, plain_counter); |
| if (rc) |
| GOTO(parse_out, rc); |
| |
| parse_out: |
| rc2 = llog_close(env, llh); |
| if (rc2) { |
| CERROR("6: llog_close failed: rc = %d\n", rc2); |
| if (rc == 0) |
| rc = rc2; |
| } |
| nctxt_put: |
| llog_ctxt_put(nctxt); |
| ctxt_release: |
| llog_ctxt_put(ctxt); |
| return rc; |
| } |
| |
| static union { |
| struct llog_rec_hdr lrh; /* common header */ |
| struct llog_logid_rec llr; /* LLOG_LOGID_MAGIC */ |
| struct llog_unlink64_rec lur; /* MDS_UNLINK64_REC */ |
| struct llog_setattr64_rec lsr64; /* MDS_SETATTR64_REC */ |
| struct llog_size_change_rec lscr; /* OST_SZ_REC */ |
| struct llog_changelog_rec lcr; /* CHANGELOG_REC */ |
| struct llog_changelog_user_rec lcur; /* CHANGELOG_USER_REC */ |
| struct llog_gen_rec lgr; /* LLOG_GEN_REC */ |
| } llog_records; |
| |
| static int test_7_print_cb(const struct lu_env *env, struct llog_handle *llh, |
| struct llog_rec_hdr *rec, void *data) |
| { |
| struct lu_fid fid = {0}; |
| |
| logid_to_fid(&llh->lgh_id, &fid); |
| |
| CDEBUG(D_OTHER, "record type %#x at index %d in log "DFID"\n", |
| rec->lrh_type, rec->lrh_index, PFID(&fid)); |
| |
| plain_counter++; |
| return 0; |
| } |
| |
| static int test_7_cancel_cb(const struct lu_env *env, struct llog_handle *llh, |
| struct llog_rec_hdr *rec, void *data) |
| { |
| plain_counter++; |
| /* test LLOG_DEL_RECORD is working */ |
| return LLOG_DEL_RECORD; |
| } |
| |
| static int llog_test_7_sub(const struct lu_env *env, struct llog_ctxt *ctxt) |
| { |
| struct llog_handle *llh; |
| int rc = 0, i, process_count; |
| int num_recs = 0; |
| |
| rc = llog_open_create(env, ctxt, &llh, NULL, NULL); |
| if (rc) { |
| CERROR("7_sub: create log failed\n"); |
| return rc; |
| } |
| |
| rc = llog_init_handle(env, llh, |
| LLOG_F_IS_PLAIN | LLOG_F_ZAP_WHEN_EMPTY, |
| &uuid); |
| if (rc) { |
| CERROR("7_sub: can't init llog handle: %d\n", rc); |
| GOTO(out_close, rc); |
| } |
| for (i = 0; i < LLOG_BITMAP_SIZE(llh->lgh_hdr); i++) { |
| rc = llog_write(env, llh, &llog_records.lrh, NULL, 0, |
| NULL, -1); |
| if (rc == -ENOSPC) { |
| break; |
| } else if (rc < 0) { |
| CERROR("7_sub: write recs failed at #%d: %d\n", |
| i + 1, rc); |
| GOTO(out_close, rc); |
| } |
| num_recs++; |
| } |
| if (rc != -ENOSPC) { |
| CWARN("7_sub: write record more than BITMAP size!\n"); |
| GOTO(out_close, rc = -EINVAL); |
| } |
| |
| rc = verify_handle("7_sub", llh, num_recs + 1); |
| if (rc) { |
| CERROR("7_sub: verify handle failed: %d\n", rc); |
| GOTO(out_close, rc); |
| } |
| if (num_recs < LLOG_BITMAP_SIZE(llh->lgh_hdr) - 1) |
| CWARN("7_sub: records are not aligned, written %d from %u\n", |
| num_recs, LLOG_BITMAP_SIZE(llh->lgh_hdr) - 1); |
| |
| plain_counter = 0; |
| rc = llog_process(env, llh, test_7_print_cb, "test 7", NULL); |
| if (rc) { |
| CERROR("7_sub: llog process failed: %d\n", rc); |
| GOTO(out_close, rc); |
| } |
| process_count = plain_counter; |
| if (process_count != num_recs) { |
| CERROR("7_sub: processed %d records from %d total\n", |
| process_count, num_recs); |
| GOTO(out_close, rc = -EINVAL); |
| } |
| |
| plain_counter = 0; |
| rc = llog_reverse_process(env, llh, test_7_cancel_cb, "test 7", NULL); |
| if (rc) { |
| CERROR("7_sub: reverse llog process failed: %d\n", rc); |
| GOTO(out_close, rc); |
| } |
| if (process_count != plain_counter) { |
| CERROR("7_sub: Reverse/direct processing found different" |
| "number of records: %d/%d\n", |
| plain_counter, process_count); |
| GOTO(out_close, rc = -EINVAL); |
| } |
| if (llog_exist(llh)) { |
| CERROR("7_sub: llog exists but should be zapped\n"); |
| GOTO(out_close, rc = -EEXIST); |
| } |
| |
| rc = verify_handle("7_sub", llh, 1); |
| out_close: |
| if (rc) |
| llog_destroy(env, llh); |
| llog_close(env, llh); |
| return rc; |
| } |
| |
| /* Test all llog records writing and processing */ |
| static int llog_test_7(const struct lu_env *env, struct obd_device *obd) |
| { |
| struct llog_ctxt *ctxt; |
| int rc; |
| |
| ctxt = llog_get_context(obd, LLOG_TEST_ORIG_CTXT); |
| |
| CWARN("7a: test llog_logid_rec\n"); |
| llog_records.llr.lid_hdr.lrh_len = sizeof(llog_records.llr); |
| llog_records.llr.lid_tail.lrt_len = sizeof(llog_records.llr); |
| llog_records.llr.lid_hdr.lrh_type = LLOG_LOGID_MAGIC; |
| |
| rc = llog_test_7_sub(env, ctxt); |
| if (rc) { |
| CERROR("7a: llog_logid_rec test failed\n"); |
| GOTO(out, rc); |
| } |
| |
| CWARN("7b: test llog_unlink64_rec\n"); |
| llog_records.lur.lur_hdr.lrh_len = sizeof(llog_records.lur); |
| llog_records.lur.lur_tail.lrt_len = sizeof(llog_records.lur); |
| llog_records.lur.lur_hdr.lrh_type = MDS_UNLINK64_REC; |
| |
| rc = llog_test_7_sub(env, ctxt); |
| if (rc) { |
| CERROR("7b: llog_unlink_rec test failed\n"); |
| GOTO(out, rc); |
| } |
| |
| CWARN("7c: test llog_setattr64_rec\n"); |
| llog_records.lsr64.lsr_hdr.lrh_len = sizeof(llog_records.lsr64); |
| llog_records.lsr64.lsr_tail.lrt_len = sizeof(llog_records.lsr64); |
| llog_records.lsr64.lsr_hdr.lrh_type = MDS_SETATTR64_REC; |
| |
| rc = llog_test_7_sub(env, ctxt); |
| if (rc) { |
| CERROR("7c: llog_setattr64_rec test failed\n"); |
| GOTO(out, rc); |
| } |
| |
| CWARN("7d: test llog_size_change_rec\n"); |
| llog_records.lscr.lsc_hdr.lrh_len = sizeof(llog_records.lscr); |
| llog_records.lscr.lsc_tail.lrt_len = sizeof(llog_records.lscr); |
| llog_records.lscr.lsc_hdr.lrh_type = OST_SZ_REC; |
| |
| rc = llog_test_7_sub(env, ctxt); |
| if (rc) { |
| CERROR("7d: llog_size_change_rec test failed\n"); |
| GOTO(out, rc); |
| } |
| |
| CWARN("7e: test llog_changelog_rec\n"); |
| llog_records.lcr.cr_hdr.lrh_len = sizeof(llog_records.lcr); |
| llog_records.lcr.cr_tail.lrt_len = sizeof(llog_records.lcr); |
| llog_records.lcr.cr_hdr.lrh_type = CHANGELOG_REC; |
| |
| rc = llog_test_7_sub(env, ctxt); |
| if (rc) { |
| CERROR("7e: llog_changelog_rec test failed\n"); |
| GOTO(out, rc); |
| } |
| |
| CWARN("7f: test llog_changelog_user_rec\n"); |
| llog_records.lcur.cur_hdr.lrh_len = sizeof(llog_records.lcur); |
| llog_records.lcur.cur_tail.lrt_len = sizeof(llog_records.lcur); |
| llog_records.lcur.cur_hdr.lrh_type = CHANGELOG_USER_REC; |
| |
| rc = llog_test_7_sub(env, ctxt); |
| if (rc) { |
| CERROR("7f: llog_changelog_user_rec test failed\n"); |
| GOTO(out, rc); |
| } |
| |
| CWARN("7g: test llog_gen_rec\n"); |
| llog_records.lgr.lgr_hdr.lrh_len = sizeof(llog_records.lgr); |
| llog_records.lgr.lgr_tail.lrt_len = sizeof(llog_records.lgr); |
| llog_records.lgr.lgr_hdr.lrh_type = LLOG_GEN_REC; |
| |
| rc = llog_test_7_sub(env, ctxt); |
| if (rc) { |
| CERROR("7g: llog_size_change_rec test failed\n"); |
| GOTO(out, rc); |
| } |
| out: |
| llog_ctxt_put(ctxt); |
| return rc; |
| } |
| |
| /* ------------------------------------------------------------------------- |
| * Tests above, boring obd functions below |
| * ------------------------------------------------------------------------- */ |
| static int llog_run_tests(const struct lu_env *env, struct obd_device *obd) |
| { |
| struct llog_handle *llh = NULL; |
| struct llog_ctxt *ctxt; |
| int rc, err; |
| char name[10]; |
| |
| ctxt = llog_get_context(obd, LLOG_TEST_ORIG_CTXT); |
| LASSERT(ctxt); |
| |
| sprintf(name, "%x", llog_test_rand); |
| |
| rc = llog_test_1(env, obd, name); |
| if (rc) |
| GOTO(cleanup_ctxt, rc); |
| |
| rc = llog_test_2(env, obd, name, &llh); |
| if (rc) |
| GOTO(cleanup_ctxt, rc); |
| |
| rc = llog_test_3(env, obd, llh); |
| if (rc) |
| GOTO(cleanup, rc); |
| |
| rc = llog_test_4(env, obd); |
| if (rc) |
| GOTO(cleanup, rc); |
| |
| rc = llog_test_5(env, obd); |
| if (rc) |
| GOTO(cleanup, rc); |
| |
| rc = llog_test_6(env, obd, name); |
| if (rc) |
| GOTO(cleanup, rc); |
| |
| rc = llog_test_7(env, obd); |
| if (rc) |
| GOTO(cleanup, rc); |
| |
| cleanup: |
| err = llog_destroy(env, llh); |
| if (err) |
| CERROR("cleanup: llog_destroy failed: %d\n", err); |
| llog_close(env, llh); |
| if (rc == 0) |
| rc = err; |
| cleanup_ctxt: |
| llog_ctxt_put(ctxt); |
| return rc; |
| } |
| |
| #ifdef LPROCFS |
| static struct lprocfs_vars lprocfs_llog_test_obd_vars[] = { {0} }; |
| static struct lprocfs_vars lprocfs_llog_test_module_vars[] = { {0} }; |
| static void lprocfs_llog_test_init_vars(struct lprocfs_static_vars *lvars) |
| { |
| lvars->module_vars = lprocfs_llog_test_module_vars; |
| lvars->obd_vars = lprocfs_llog_test_obd_vars; |
| } |
| #else |
| static void lprocfs_llog_test_init_vars(struct lprocfs_static_vars *lvars) |
| { |
| } |
| #endif |
| |
| static int llog_test_cleanup(struct obd_device *obd) |
| { |
| struct obd_device *tgt; |
| struct lu_env env; |
| int rc; |
| |
| rc = lu_env_init(&env, LCT_LOCAL | LCT_MG_THREAD); |
| if (rc) |
| return rc; |
| |
| tgt = obd->obd_lvfs_ctxt.dt->dd_lu_dev.ld_obd; |
| rc = llog_cleanup(&env, llog_get_context(tgt, LLOG_TEST_ORIG_CTXT)); |
| if (rc) |
| CERROR("failed to llog_test_llog_finish: %d\n", rc); |
| lu_env_fini(&env); |
| return rc; |
| } |
| |
| static int llog_test_setup(struct obd_device *obd, struct lustre_cfg *lcfg) |
| { |
| struct obd_device *tgt; |
| struct llog_ctxt *ctxt; |
| struct dt_object *o; |
| struct lu_env env; |
| struct lu_context test_session; |
| int rc; |
| |
| if (lcfg->lcfg_bufcount < 2) { |
| CERROR("requires a TARGET OBD name\n"); |
| return -EINVAL; |
| } |
| |
| if (lcfg->lcfg_buflens[1] < 1) { |
| CERROR("requires a TARGET OBD name\n"); |
| return -EINVAL; |
| } |
| |
| /* disk obd */ |
| tgt = class_name2obd(lustre_cfg_string(lcfg, 1)); |
| if (!tgt || !tgt->obd_attached || !tgt->obd_set_up) { |
| CERROR("target device not attached or not set up (%s)\n", |
| lustre_cfg_string(lcfg, 1)); |
| return -EINVAL; |
| } |
| |
| rc = lu_env_init(&env, LCT_LOCAL | LCT_MG_THREAD); |
| if (rc) |
| return rc; |
| |
| rc = lu_context_init(&test_session, LCT_SESSION); |
| if (rc) |
| GOTO(cleanup_env, rc); |
| test_session.lc_thread = (struct ptlrpc_thread *)current; |
| lu_context_enter(&test_session); |
| env.le_ses = &test_session; |
| |
| CWARN("Setup llog-test device over %s device\n", |
| lustre_cfg_string(lcfg, 1)); |
| |
| OBD_SET_CTXT_MAGIC(&obd->obd_lvfs_ctxt); |
| obd->obd_lvfs_ctxt.dt = lu2dt_dev(tgt->obd_lu_dev); |
| |
| rc = llog_setup(&env, tgt, &tgt->obd_olg, LLOG_TEST_ORIG_CTXT, tgt, |
| &llog_osd_ops); |
| if (rc) |
| GOTO(cleanup_session, rc); |
| |
| /* use MGS llog dir for tests */ |
| ctxt = llog_get_context(tgt, LLOG_CONFIG_ORIG_CTXT); |
| LASSERT(ctxt); |
| o = ctxt->loc_dir; |
| llog_ctxt_put(ctxt); |
| |
| ctxt = llog_get_context(tgt, LLOG_TEST_ORIG_CTXT); |
| LASSERT(ctxt); |
| ctxt->loc_dir = o; |
| llog_ctxt_put(ctxt); |
| |
| llog_test_rand = cfs_rand(); |
| |
| rc = llog_run_tests(&env, tgt); |
| if (rc) |
| llog_test_cleanup(obd); |
| cleanup_session: |
| lu_context_exit(&test_session); |
| lu_context_fini(&test_session); |
| cleanup_env: |
| lu_env_fini(&env); |
| return rc; |
| } |
| |
| static struct obd_ops llog_obd_ops = { |
| .o_owner = THIS_MODULE, |
| .o_setup = llog_test_setup, |
| .o_cleanup = llog_test_cleanup, |
| }; |
| |
| static int __init llog_test_init(void) |
| { |
| struct lprocfs_static_vars uninitialized_var(lvars); |
| |
| lprocfs_llog_test_init_vars(&lvars); |
| return class_register_type(&llog_obd_ops, NULL, |
| lvars.module_vars, "llog_test", NULL); |
| } |
| |
| static void __exit llog_test_exit(void) |
| { |
| class_unregister_type("llog_test"); |
| } |
| |
| MODULE_AUTHOR("Sun Microsystems, Inc. <http://www.lustre.org/>"); |
| MODULE_DESCRIPTION("llog test module"); |
| MODULE_LICENSE("GPL"); |
| |
| module_init(llog_test_init); |
| module_exit(llog_test_exit); |