if (attr->cra_type == CRT_WRITE) {
valid_flags |= OBD_MD_FLMTIME | OBD_MD_FLCTIME;
obdo_set_o_projid(oa, ll_i2info(inode)->lli_projid);
+ } else if (attr->cra_type == CRT_READ) {
+ valid_flags |= OBD_MD_FLATIME;
}
obdo_from_inode(oa, inode, valid_flags & attr->cra_flags);
obdo_set_parent_fid(oa, &ll_i2info(inode)->lli_fid);
LUSTRE_RW_ATTR(precreate_batch);
/**
+ * Show number of seconds to delay atime
+ *
+ * \param[in] m seq_file handle
+ * \param[in] data unused for single entry
+ *
+ * \retval 0 on success
+ * \retval negative value on error
+ */
+static ssize_t atime_diff_show(struct kobject *kobj, struct attribute *attr,
+ char *buf)
+{
+ struct obd_device *obd = container_of(kobj, struct obd_device,
+ obd_kset.kobj);
+ struct ofd_device *ofd = ofd_dev(obd->obd_lu_dev);
+
+ return snprintf(buf, PAGE_SIZE, "%lld\n", ofd->ofd_atime_diff);
+}
+
+/**
+ * Change number of seconds to delay atime
+ *
+ * \param[in] file proc file
+ * \param[in] buffer string which represents maximum number
+ * \param[in] count \a buffer length
+ * \param[in] off unused for single entry
+ *
+ * \retval \a count on success
+ * \retval negative number on error
+ */
+static ssize_t atime_diff_store(struct kobject *kobj, struct attribute *attr,
+ const char *buffer, size_t count)
+{
+ struct obd_device *obd = container_of(kobj, struct obd_device,
+ obd_kset.kobj);
+ struct ofd_device *ofd = ofd_dev(obd->obd_lu_dev);
+ unsigned int val;
+ int rc;
+
+ rc = kstrtouint(buffer, 0, &val);
+ if (rc)
+ return rc;
+
+ if (val > 86400)
+ return -EINVAL;
+
+ ofd->ofd_atime_diff = val;
+ return count;
+}
+LUSTRE_RW_ATTR(atime_diff);
+
+/**
* Show the last used ID for each FID sequence used by OFD.
*
* \param[in] m seq_file handle
&lustre_attr_seqs_allocated.attr,
&lustre_attr_grant_precreate.attr,
&lustre_attr_precreate_batch.attr,
+ &lustre_attr_atime_diff.attr,
&lustre_attr_degraded.attr,
&lustre_attr_fstype.attr,
&lustre_attr_no_precreate.attr,
if (tgd->tgd_osfs.os_bsize * tgd->tgd_osfs.os_blocks <
OFD_PRECREATE_SMALL_FS)
m->ofd_precreate_batch = OFD_PRECREATE_BATCH_SMALL;
+ m->ofd_atime_diff = OFD_DEF_ATIME_DIFF;
rc = ofd_fs_setup(env, m, obd);
if (rc)
#define OFD_SOFT_SYNC_LIMIT_DEFAULT 16
+/*
+ * update atime if on-disk value older than client's one
+ * by OFD_ATIME_DIFF or more
+ */
+#define OFD_DEF_ATIME_DIFF 0 /* disabled */
+
/* request stats */
enum {
LPROC_OFD_STATS_READ = 0,
struct attribute *ofd_read_cache_enable;
struct attribute *ofd_read_cache_max_filesize;
struct attribute *ofd_write_cache_enable;
+ time64_t ofd_atime_diff;
};
static inline struct ofd_device *ofd_dev(struct lu_device *d)
struct lu_object_header ofo_header;
struct dt_object ofo_obj;
struct filter_fid ofo_ff;
+ time64_t ofo_atime_ondisk;
unsigned int ofo_pfid_checking:1,
ofo_pfid_verified:1;
};
}
+/*
+ * Lazy ATIME update to refresh atime every ofd_atime_diff
+ * seconds so that external scanning tool can see it actual
+ * within that period and be able to identify accessed files
+ */
+static void ofd_handle_atime(const struct lu_env *env, struct ofd_device *ofd,
+ struct ofd_object *fo, time64_t atime)
+{
+ struct lu_attr *la;
+ struct dt_object *o;
+ struct thandle *th;
+ int rc;
+
+ if (ofd->ofd_atime_diff == 0)
+ return;
+
+ la = &ofd_info(env)->fti_attr2;
+ o = ofd_object_child(fo);
+
+ if (unlikely(fo->ofo_atime_ondisk == 0)) {
+ rc = dt_attr_get(env, o, la);
+ if (unlikely(rc))
+ return;
+ LASSERT(la->la_valid & LA_ATIME);
+ if (la->la_atime == 0)
+ la->la_atime = la->la_mtime;
+ fo->ofo_atime_ondisk = la->la_atime;
+ }
+ if (atime - fo->ofo_atime_ondisk < ofd->ofd_atime_diff)
+ return;
+
+ /* atime hasn't been updated too long, update it */
+ fo->ofo_atime_ondisk = atime;
+
+ th = ofd_trans_create(env, ofd);
+ if (IS_ERR(th)) {
+ CERROR("%s: cannot create transaction: rc = %d\n",
+ ofd_name(ofd), (int)PTR_ERR(th));
+ return;
+ }
+
+ la->la_valid = LA_ATIME;
+ rc = dt_declare_attr_set(env, o, la, th);
+ if (rc)
+ GOTO(out_tx, rc);
+
+ rc = dt_trans_start_local(env, ofd->ofd_osd , th);
+ if (rc) {
+ CERROR("%s: cannot start transaction: rc = %d\n",
+ ofd_name(ofd), rc);
+ GOTO(out_tx, rc);
+ }
+
+ ofd_read_lock(env, fo);
+ if (ofd_object_exists(fo)) {
+ la->la_atime = fo->ofo_atime_ondisk;
+ rc = dt_attr_set(env, o, la, th);
+ }
+
+ ofd_read_unlock(env, fo);
+
+out_tx:
+ ofd_trans_stop(env, ofd, th, rc);
+}
+
/**
* Prepare buffers for read request processing.
*
ofd_info(env)->fti_obj = fo;
+ if (oa->o_valid & OBD_MD_FLATIME)
+ ofd_handle_atime(env, ofd, fo, oa->o_atime);
+
ofd_read_lock(env, fo);
if (!ofd_object_exists(fo))
GOTO(unlock, rc = -ENOENT);
GOTO(out_stop, rc);
}
+ /* don't update atime on disk if it is older */
+ if (la->la_valid & LA_ATIME && la->la_atime <= fo->ofo_atime_ondisk)
+ la->la_valid &= ~LA_ATIME;
+
if (la->la_valid) {
/* update [mac]time if needed */
rc = dt_declare_attr_set(env, o, la, th);
rc = dt_attr_set(env, o, la, th);
if (rc)
GOTO(out_unlock, rc);
+ if (la->la_valid & LA_ATIME)
+ fo->ofo_atime_ondisk = la->la_atime;
}
/* get attr to return */
lvb->lvb_atime = info->fti_attr.la_atime;
lvb->lvb_ctime = info->fti_attr.la_ctime;
- CDEBUG(D_DLMTRACE, "res: "DFID" initial lvb size: %llu, "
- "mtime: %#llx, blocks: %#llx\n",
- PFID(&info->fti_fid), lvb->lvb_size,
- lvb->lvb_mtime, lvb->lvb_blocks);
+ if (fo->ofo_atime_ondisk == 0)
+ fo->ofo_atime_ondisk = info->fti_attr.la_atime;
+
+ CDEBUG(D_DLMTRACE,
+ "res: "DFID" initial LVB size: %llu, mtime: %#llx, atime: %#llx, ctime: %#llx, blocks: %#llx\n",
+ PFID(&info->fti_fid), lvb->lvb_size, lvb->lvb_mtime,
+ lvb->lvb_atime, lvb->lvb_ctime, lvb->lvb_blocks);
info->fti_attr.la_valid = 0;
oa->o_mtime = lvb->lvb_mtime;
oa->o_valid |= OBD_MD_FLMTIME;
}
- if ((flags & OBD_MD_FLATIME) != 0) {
+ /* XXX:
+ * I don't understand this part, what for OSC resets atime just
+ * set by VVP layer to 0 so that OST gets 0 instead of actual
+ * atime, bzzz. please inspect this place with extra care.
+ */
+ if ((flags & OBD_MD_FLATIME) && lvb->lvb_atime > oa->o_atime) {
oa->o_atime = lvb->lvb_atime;
oa->o_valid |= OBD_MD_FLATIME;
}
}
run_test 39p "remote directory cached attributes updated after create ========"
+test_39r() {
+ [ $OST1_VERSION -ge $(version_code 2.13.52) ] ||
+ skip "no atime update on old OST"
+ if [ "$ost1_FSTYPE" != ldiskfs ]; then
+ skip_env "ldiskfs only test"
+ fi
+
+ local saved_adiff
+ saved_adiff=$(do_facet ost1 \
+ lctl get_param -n obdfilter.*OST0000.atime_diff)
+ stack_trap "do_facet ost1 \
+ lctl set_param obdfilter.*.atime_diff=$saved_adiff"
+
+ do_facet ost1 "lctl set_param obdfilter.*.atime_diff=5"
+
+ $LFS setstripe -i 0 $DIR/$tfile
+ dd if=/dev/zero of=$DIR/$tfile bs=4k count=1 ||
+ error "can't write initial file"
+ cancel_lru_locks osc
+
+ # exceed atime_diff and access file
+ sleep 6
+ dd if=$DIR/$tfile of=/dev/null || error "can't udpate atime"
+
+ local atime_cli=$(stat -c %X $DIR/$tfile)
+ echo "client atime: $atime_cli"
+ # allow atime update to be written to device
+ do_facet ost1 "$LCTL set_param -n osd*.*OST*.force_sync 1"
+ sleep 5
+
+ local ostdev=$(ostdevname 1)
+ local fid=($(lfs getstripe -y $DIR/$tfile |
+ awk '/l_fid:/ { print $2 }' | tr ':' ' '))
+ local objpath="O/0/d$((${fid[1]} % 32))/$((${fid[1]}))"
+ local cmd="debugfs -c -R \\\"stat $objpath\\\" $ostdev"
+
+ echo "OST atime: $(do_facet ost1 "$cmd" |& grep atime)"
+ local atime_ost=$(do_facet ost1 "$cmd" |&
+ awk -F'[: ]' '/atime:/ { print $4 }')
+ (( atime_cli == atime_ost )) ||
+ error "atime on client $atime_cli != ost $atime_ost"
+}
+run_test 39r "lazy atime update on OST"
test_39q() { # LU-8041
local testdir=$DIR/$tdir