+ RETURN(rc);
+}
+
+static int lmv_fsync(struct obd_export *exp, const struct lu_fid *fid,
+ struct ptlrpc_request **request)
+{
+ struct obd_device *obd = exp->exp_obd;
+ struct lmv_obd *lmv = &obd->u.lmv;
+ struct lmv_tgt_desc *tgt;
+ int rc;
+
+ ENTRY;
+
+ tgt = lmv_fid2tgt(lmv, fid);
+ if (IS_ERR(tgt))
+ RETURN(PTR_ERR(tgt));
+
+ rc = md_fsync(tgt->ltd_exp, fid, request);
+ RETURN(rc);
+}
+
+struct stripe_dirent {
+ struct page *sd_page;
+ struct lu_dirpage *sd_dp;
+ struct lu_dirent *sd_ent;
+ bool sd_eof;
+};
+
+struct lmv_dir_ctxt {
+ struct lmv_obd *ldc_lmv;
+ struct md_op_data *ldc_op_data;
+ struct md_callback *ldc_cb_op;
+ __u64 ldc_hash;
+ int ldc_count;
+ struct stripe_dirent ldc_stripes[0];
+};
+
+static inline void stripe_dirent_unload(struct stripe_dirent *stripe)
+{
+ if (stripe->sd_page) {
+ kunmap(stripe->sd_page);
+ put_page(stripe->sd_page);
+ stripe->sd_page = NULL;
+ stripe->sd_ent = NULL;
+ }
+}
+
+static inline void put_lmv_dir_ctxt(struct lmv_dir_ctxt *ctxt)
+{
+ int i;
+
+ for (i = 0; i < ctxt->ldc_count; i++)
+ stripe_dirent_unload(&ctxt->ldc_stripes[i]);
+}
+
+/* if @ent is dummy, or . .., get next */
+static struct lu_dirent *stripe_dirent_get(struct lmv_dir_ctxt *ctxt,
+ struct lu_dirent *ent,
+ int stripe_index)
+{
+ for (; ent; ent = lu_dirent_next(ent)) {
+ /* Skip dummy entry */
+ if (le16_to_cpu(ent->lde_namelen) == 0)
+ continue;
+
+ /* skip . and .. for other stripes */
+ if (stripe_index &&
+ (strncmp(ent->lde_name, ".",
+ le16_to_cpu(ent->lde_namelen)) == 0 ||
+ strncmp(ent->lde_name, "..",
+ le16_to_cpu(ent->lde_namelen)) == 0))
+ continue;
+
+ if (le64_to_cpu(ent->lde_hash) >= ctxt->ldc_hash)
+ break;
+ }
+
+ return ent;
+}
+
+static struct lu_dirent *stripe_dirent_load(struct lmv_dir_ctxt *ctxt,
+ struct stripe_dirent *stripe,
+ int stripe_index)
+{
+ struct md_op_data *op_data = ctxt->ldc_op_data;
+ struct lmv_oinfo *oinfo;
+ struct lu_fid fid = op_data->op_fid1;
+ struct inode *inode = op_data->op_data;
+ struct lmv_tgt_desc *tgt;
+ struct lu_dirent *ent = stripe->sd_ent;
+ __u64 hash = ctxt->ldc_hash;
+ int rc = 0;
+
+ ENTRY;
+
+ LASSERT(stripe == &ctxt->ldc_stripes[stripe_index]);
+ LASSERT(!ent);
+
+ do {
+ if (stripe->sd_page) {
+ __u64 end = le64_to_cpu(stripe->sd_dp->ldp_hash_end);
+
+ /* @hash should be the last dirent hash */
+ LASSERTF(hash <= end,
+ "ctxt@%p stripe@%p hash %llx end %llx\n",
+ ctxt, stripe, hash, end);
+ /* unload last page */
+ stripe_dirent_unload(stripe);
+ /* eof */
+ if (end == MDS_DIR_END_OFF) {
+ stripe->sd_eof = true;
+ break;
+ }
+ hash = end;
+ }
+
+ oinfo = &op_data->op_mea1->lsm_md_oinfo[stripe_index];
+ if (!oinfo->lmo_root) {
+ rc = -ENOENT;
+ break;
+ }
+
+ tgt = lmv_tgt(ctxt->ldc_lmv, oinfo->lmo_mds);
+ if (!tgt) {
+ rc = -ENODEV;
+ break;
+ }
+
+ /* op_data is shared by stripes, reset after use */
+ op_data->op_fid1 = oinfo->lmo_fid;
+ op_data->op_fid2 = oinfo->lmo_fid;
+ op_data->op_data = oinfo->lmo_root;
+
+ rc = md_read_page(tgt->ltd_exp, op_data, ctxt->ldc_cb_op, hash,
+ &stripe->sd_page);
+
+ op_data->op_fid1 = fid;
+ op_data->op_fid2 = fid;
+ op_data->op_data = inode;
+
+ if (rc)
+ break;
+
+ stripe->sd_dp = page_address(stripe->sd_page);
+ ent = stripe_dirent_get(ctxt, lu_dirent_start(stripe->sd_dp),
+ stripe_index);
+ /* in case a page filled with ., .. and dummy, read next */
+ } while (!ent);
+
+ stripe->sd_ent = ent;
+ if (rc) {
+ LASSERT(!ent);
+ /* treat error as eof, so dir can be partially accessed */
+ stripe->sd_eof = true;
+ LCONSOLE_WARN("dir "DFID" stripe %d readdir failed: %d, "
+ "directory is partially accessed!\n",
+ PFID(&ctxt->ldc_op_data->op_fid1), stripe_index,
+ rc);
+ }
+
+ RETURN(ent);