Exemplo n.º 1
0
static int llog_changelog_cancel_cb(const struct lu_env *env,
				    struct llog_handle *llh,
				    struct llog_rec_hdr *hdr, void *data)
{
	struct llog_changelog_rec *rec = (struct llog_changelog_rec *)hdr;
	struct llog_cookie	 cookie;
	long long		 endrec = *(long long *)data;
	int			 rc;

	ENTRY;

	/* This is always a (sub)log, not the catalog */
	LASSERT(llh->lgh_hdr->llh_flags & LLOG_F_IS_PLAIN);

	if (rec->cr.cr_index > endrec)
		/* records are in order, so we're done */
		RETURN(LLOG_PROC_BREAK);

	cookie.lgc_lgl = llh->lgh_id;
	cookie.lgc_index = hdr->lrh_index;

	/* cancel them one at a time.  I suppose we could store up the cookies
	 * and cancel them all at once; probably more efficient, but this is
	 * done as a user call, so who cares... */
	rc = llog_cat_cancel_records(env, llh->u.phd.phd_cat_handle, 1,
				     &cookie);
	RETURN(rc < 0 ? rc : 0);
}
Exemplo n.º 2
0
/** Two things:
 * 1. Find the smallest record everyone is willing to purge
 * 2. Update the last purgeable record for this user
 */
static int mdd_changelog_user_purge_cb(const struct lu_env *env,
				       struct llog_handle *llh,
				       struct llog_rec_hdr *hdr, void *data)
{
	struct llog_changelog_user_rec	*rec;
	struct mdd_changelog_user_data	*mcud = data;
	int				 rc;

        ENTRY;

        LASSERT(llh->lgh_hdr->llh_flags & LLOG_F_IS_PLAIN);

        rec = (struct llog_changelog_user_rec *)hdr;

        mcud->mcud_usercount++;

        /* If we have a new endrec for this id, use it for the following
           min check instead of its old value */
        if (rec->cur_id == mcud->mcud_id)
                rec->cur_endrec = max(rec->cur_endrec, mcud->mcud_endrec);

        /* Track the minimum referenced record */
        if (mcud->mcud_minid == 0 || mcud->mcud_minrec > rec->cur_endrec) {
                mcud->mcud_minid = rec->cur_id;
                mcud->mcud_minrec = rec->cur_endrec;
        }

        if (rec->cur_id != mcud->mcud_id)
                RETURN(0);

        /* Update this user's record */
        mcud->mcud_found = 1;

        /* Special case: unregister this user */
        if (mcud->mcud_endrec == MCUD_UNREGISTER) {
                struct llog_cookie cookie;

                cookie.lgc_lgl = llh->lgh_id;
                cookie.lgc_index = hdr->lrh_index;

		rc = llog_cat_cancel_records(env, llh->u.phd.phd_cat_handle,
					     1, &cookie);
                if (rc == 0)
                        mcud->mcud_usercount--;

                RETURN(rc);
        }

        /* Update the endrec */
        CDEBUG(D_IOCTL, "Rewriting changelog user %d endrec to "LPU64"\n",
               mcud->mcud_id, rec->cur_endrec);

	rc = llog_write(env, llh, hdr, hdr->lrh_index);

        RETURN(rc);
}
Exemplo n.º 3
0
Arquivo: mds_log.c Projeto: hpc/lustre
static int llog_changelog_cancel_cb(struct llog_handle *llh,
                                    struct llog_rec_hdr *hdr, void *data)
{
        struct llog_changelog_rec *rec = (struct llog_changelog_rec *)hdr;
        struct llog_cookie cookie;
        long long endrec = *(long long *)data;
        int rc, err;
        struct obd_device *obd;
        void *trans_h;
        struct inode *inode;
        ENTRY;

        /* This is always a (sub)log, not the catalog */
        LASSERT(llh->lgh_hdr->llh_flags & LLOG_F_IS_PLAIN);

        if (rec->cr.cr_index > endrec)
                /* records are in order, so we're done */
                RETURN(LLOG_PROC_BREAK);

        cookie.lgc_lgl = llh->lgh_id;
        cookie.lgc_index = hdr->lrh_index;
        obd = llh->lgh_ctxt->loc_exp->exp_obd;
        inode = llh->lgh_file->f_dentry->d_inode;

        /* XXX This is a workaround for the deadlock of changelog adding vs.
         * changelog cancelling. Changelog adding always start transaction
         * before acquiring the catlog lock (lgh_lock), whereas, changelog
         * cancelling do start transaction after holding catlog lock.
         *
         * We start the transaction earlier here to keep the locking ordering:
         * 'start transaction -> catlog lock'. LU-81. */
        trans_h = fsfilt_start_log(obd, inode, FSFILT_OP_CANCEL_UNLINK,
                                   NULL, 1);
        if (IS_ERR(trans_h)) {
                CERROR("fsfilt_start_log failed: %ld\n", PTR_ERR(trans_h));
                RETURN(PTR_ERR(trans_h));
        }

        /* cancel them one at a time.  I suppose we could store up the cookies
           and cancel them all at once; probably more efficient, but this is
           done as a user call, so who cares... */
        rc = llog_cat_cancel_records(llh->u.phd.phd_cat_handle, 1, &cookie);

        err = fsfilt_commit(obd, inode, trans_h, 0);
        if (err) {
                CERROR("fsfilt_commit failed: %d\n", err);
                rc = (rc >= 0) ? err : rc;
        }

        RETURN(rc < 0 ? rc : 0);
}
Exemplo n.º 4
0
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;
}
Exemplo n.º 5
0
static int llog_cancel_rec_cb(struct llog_handle *llh, struct llog_rec_hdr *rec,
                              void *data)
{
        struct llog_cookie cookie;
        static int i = 0;

        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(llh->u.phd.phd_cat_handle, 1, &cookie);
        i++;
        if (i == 40000)
                RETURN(-4711);
        RETURN(0);
}
Exemplo n.º 6
0
/* 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;
}
Exemplo n.º 7
0
int llog_ioctl(struct llog_ctxt *ctxt, int cmd, struct obd_ioctl_data *data)
{
        struct llog_logid logid;
        int err = 0;
        struct llog_handle *handle = NULL;

        ENTRY;
        if (*data->ioc_inlbuf1 == '#') {
                err = str2logid(&logid, data->ioc_inlbuf1, data->ioc_inllen1);
                if (err)
                        GOTO(out, err);
                err = llog_create(ctxt, &handle, &logid, NULL);
                if (err)
                        GOTO(out, err);
        } else if (*data->ioc_inlbuf1 == '$') {
                char *name = data->ioc_inlbuf1 + 1;
                err = llog_create(ctxt, &handle, NULL, name);
                if (err)
                        GOTO(out, err);
        } else {
                GOTO(out, err = -EINVAL);
        }

        err = llog_init_handle(handle, 0, NULL);
        if (err)
                GOTO(out_close, err = -ENOENT);

        switch (cmd) {
        case OBD_IOC_LLOG_INFO: {
                int l;
                int remains = data->ioc_inllen2 +
                        cfs_size_round(data->ioc_inllen1);
                char *out = data->ioc_bulk;

                l = snprintf(out, remains,
                             "logid:            #"LPX64"#"LPX64"#%08x\n"
                             "flags:            %x (%s)\n"
                             "records count:    %d\n"
                             "last index:       %d\n",
                             handle->lgh_id.lgl_oid, handle->lgh_id.lgl_oseq,
                             handle->lgh_id.lgl_ogen,
                             handle->lgh_hdr->llh_flags,
                             handle->lgh_hdr->llh_flags &
                             LLOG_F_IS_CAT ? "cat" : "plain",
                             handle->lgh_hdr->llh_count,
                             handle->lgh_last_idx);
                out += l;
                remains -= l;
                if (remains <= 0)
                        CERROR("not enough space for log header info\n");

                GOTO(out_close, err);
        }
        case OBD_IOC_LLOG_CHECK: {
                LASSERT(data->ioc_inllen1);
                err = llog_process(handle, llog_check_cb, data, NULL);
                if (err == -LLOG_EEMPTY)
                        err = 0;
                GOTO(out_close, err);
        }

        case OBD_IOC_LLOG_PRINT: {
                LASSERT(data->ioc_inllen1);
                err = llog_process(handle, class_config_dump_handler,data,NULL);
                if (err == -LLOG_EEMPTY)
                        err = 0;
                else
                        err = llog_process(handle, llog_print_cb, data, NULL);

                GOTO(out_close, err);
        }
        case OBD_IOC_LLOG_CANCEL: {
                struct llog_cookie cookie;
                struct llog_logid plain;
                char *endp;

                cookie.lgc_index = simple_strtoul(data->ioc_inlbuf3, &endp, 0);
                if (*endp != '\0')
                        GOTO(out_close, err = -EINVAL);

                if (handle->lgh_hdr->llh_flags & LLOG_F_IS_CAT) {
                        cfs_down_write(&handle->lgh_lock);
                        err = llog_cancel_rec(handle, cookie.lgc_index);
                        cfs_up_write(&handle->lgh_lock);
                        GOTO(out_close, err);
                }

                err = str2logid(&plain, data->ioc_inlbuf2, data->ioc_inllen2);
                if (err)
                        GOTO(out_close, err);
                cookie.lgc_lgl = plain;

                if (!(handle->lgh_hdr->llh_flags & LLOG_F_IS_CAT))
                        GOTO(out_close, err = -EINVAL);

                err = llog_cat_cancel_records(handle, 1, &cookie);
                GOTO(out_close, err);
        }
        case OBD_IOC_LLOG_REMOVE: {
                struct llog_logid plain;

                if (handle->lgh_hdr->llh_flags & LLOG_F_IS_PLAIN) {
                        err = llog_destroy(handle);
                        if (!err)
                                llog_free_handle(handle);
                        GOTO(out, err);
                }

                if (!(handle->lgh_hdr->llh_flags & LLOG_F_IS_CAT))
                        GOTO(out_close, err = -EINVAL);

                if (data->ioc_inlbuf2) {
                        /*remove indicate log from the catalog*/
                        err = str2logid(&plain, data->ioc_inlbuf2,
                                        data->ioc_inllen2);
                        if (err)
                                GOTO(out_close, err);
                        err = llog_remove_log(handle, &plain);
                } else {
                        /*remove all the log of the catalog*/
                        llog_process(handle, llog_delete_cb, NULL, NULL);
                }
                GOTO(out_close, err);
        }
        }

out_close:
        if (handle->lgh_hdr &&
            handle->lgh_hdr->llh_flags & LLOG_F_IS_CAT)
                llog_cat_put(handle);
        else
                llog_close(handle);
out:
        RETURN(err);
}
Exemplo n.º 8
0
int llog_ioctl(const struct lu_env *env, struct llog_ctxt *ctxt, int cmd,
	       struct obd_ioctl_data *data)
{
	struct llog_logid	 logid;
	int			 rc = 0;
	struct llog_handle	*handle = NULL;

	if (*data->ioc_inlbuf1 == '#') {
		rc = str2logid(&logid, data->ioc_inlbuf1, data->ioc_inllen1);
		if (rc)
			return rc;
		rc = llog_open(env, ctxt, &handle, &logid, NULL,
			       LLOG_OPEN_EXISTS);
		if (rc)
			return rc;
	} else if (*data->ioc_inlbuf1 == '$') {
		char *name = data->ioc_inlbuf1 + 1;

		rc = llog_open(env, ctxt, &handle, NULL, name,
			       LLOG_OPEN_EXISTS);
		if (rc)
			return rc;
	} else {
		return -EINVAL;
	}

	rc = llog_init_handle(env, handle, 0, NULL);
	if (rc)
		GOTO(out_close, rc = -ENOENT);

	switch (cmd) {
	case OBD_IOC_LLOG_INFO: {
		int	 l;
		int	 remains = data->ioc_inllen2 +
				   cfs_size_round(data->ioc_inllen1);
		char	*out = data->ioc_bulk;

		l = snprintf(out, remains,
			     "logid:	    #"DOSTID"#%08x\n"
			     "flags:	    %x (%s)\n"
			     "records count:    %d\n"
			     "last index:       %d\n",
			     POSTID(&handle->lgh_id.lgl_oi),
			     handle->lgh_id.lgl_ogen,
			     handle->lgh_hdr->llh_flags,
			     handle->lgh_hdr->llh_flags &
			     LLOG_F_IS_CAT ? "cat" : "plain",
			     handle->lgh_hdr->llh_count,
			     handle->lgh_last_idx);
		out += l;
		remains -= l;
		if (remains <= 0) {
			CERROR("%s: not enough space for log header info\n",
			       ctxt->loc_obd->obd_name);
			rc = -ENOSPC;
		}
		break;
	}
	case OBD_IOC_LLOG_CHECK:
		LASSERT(data->ioc_inllen1 > 0);
		rc = llog_process(env, handle, llog_check_cb, data, NULL);
		if (rc == -LLOG_EEMPTY)
			rc = 0;
		else if (rc)
			GOTO(out_close, rc);
		break;
	case OBD_IOC_LLOG_PRINT:
		LASSERT(data->ioc_inllen1 > 0);
		rc = llog_process(env, handle, llog_print_cb, data, NULL);
		if (rc == -LLOG_EEMPTY)
			rc = 0;
		else if (rc)
			GOTO(out_close, rc);
		break;
	case OBD_IOC_LLOG_CANCEL: {
		struct llog_cookie cookie;
		struct llog_logid plain;
		char *endp;

		cookie.lgc_index = simple_strtoul(data->ioc_inlbuf3, &endp, 0);
		if (*endp != '\0')
			GOTO(out_close, rc = -EINVAL);

		if (handle->lgh_hdr->llh_flags & LLOG_F_IS_PLAIN) {
			rc = llog_cancel_rec(NULL, handle, cookie.lgc_index);
			GOTO(out_close, rc);
		} else if (!(handle->lgh_hdr->llh_flags & LLOG_F_IS_CAT)) {
			GOTO(out_close, rc = -EINVAL);
		}

		if (data->ioc_inlbuf2 == NULL) /* catalog but no logid */
			GOTO(out_close, rc = -ENOTTY);

		rc = str2logid(&plain, data->ioc_inlbuf2, data->ioc_inllen2);
		if (rc)
			GOTO(out_close, rc);
		cookie.lgc_lgl = plain;
		rc = llog_cat_cancel_records(env, handle, 1, &cookie);
		if (rc)
			GOTO(out_close, rc);
		break;
	}
	case OBD_IOC_LLOG_REMOVE: {
		struct llog_logid plain;

		if (handle->lgh_hdr->llh_flags & LLOG_F_IS_PLAIN) {
			rc = llog_destroy(env, handle);
			GOTO(out_close, rc);
		} else if (!(handle->lgh_hdr->llh_flags & LLOG_F_IS_CAT)) {
			GOTO(out_close, rc = -EINVAL);
		}

		if (data->ioc_inlbuf2 > 0) {
			/* remove indicate log from the catalog */
			rc = str2logid(&plain, data->ioc_inlbuf2,
				       data->ioc_inllen2);
			if (rc)
				GOTO(out_close, rc);
			rc = llog_remove_log(env, handle, &plain);
		} else {
			/* remove all the log of the catalog */
			rc = llog_process(env, handle, llog_delete_cb, NULL,
					  NULL);
			if (rc)
				GOTO(out_close, rc);
		}
		break;
	}
	default:
		CERROR("%s: Unknown ioctl cmd %#x\n",
		       ctxt->loc_obd->obd_name, cmd);
		GOTO(out_close, rc = -ENOTTY);
	}

out_close:
	if (handle->lgh_hdr &&
	    handle->lgh_hdr->llh_flags & LLOG_F_IS_CAT)
		llog_cat_close(env, handle);
	else
		llog_close(env, handle);
	return rc;
}
Exemplo n.º 9
0
int llog_origin_handle_cancel(struct ptlrpc_request *req)
{
        struct obd_device *obd = req->rq_export->exp_obd;
        int num_cookies, rc = 0, err, i, failed = 0;
        struct obd_device *disk_obd;
        struct llog_cookie *logcookies;
        struct llog_ctxt *ctxt = NULL;
        struct lvfs_run_ctxt saved;
        struct llog_handle *cathandle;
        struct inode *inode;
        void *handle;
        ENTRY;

        logcookies = req_capsule_client_get(&req->rq_pill, &RMF_LOGCOOKIES);
        num_cookies = req_capsule_get_size(&req->rq_pill, &RMF_LOGCOOKIES,
                                           RCL_CLIENT) / sizeof(*logcookies);
        if (logcookies == NULL || num_cookies == 0) {
                DEBUG_REQ(D_HA, req, "No llog cookies sent");
                RETURN(-EFAULT);
        }

        ctxt = llog_get_context(obd, logcookies->lgc_subsys);
        if (ctxt == NULL)
                RETURN(-ENODEV);

        disk_obd = ctxt->loc_exp->exp_obd;
        push_ctxt(&saved, &disk_obd->obd_lvfs_ctxt, NULL);
        for (i = 0; i < num_cookies; i++, logcookies++) {
                cathandle = ctxt->loc_handle;
                LASSERT(cathandle != NULL);
                inode = cathandle->lgh_file->f_dentry->d_inode;

                handle = fsfilt_start_log(disk_obd, inode,
                                          FSFILT_OP_CANCEL_UNLINK, NULL, 1);
                if (IS_ERR(handle)) {
                        CERROR("fsfilt_start_log() failed: %ld\n",
                               PTR_ERR(handle));
                        GOTO(pop_ctxt, rc = PTR_ERR(handle));
                }

                rc = llog_cat_cancel_records(cathandle, 1, logcookies);

                /*
                 * Do not raise -ENOENT errors for resent rpcs. This rec already
                 * might be killed.
                 */
                if (rc == -ENOENT &&
                    (lustre_msg_get_flags(req->rq_reqmsg) & MSG_RESENT)) {
                        /*
                         * Do not change this message, reply-single.sh test_59b
                         * expects to find this in log.
                         */
                        CDEBUG(D_RPCTRACE, "RESENT cancel req %p - ignored\n",
                               req);
                        rc = 0;
                } else if (rc == 0) {
                        CDEBUG(D_RPCTRACE, "Canceled %d llog-records\n",
                               num_cookies);
                }

                err = fsfilt_commit(disk_obd, inode, handle, 0);
                if (err) {
                        CERROR("Error committing transaction: %d\n", err);
                        if (!rc)
                                rc = err;
                        failed++;
                        GOTO(pop_ctxt, rc);
                } else if (rc)
                        failed++;
        }
        GOTO(pop_ctxt, rc);
pop_ctxt:
        pop_ctxt(&saved, &disk_obd->obd_lvfs_ctxt, NULL);
        if (rc)
                CERROR("Cancel %d of %d llog-records failed: %d\n",
                       failed, num_cookies, rc);

        llog_ctxt_put(ctxt);
        return rc;
}
Exemplo n.º 10
0
/* Test catalogue additions */
static int llog_test_4(struct obd_device *obd)
{
        struct llog_handle *cath;
        char name[10];
        int rc, i, buflen;
        struct llog_mini_rec lmr;
        struct llog_cookie cookie;
        struct llog_ctxt *ctxt = llog_get_context(obd, LLOG_TEST_ORIG_CTXT);
        int num_recs = 0;
        char *buf;
        struct llog_rec_hdr rec;

        ENTRY;

        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_create(ctxt, &cath, NULL, name);
        if (rc) {
                CERROR("1a: llog_create with name %s failed: %d\n", name, rc);
                GOTO(out, rc);
        }
        llog_init_handle(cath, LLOG_F_IS_CAT, &uuid);
        num_recs++;
        cat_logid = cath->lgh_id;

        CWARN("4b: write 1 record into the catalog\n");
        rc = llog_cat_add_rec(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++;
        if ((rc = verify_handle("4b", cath, 2)))
                GOTO(ctxt_release, rc);

        if ((rc = verify_handle("4b", cath->u.chd.chd_current_log, num_recs)))
                GOTO(ctxt_release, rc);

        CWARN("4c: cancel 1 log record\n");
        rc = llog_cat_cancel_records(cath, 1, &cookie);
        if (rc) {
                CERROR("4c: cancel 1 catalog based record failed: %d\n", rc);
                GOTO(out, rc);
        }
        num_recs--;

        if ((rc = verify_handle("4c", cath->u.chd.chd_current_log, num_recs)))
                GOTO(ctxt_release, rc);

        CWARN("4d: write 40,000 more log records\n");
        for (i = 0; i < 40000; i++) {
                rc = llog_cat_add_rec(cath, &lmr.lmr_hdr, NULL, NULL);
                if (rc) {
                        CERROR("4d: write 40000 records failed at #%d: %d\n",
                               i + 1, rc);
                        GOTO(out, rc);
                }
                num_recs++;
        }

        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_rec(cath, &rec, NULL, buf);
                if (rc) {
                        CERROR("4e: write 5 records failed at #%d: %d\n",
                               i + 1, rc);
                        OBD_FREE(buf, buflen);
                        GOTO(out, rc);
                }
                num_recs++;
        }
        OBD_FREE(buf, buflen);

 out:
        CWARN("4f: put newly-created catalog\n");
        rc = llog_cat_put(cath);
ctxt_release:
        llog_ctxt_put(ctxt);
        if (rc)
                CERROR("1b: close log %s failed: %d\n", name, rc);
        RETURN(rc);
}