#define OBD_FAIL_CAT_RECORDS 0x1312
#define OBD_FAIL_CAT_FREE_RECORDS 0x1313
#define OBD_FAIL_TIME_IN_CHLOG_USER 0x1314
+#define CFS_FAIL_CHLOG_USER_REG_UNREG_RACE 0x1315
#define OBD_FAIL_LLITE 0x1400
#define OBD_FAIL_LLITE_FAULT_TRUNC_RACE 0x1401
spin_lock(&mdd->mdd_cl.mc_user_lock);
mdd->mdd_cl.mc_lastuser = rec->cur_id;
+ mdd->mdd_cl.mc_users++;
if (rec->cur_endrec > mdd->mdd_cl.mc_index)
mdd->mdd_cl.mc_index = rec->cur_endrec;
spin_unlock(&mdd->mdd_cl.mc_user_lock);
RETURN(-ENOMEM);
}
- /* Assume we want it on since somebody registered */
- rc = mdd_changelog_on(env, mdd);
- if (rc)
- GOTO(out, rc);
+ CFS_RACE(CFS_FAIL_CHLOG_USER_REG_UNREG_RACE);
rec->cur_hdr.lrh_len = sizeof(*rec);
rec->cur_hdr.lrh_type = CHANGELOG_USER_REC;
GOTO(out, rc = -EOVERFLOW);
}
*id = rec->cur_id = ++mdd->mdd_cl.mc_lastuser;
+ mdd->mdd_cl.mc_users++;
rec->cur_endrec = mdd->mdd_cl.mc_index;
rec->cur_time = (__u32)get_seconds();
spin_unlock(&mdd->mdd_cl.mc_user_lock);
rc = llog_cat_add(env, ctxt->loc_handle, &rec->cur_hdr, NULL);
+ if (rc) {
+ CWARN("%s: Failed to register changelog user %d: rc=%d\n",
+ mdd2obd_dev(mdd)->obd_name, *id, rc);
+ spin_lock(&mdd->mdd_cl.mc_user_lock);
+ mdd->mdd_cl.mc_users--;
+ spin_unlock(&mdd->mdd_cl.mc_user_lock);
+ GOTO(out, rc);
+ }
CDEBUG(D_IOCTL, "Registered changelog user %d\n", *id);
+
+ /* Assume we want it on since somebody registered */
+ rc = mdd_changelog_on(env, mdd);
+ if (rc)
+ GOTO(out, rc);
+
out:
OBD_FREE_PTR(rec);
llog_ctxt_put(ctxt);
}
struct mdd_changelog_user_purge {
+ struct mdd_device *mcup_mdd;
__u32 mcup_id;
__u32 mcup_usercount;
__u64 mcup_minrec;
if (rc == 0) {
mcup->mcup_found = true;
mcup->mcup_usercount--;
+ spin_lock(&mcup->mcup_mdd->mdd_cl.mc_user_lock);
+ mcup->mcup_mdd->mdd_cl.mc_users--;
+ spin_unlock(&mcup->mcup_mdd->mdd_cl.mc_user_lock);
}
RETURN(rc);
struct mdd_device *mdd, __u32 id)
{
struct mdd_changelog_user_purge mcup = {
+ .mcup_mdd = mdd,
.mcup_id = id,
.mcup_found = false,
.mcup_usercount = 0,
mdd_changelog_user_purge_cb, &mcup,
0, 0);
+ if ((rc == 0) && (mcup.mcup_usercount == 0)) {
+ spin_lock(&mdd->mdd_cl.mc_user_lock);
+ if (mdd->mdd_cl.mc_users == 0) {
+ /* No more users; turn changelogs off */
+ CDEBUG(D_IOCTL, "turning off changelogs\n");
+ rc = mdd_changelog_off(env, mdd);
+ }
+ spin_unlock(&mdd->mdd_cl.mc_user_lock);
+ }
+
if ((rc == 0) && mcup.mcup_found) {
CDEBUG(D_IOCTL, "%s: Purging changelog entries for user %d "
"record=%llu\n",
GOTO(out, rc = -ENOENT);
}
- if ((rc == 0) && (mcup.mcup_usercount == 0)) {
- /* No more users; turn changelogs off */
- CDEBUG(D_IOCTL, "turning off changelogs\n");
- rc = mdd_changelog_off(env, mdd);
- }
+ CFS_RACE(CFS_FAIL_CHLOG_USER_REG_UNREG_RACE);
EXIT;
out:
ktime_t mc_starttime;
spinlock_t mc_user_lock;
int mc_lastuser;
+ int mc_users; /* registered users number */
struct task_struct *mc_gc_task;
time64_t mc_gc_time;
unsigned int mc_deniednext; /* interval for recording denied
}
run_test 160g "changelog garbage collect (old users)"
+test_160h() {
+
+ local mdts=$(comma_list $(mdts_nodes))
+
+ changelog_register || error "first changelog_register failed"
+
+ # generate some changelog records to accumulate on each MDT
+ test_mkdir -c $MDSCOUNT $DIR/$tdir || error "mkdir $tdir failed"
+ createmany -m $DIR/$tdir/$tfile $((MDSCOUNT * 2)) ||
+ error "create $DIR/$tdir/$tfile failed"
+
+ # check changelogs have been generated
+ local nbcl=$(changelog_dump | wc -l)
+ [[ $nbcl -eq 0 ]] && error "no changelogs found"
+
+ # simulate race between register and unregister
+ # XXX as fail_loc is set per-MDS, with DNE configs the race
+ # simulation will only occur for one MDT per MDS and for the
+ # others the normal race scenario will take place
+ #define CFS_FAIL_CHLOG_USER_REG_UNREG_RACE 0x1315
+ do_nodes $mdts $LCTL set_param fail_loc=0x10001315
+ do_nodes $mdts $LCTL set_param fail_val=1
+
+ # unregister 1st user
+ changelog_deregister &
+ local pid1=$!
+ # wait some time for deregister work to reach race rdv
+ sleep 2
+ # register 2nd user
+ changelog_register || error "2nd user register failed"
+
+ wait $pid1 || error "1st user deregister failed"
+
+ local i
+ local last_rec
+ declare -A LAST_REC
+ for i in $(seq $MDSCOUNT); do
+ if changelog_users mds$i | grep "^cl"; then
+ # make sure new records are added with one user present
+ LAST_REC[mds$i]=$(changelog_users $SINGLEMDS |
+ awk '/^current.index:/ { print $NF }')
+ else
+ error "mds$i has no user registered"
+ fi
+ done
+
+ # generate more changelog records to accumulate on each MDT
+ createmany -m $DIR/$tdir/${tfile}bis $((MDSCOUNT * 2)) ||
+ error "create $DIR/$tdir/${tfile}bis failed"
+
+ for i in $(seq $MDSCOUNT); do
+ last_rec=$(changelog_users $SINGLEMDS |
+ awk '/^current.index:/ { print $NF }')
+ echo "verify changelogs are on: $last_rec != ${LAST_REC[mds$i]}"
+ [ $last_rec != ${LAST_REC[mds$i]} ] ||
+ error "changelogs are off on mds$i"
+ done
+}
+run_test 160h "changelog user register/unregister race"
+
test_161a() {
[ $PARALLEL == "yes" ] && skip "skip parallel run"
changelog_deregister() {
local cl_user
+ # bash assoc arrays do not guarantee to list keys in created order
+ # so reorder to get same order than in changelog_register()
+ local cl_facets=$(echo "${!CL_USERS[@]}" | tr " " "\n" | sort |
+ tr "\n" " ")
- for facet in "${!CL_USERS[@]}"; do
+ for facet in $cl_facets; do
for cl_user in ${CL_USERS[$facet]}; do
__changelog_deregister $facet $cl_user || return $?
done
# users.
changelog_clear() {
local rc
- for facet in ${!CL_USERS[@]}; do
+ # bash assoc arrays do not guarantee to list keys in created order
+ # so reorder to get same order than in changelog_register()
+ local cl_facets=$(echo "${!CL_USERS[@]}" | tr " " "\n" | sort |
+ tr "\n" " ")
+
+ for facet in $cl_facets; do
for cl_user in ${CL_USERS[$facet]}; do
__changelog_clear $facet $cl_user $1 || rc=${rc:-$?}
done