CERROR("changelog init failed: %d\n", rc);
return rc;
}
- CDEBUG(D_INODE, "changelog starting index="LPU64"\n",
+ CDEBUG(D_IOCTL, "changelog starting index="LPU64"\n",
mdd->mdd_cl.mc_index);
/* Find last changelog user id */
CERROR("changelog user init failed: %d\n", rc);
return rc;
}
- return 0;
+
+ /* If we have registered users, assume we want changelogs on */
+ if (mdd->mdd_cl.mc_lastuser > 0)
+ rc = mdd_changelog_on(mdd, 1);
+
+ return rc;
}
static int mdd_changelog_init(const struct lu_env *env, struct mdd_device *mdd)
mdd->mdd_cl.mc_flags = 0;
}
+/* Start / stop recording */
+int mdd_changelog_on(struct mdd_device *mdd, int on)
+{
+ int rc = 0;
+
+ if ((on == 1) && ((mdd->mdd_cl.mc_flags & CLM_ON) == 0)) {
+ LCONSOLE_INFO("%s: changelog on\n", mdd2obd_dev(mdd)->obd_name);
+ if (mdd->mdd_cl.mc_flags & CLM_ERR) {
+ CERROR("Changelogs cannot be enabled due to error "
+ "condition (see %s log).\n",
+ mdd2obd_dev(mdd)->obd_name);
+ rc = -ESRCH;
+ } else {
+ spin_lock(&mdd->mdd_cl.mc_lock);
+ mdd->mdd_cl.mc_flags |= CLM_ON;
+ spin_unlock(&mdd->mdd_cl.mc_lock);
+ rc = mdd_changelog_write_header(mdd, CLM_START);
+ }
+ } else if ((on == 0) && ((mdd->mdd_cl.mc_flags & CLM_ON) == CLM_ON)) {
+ LCONSOLE_INFO("%s: changelog off\n",mdd2obd_dev(mdd)->obd_name);
+ rc = mdd_changelog_write_header(mdd, CLM_FINI);
+ spin_lock(&mdd->mdd_cl.mc_lock);
+ mdd->mdd_cl.mc_flags &= ~CLM_ON;
+ spin_unlock(&mdd->mdd_cl.mc_lock);
+ }
+ return rc;
+}
+
/** Add a changelog entry \a rec to the changelog llog
* \param mdd
* \param rec
RETURN(-ENOMEM);
}
+ /* Assume we want it on since somebody registered */
+ rc = mdd_changelog_on(mdd, 1);
+ if (rc)
+ GOTO(out, rc);
+
rec->cur_hdr.lrh_len = sizeof(*rec);
rec->cur_hdr.lrh_type = CHANGELOG_USER_REC;
- rec->cur_endrec = 0ULL;
spin_lock(&mdd->mdd_cl.mc_user_lock);
if (mdd->mdd_cl.mc_lastuser == (unsigned int)(-1)) {
spin_unlock(&mdd->mdd_cl.mc_user_lock);
GOTO(out, rc = -EOVERFLOW);
}
*id = rec->cur_id = ++mdd->mdd_cl.mc_lastuser;
+ rec->cur_endrec = mdd->mdd_cl.mc_index;
spin_unlock(&mdd->mdd_cl.mc_user_lock);
+
rc = llog_add(ctxt, &rec->cur_hdr, NULL, NULL, 0);
- CDEBUG(D_INODE, "Registered changelog user %d\n", *id);
+ CDEBUG(D_IOCTL, "Registered changelog user %d\n", *id);
out:
OBD_FREE_PTR(rec);
llog_ctxt_put(ctxt);
__u64 mcud_minrec; /**< lowest changelog recno still referenced */
__u32 mcud_id;
__u32 mcud_minid; /**< user id with lowest rec reference */
+ __u32 mcud_usercount;
int mcud_found:1;
};
+#define MCUD_UNREGISTER -1LL
/** Two things:
* 1. Find the smallest record everyone is willing to purge
rec = (struct llog_changelog_user_rec *)hdr;
- /* If we have a new endrec for this id, use it for the min check */
+ 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);
/* Update this user's record */
mcud->mcud_found = 1;
- /* Special case: unregister this user if endrec == -1 */
- if (mcud->mcud_endrec == -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(llh->u.phd.phd_cat_handle,
1, &cookie);
+ if (rc == 0)
+ mcud->mcud_usercount--;
RETURN(rc);
}
CDEBUG(D_IOCTL, "Purge request: id=%d, endrec="LPD64"\n", id, endrec);
+ data.mcud_id = id;
+ data.mcud_minid = 0;
+ data.mcud_minrec = 0;
+ data.mcud_usercount = 0;
+ data.mcud_endrec = endrec;
+ spin_lock(&mdd->mdd_cl.mc_lock);
+ endrec = mdd->mdd_cl.mc_index;
+ spin_unlock(&mdd->mdd_cl.mc_lock);
+ if ((data.mcud_endrec == 0) ||
+ ((data.mcud_endrec > endrec) &&
+ (data.mcud_endrec != MCUD_UNREGISTER)))
+ data.mcud_endrec = endrec;
+
ctxt = llog_get_context(mdd2obd_dev(mdd),LLOG_CHANGELOG_USER_ORIG_CTXT);
if (ctxt == NULL)
return -ENXIO;
LASSERT(ctxt->loc_handle->lgh_hdr->llh_flags & LLOG_F_IS_CAT);
- data.mcud_id = id;
- data.mcud_endrec = endrec;
- data.mcud_minid = 0;
- data.mcud_minrec = 0;
rc = llog_cat_process(ctxt->loc_handle, mdd_changelog_user_purge_cb,
(void *)&data, 0, 0);
if ((rc >= 0) && (data.mcud_minrec > 0)) {
- CDEBUG(D_INODE, "Purging CL entries up to "LPD64
+ CDEBUG(D_IOCTL, "Purging changelog entries up to "LPD64
", referenced by "CHANGELOG_USER_PREFIX"%d\n",
data.mcud_minrec, data.mcud_minid);
rc = mdd_changelog_llog_cancel(mdd, data.mcud_minrec);
rc);
}
+ llog_ctxt_put(ctxt);
+
if (!data.mcud_found) {
CWARN("No entry for user %d. Last changelog reference is "
LPD64" by changelog user %d\n", data.mcud_id,
rc = -ENOENT;
}
- llog_ctxt_put(ctxt);
+ if (!rc && data.mcud_usercount == 0)
+ /* No more users; turn changelogs off */
+ rc = mdd_changelog_on(mdd, 0);
+
RETURN (rc);
}
rc = mdd_changelog_user_register(mdd, &data->ioc_u32_1);
break;
case OBD_IOC_CHANGELOG_DEREG:
- rc = mdd_changelog_user_purge(mdd, data->ioc_u32_1, -1);
+ rc = mdd_changelog_user_purge(mdd, data->ioc_u32_1,
+ MCUD_UNREGISTER);
break;
default:
rc = -EOPNOTSUPP;
struct thandle *handle);
int mdd_changelog_llog_cancel(struct mdd_device *mdd, long long endrec);
int mdd_changelog_write_header(struct mdd_device *mdd, int markerflags);
+int mdd_changelog_on(struct mdd_device *mdd, int on);
/* mdd_permission.c */
#define mdd_cap_t(x) (x)
if (kernbuf[count - 1] == '\n')
kernbuf[count - 1] = '\0';
+ /* Forced on/off/purge rec, independent of changelog users! */
if (strcmp(kernbuf, "on") == 0) {
- LCONSOLE_INFO("changelog on\n");
- if (mdd->mdd_cl.mc_flags & CLM_ERR) {
- CERROR("Changelogs cannot be enabled due to error "
- "condition.\n");
- } else {
- spin_lock(&mdd->mdd_cl.mc_lock);
- mdd->mdd_cl.mc_flags |= CLM_ON;
- spin_unlock(&mdd->mdd_cl.mc_lock);
- rc = mdd_changelog_write_header(mdd, CLM_START);
- if (rc)
- return rc;
- }
+ rc = mdd_changelog_on(mdd, 1);
} else if (strcmp(kernbuf, "off") == 0) {
- LCONSOLE_INFO("changelog off\n");
- rc = mdd_changelog_write_header(mdd, CLM_FINI);
- if (rc)
- return rc;
- spin_lock(&mdd->mdd_cl.mc_lock);
- mdd->mdd_cl.mc_flags &= ~CLM_ON;
- spin_unlock(&mdd->mdd_cl.mc_lock);
+ rc = mdd_changelog_on(mdd, 0);
} else {
/* purge to an index */
long long unsigned endrec;
LCONSOLE_INFO("changelog purge to %llu\n", endrec);
rc = mdd_changelog_llog_cancel(mdd, endrec);
- if (rc < 0)
- return rc;
}
+ if (rc < 0)
+ return rc;
return count;
out_usage:
run_test 59 "Read cancel race on client eviction"
err17935 () {
+ # we assume that all md changes are in the MDT0 changelog
if [ $MDSCOUNT -gt 1 ]; then
error_ignore 17935 $*
else
}
test_60() {
- remote_mds && { skip "remote MDS" && return 0; }
+ MDT0=$($LCTL get_param -n mdc.*.mds_server_uuid | \
+ awk '{gsub(/_UUID/,""); print $1}' | head -1)
NUM_FILES=15000
mkdir -p $DIR/$tdir
- # Enable and clear changelog
- $LCTL conf_param ${mds1_svc}.mdd.changelog=on
- $LCTL set_param -n mdd.*.changelog on
- $LFS changelog_clear $FSNAME 0
+ # Register (and start) changelog
+ USER=$(do_facet $SINGLEMDS lctl --device $MDT0 changelog_register -n)
+ echo "Registered as $MDT0 changelog user $USER"
- # Create NUM_FILES in the background
+ # Generate a large number of changelog entries
createmany -o $DIR/$tdir/$tfile $NUM_FILES
sync
sleep 5
CLIENT_PID=$!
sleep 1
- # Failover the MDS while creates are happening
+ # Failover the MDS while unlinks are happening
facet_failover $SINGLEMDS
# Wait for unlinkmany to finish
wait $CLIENT_PID
- # Check if NUM_FILES create/unlink events were recorded
+ # Check if all the create/unlink events were recorded
# in the changelog
- $LFS changelog $FSNAME >> $DIR/$tdir/changelog
+ $LFS changelog $MDT0 >> $DIR/$tdir/changelog
local cl_count=$(grep UNLNK $DIR/$tdir/changelog | wc -l)
- echo "$cl_count unlinks in changelog"
-
- [ $cl_count -eq $NUM_FILES ] || err17935 "Recorded ${cl_count} unlinks out
-of $NUM_FILES"
-
- # Also make sure we can clear large changelogs
- lctl set_param -n mdd.*.changelog off
- $LFS changelog_clear $FSNAME 0
-
- cl_count=$($LFS changelog $FSNAME | wc -l)
- [ $cl_count -eq 1 ] || error "Changelog not empty: $cl_count entries"
+ echo "$cl_count unlinks in $MDT0 changelog"
+
+ do_facet $SINGLEMDS lctl --device $MDT0 changelog_deregister $USER
+ USERS=$(( $(do_facet $SINGLEMDS lctl get_param -n \
+ mdd.$MDT0.changelog_users | wc -l) - 2 ))
+ if [ $USERS -eq 0 ]; then
+ [ $cl_count -eq $NUM_FILES ] || \
+ err17935 "Recorded ${cl_count} unlinks out of $NUM_FILES"
+ # Also make sure we can clear large changelogs
+ cl_count=$($LFS changelog $FSNAME | wc -l)
+ [ $cl_count -le 2 ] || \
+ error "Changelog not empty: $cl_count entries"
+ else
+ # If there are other users, there may be other unlinks in the log
+ [ $cl_count -ge $NUM_FILES ] || \
+ err17935 "Recorded ${cl_count} unlinks out of $NUM_FILES"
+ echo "$USERS other changelog users; can't verify clear"
+ fi
}
run_test 60 "Add Changelog entries during MDS failover"
fi
}
test_160() {
- do_facet $SINGLEMDS lctl set_param mdd.$MDT0.changelog on
USER=$(do_facet $SINGLEMDS lctl --device $MDT0 changelog_register -n)
echo "Registered as changelog user $USER"
do_facet $SINGLEMDS lctl get_param -n mdd.$MDT0.changelog_users | \
ln -s $DIR/$tdir/pics/2008/portland.jpg $DIR/$tdir/pics/desktop.jpg
rm $DIR/$tdir/pics/desktop.jpg
- # verify contents
$LFS changelog $MDT0 | tail -5
+
+ echo "verifying changelog mask"
+ $LCTL set_param mdd.$MDT0.changelog_mask="-mkdir"
+ mkdir -p $DIR/$tdir/pics/2009/sofia
+ $LCTL set_param mdd.$MDT0.changelog_mask="+mkdir"
+ mkdir $DIR/$tdir/pics/2009/zachary
+ DIRS=$($LFS changelog $MDT0 | tail -5 | grep -c MKDIR)
+ [ $DIRS -eq 1 ] || err17935 "changelog mask count $DIRS != 1"
+
+ # verify contents
echo "verifying target fid"
fidc=$($LFS changelog $MDT0 | grep timestamp | grep "CREAT" | \
tail -1 | awk '{print $5}')
[ "$fidc" == "p=$fidf" ] || \
err17935 "pfid in changelog $fidc != dir fid $fidf"
- echo "verifying user clear"
- USERS=$(( $(do_facet $SINGLEMDS lctl get_param -n \
- mdd.$MDT0.changelog_users | wc -l) - 2 ))
- FIRST_REC=$($LFS changelog $MDT0 | head -1 | awk '{print $1}')
- $LFS changelog_clear $MDT0 $USER $(($FIRST_REC + 5))
- USER_REC=$(do_facet $SINGLEMDS lctl get_param -n \
+ USER_REC1=$(do_facet $SINGLEMDS lctl get_param -n \
mdd.$MDT0.changelog_users | grep $USER | awk '{print $2}')
- [ $USER_REC == $(($FIRST_REC + 5)) ] || \
- err17935 "user index should be $(($FIRST_REC + 5)); is $USER_REC"
- CLEAR_REC=$($LFS changelog $MDT0 | head -1 | awk '{print $1}')
- [ $CLEAR_REC == $(($FIRST_REC + 6)) -o $USERS -gt 1 ] || \
- err17935 "first index should be $(($FIRST_REC + 6)); is $PURGE_REC"
+ $LFS changelog_clear $MDT0 $USER $(($USER_REC1 + 5))
+ USER_REC2=$(do_facet $SINGLEMDS lctl get_param -n \
+ mdd.$MDT0.changelog_users | grep $USER | awk '{print $2}')
+ echo "verifying user clear: $(( $USER_REC1 + 5 )) == $USER_REC2"
+ [ $USER_REC2 == $(($USER_REC1 + 5)) ] || \
+ err17935 "user index should be $(($USER_REC1 + 5)); is $USER_REC2"
+
+ MIN_REC=$(do_facet $SINGLEMDS lctl get_param mdd.$MDT0.changelog_users | \
+ awk 'min == "" || $2 < min {min = $2}; END {print min}')
+ FIRST_REC=$($LFS changelog $MDT0 | head -1 | awk '{print $1}')
+ echo "verifying min purge: $(( $MIN_REC + 1 )) == $FIRST_REC"
+ [ $FIRST_REC == $(($MIN_REC + 1)) ] || \
+ err17935 "first index should be $(($MIN_REC + 1)); is $FIRST_REC"
echo "verifying user deregister"
do_facet $SINGLEMDS lctl --device $MDT0 changelog_deregister $USER
do_facet $SINGLEMDS lctl get_param -n mdd.$MDT0.changelog_users | \
grep -q $USER && error "User $USER still found in changelog_users"
- [ $USERS -eq 1 ] && \
- do_facet $SINGLEMDS lctl set_param mdd.$MDT0.changelog off || true
+ USERS=$(( $(do_facet $SINGLEMDS lctl get_param -n \
+ mdd.$MDT0.changelog_users | wc -l) - 2 ))
+ if [ $USERS -eq 0 ]; then
+ LAST_REC1=$(do_facet $SINGLEMDS lctl get_param -n \
+ mdd.$MDT0.changelog_users | head -1 | awk '{print $3}')
+ touch $DIR/$tdir/chloe
+ LAST_REC2=$(do_facet $SINGLEMDS lctl get_param -n \
+ mdd.$MDT0.changelog_users | head -1 | awk '{print $3}')
+ echo "verify changelogs are off if we were the only user: $LAST_REC1 == $LAST_REC2"
+ [ $LAST_REC1 == $LAST_REC2 ] || error "changelogs not off"
+ else
+ echo "$USERS other changelog users; can't verify off"
+ fi
}
run_test 160 "changelog sanity"