+/**
+ * We calculate on which OST the mapping will end. If the length of mapping
+ * is greater than (stripe_size * stripe_count) then the last_stripe will
+ * will be one just before start_stripe. Else we check if the mapping
+ * intersects each OST and find last_stripe.
+ * This function returns the last_stripe and also sets the stripe_count
+ * over which the mapping is spread
+ *
+ * \param lsm [in] striping information for the file
+ * \param index [in] stripe component index
+ * \param ext [in] logical extent of mapping
+ * \param start_stripe [in] starting stripe of the mapping
+ * \param stripe_count [out] the number of stripes across which to map is
+ * returned
+ *
+ * \retval last_stripe return the last stripe of the mapping
+ */
+static int fiemap_calc_last_stripe(struct lov_stripe_md *lsm, int index,
+ struct lu_extent *ext,
+ int start_stripe, int *stripe_count)
+{
+ struct lov_stripe_md_entry *lsme = lsm->lsm_entries[index];
+ int last_stripe;
+ u64 obd_start;
+ u64 obd_end;
+ int i, j;
+
+ if (ext->e_end - ext->e_start >
+ lsme->lsme_stripe_size * lsme->lsme_stripe_count) {
+ last_stripe = (start_stripe < 1 ? lsme->lsme_stripe_count - 1 :
+ start_stripe - 1);
+ *stripe_count = lsme->lsme_stripe_count;
+ } else {
+ for (j = 0, i = start_stripe; j < lsme->lsme_stripe_count;
+ i = (i + 1) % lsme->lsme_stripe_count, j++) {
+ if ((lov_stripe_intersects(lsm, index, i, ext,
+ &obd_start, &obd_end)) == 0)
+ break;
+ }
+ *stripe_count = j;
+ last_stripe = (start_stripe + j - 1) % lsme->lsme_stripe_count;
+ }
+
+ return last_stripe;
+}
+
+/**
+ * Set fe_device and copy extents from local buffer into main return buffer.
+ *
+ * \param fiemap [out] fiemap to hold all extents
+ * \param lcl_fm_ext [in] array of fiemap extents get from OSC layer
+ * \param ost_index [in] OST index to be written into the fm_device
+ * field for each extent
+ * \param ext_count [in] number of extents to be copied
+ * \param current_extent [in] where to start copying in the extent array
+ */
+static void fiemap_prepare_and_copy_exts(struct fiemap *fiemap,
+ struct fiemap_extent *lcl_fm_ext,
+ int ost_index, unsigned int ext_count,
+ int current_extent)
+{
+ char *to;
+ unsigned int ext;
+
+ for (ext = 0; ext < ext_count; ext++) {
+ lcl_fm_ext[ext].fe_device = ost_index;
+ lcl_fm_ext[ext].fe_flags |= FIEMAP_EXTENT_NET;
+ }
+
+ /* Copy fm_extent's from fm_local to return buffer */
+ to = (char *)fiemap + fiemap_count_to_size(current_extent);
+ memcpy(to, lcl_fm_ext, ext_count * sizeof(struct fiemap_extent));
+}
+
+#define FIEMAP_BUFFER_SIZE 4096
+
+/**
+ * Non-zero fe_logical indicates that this is a continuation FIEMAP
+ * call. The local end offset and the device are sent in the first
+ * fm_extent. This function calculates the stripe number from the index.
+ * This function returns a stripe_no on which mapping is to be restarted.
+ *
+ * This function returns fm_end_offset which is the in-OST offset at which
+ * mapping should be restarted. If fm_end_offset=0 is returned then caller
+ * will re-calculate proper offset in next stripe.
+ * Note that the first extent is passed to lov_get_info via the value field.
+ *
+ * \param fiemap [in] fiemap request header
+ * \param lsm [in] striping information for the file
+ * \param index [in] stripe component index
+ * \param ext [in] logical extent of mapping
+ * \param start_stripe [out] starting stripe will be returned in this
+ */
+static u64 fiemap_calc_fm_end_offset(struct fiemap *fiemap,
+ struct lov_stripe_md *lsm,
+ int index, struct lu_extent *ext,
+ int *start_stripe)
+{
+ struct lov_stripe_md_entry *lsme = lsm->lsm_entries[index];
+ u64 local_end = fiemap->fm_extents[0].fe_logical;
+ u64 lun_start;
+ u64 lun_end;
+ u64 fm_end_offset;
+ int stripe_no = -1;
+ int i;
+
+ if (fiemap->fm_extent_count == 0 ||
+ fiemap->fm_extents[0].fe_logical == 0)
+ return 0;
+
+ /* Find out stripe_no from ost_index saved in the fe_device */
+ for (i = 0; i < lsme->lsme_stripe_count; i++) {
+ struct lov_oinfo *oinfo = lsme->lsme_oinfo[i];
+
+ if (lov_oinfo_is_dummy(oinfo))
+ continue;
+
+ if (oinfo->loi_ost_idx == fiemap->fm_extents[0].fe_device) {
+ stripe_no = i;
+ break;
+ }
+ }
+
+ if (stripe_no == -1)
+ return -EINVAL;
+
+ /* If we have finished mapping on previous device, shift logical
+ * offset to start of next device */
+ if (lov_stripe_intersects(lsm, index, stripe_no, ext,
+ &lun_start, &lun_end) != 0 &&
+ local_end < lun_end) {
+ fm_end_offset = local_end;
+ *start_stripe = stripe_no;
+ } else {
+ /* This is a special value to indicate that caller should
+ * calculate offset in next stripe. */
+ fm_end_offset = 0;
+ *start_stripe = (stripe_no + 1) % lsme->lsme_stripe_count;
+ }
+
+ return fm_end_offset;
+}
+
+struct fiemap_state {
+ struct fiemap *fs_fm;
+ struct lu_extent fs_ext;
+ u64 fs_length;
+ u64 fs_end_offset;
+ int fs_cur_extent;
+ int fs_cnt_need;
+ int fs_start_stripe;
+ int fs_last_stripe;
+ bool fs_device_done;
+ bool fs_finish_stripe;
+ bool fs_enough;
+};
+
+static struct cl_object *lov_find_subobj(const struct lu_env *env,
+ struct lov_object *lov,
+ struct lov_stripe_md *lsm,
+ int index)
+{
+ struct lov_device *dev = lu2lov_dev(lov2lu(lov)->lo_dev);
+ struct lov_thread_info *lti = lov_env_info(env);
+ struct lu_fid *ofid = <i->lti_fid;
+ struct lov_oinfo *oinfo;
+ struct cl_device *subdev;
+ int entry = lov_comp_entry(index);
+ int stripe = lov_comp_stripe(index);
+ int ost_idx;
+ int rc;
+ struct cl_object *result;
+
+ if (lov->lo_type != LLT_COMP)
+ GOTO(out, result = NULL);
+
+ if (entry >= lsm->lsm_entry_count ||
+ stripe >= lsm->lsm_entries[entry]->lsme_stripe_count)
+ GOTO(out, result = NULL);
+
+ oinfo = lsm->lsm_entries[entry]->lsme_oinfo[stripe];
+ ost_idx = oinfo->loi_ost_idx;
+ rc = ostid_to_fid(ofid, &oinfo->loi_oi, ost_idx);
+ if (rc != 0)
+ GOTO(out, result = NULL);
+
+ subdev = lovsub2cl_dev(dev->ld_target[ost_idx]);
+ result = lov_sub_find(env, subdev, ofid, NULL);
+out:
+ if (result == NULL)
+ result = ERR_PTR(-EINVAL);
+ return result;
+}
+
+int fiemap_for_stripe(const struct lu_env *env, struct cl_object *obj,
+ struct lov_stripe_md *lsm, struct fiemap *fiemap,
+ size_t *buflen, struct ll_fiemap_info_key *fmkey,
+ int index, int stripeno, struct fiemap_state *fs)
+{
+ struct lov_stripe_md_entry *lsme = lsm->lsm_entries[index];
+ struct cl_object *subobj;
+ struct lov_obd *lov = lu2lov_dev(obj->co_lu.lo_dev)->ld_lov;
+ struct fiemap_extent *fm_ext = &fs->fs_fm->fm_extents[0];
+ u64 req_fm_len; /* Stores length of required mapping */
+ u64 len_mapped_single_call;
+ u64 lun_start;
+ u64 lun_end;
+ u64 obd_object_end;
+ unsigned int ext_count;
+ /* EOF for object */
+ bool ost_eof = false;
+ /* done with required mapping for this OST? */
+ bool ost_done = false;
+ int ost_index;
+ int rc = 0;
+
+ fs->fs_device_done = false;
+ /* Find out range of mapping on this stripe */
+ if ((lov_stripe_intersects(lsm, index, stripeno, &fs->fs_ext,
+ &lun_start, &obd_object_end)) == 0)
+ return 0;
+
+ if (lov_oinfo_is_dummy(lsme->lsme_oinfo[stripeno]))
+ return -EIO;
+
+ /* If this is a continuation FIEMAP call and we are on
+ * starting stripe then lun_start needs to be set to
+ * end_offset */
+ if (fs->fs_end_offset != 0 && stripeno == fs->fs_start_stripe)
+ lun_start = fs->fs_end_offset;
+ lun_end = lov_size_to_stripe(lsm, index, fs->fs_ext.e_end, stripeno);
+ if (lun_start == lun_end)
+ return 0;
+
+ req_fm_len = obd_object_end - lun_start + 1;
+ fs->fs_fm->fm_length = 0;
+ len_mapped_single_call = 0;
+
+ /* find lobsub object */
+ subobj = lov_find_subobj(env, cl2lov(obj), lsm,
+ lov_comp_index(index, stripeno));
+ if (IS_ERR(subobj))
+ return PTR_ERR(subobj);
+ /* If the output buffer is very large and the objects have many
+ * extents we may need to loop on a single OST repeatedly */
+ do {
+ if (fiemap->fm_extent_count > 0) {
+ /* Don't get too many extents. */
+ if (fs->fs_cur_extent + fs->fs_cnt_need >
+ fiemap->fm_extent_count)
+ fs->fs_cnt_need = fiemap->fm_extent_count -
+ fs->fs_cur_extent;
+ }
+
+ lun_start += len_mapped_single_call;
+ fs->fs_fm->fm_length = req_fm_len - len_mapped_single_call;
+ req_fm_len = fs->fs_fm->fm_length;
+ /**
+ * If we've collected enough extent map, we'd request 1 more,
+ * to see whether we coincidentally finished all available
+ * extent map, so that FIEMAP_EXTENT_LAST would be set.
+ */
+ fs->fs_fm->fm_extent_count = fs->fs_enough ?
+ 1 : fs->fs_cnt_need;
+ fs->fs_fm->fm_mapped_extents = 0;
+ fs->fs_fm->fm_flags = fiemap->fm_flags;
+
+ ost_index = lsme->lsme_oinfo[stripeno]->loi_ost_idx;
+
+ if (ost_index < 0 || ost_index >= lov->desc.ld_tgt_count)
+ GOTO(obj_put, rc = -EINVAL);
+ /* If OST is inactive, return extent with UNKNOWN flag. */
+ if (!lov->lov_tgts[ost_index]->ltd_active) {
+ fs->fs_fm->fm_flags |= FIEMAP_EXTENT_LAST;
+ fs->fs_fm->fm_mapped_extents = 1;
+
+ fm_ext[0].fe_logical = lun_start;
+ fm_ext[0].fe_length = obd_object_end - lun_start + 1;
+ fm_ext[0].fe_flags |= FIEMAP_EXTENT_UNKNOWN;
+
+ goto inactive_tgt;
+ }
+
+ fs->fs_fm->fm_start = lun_start;
+ fs->fs_fm->fm_flags &= ~FIEMAP_FLAG_DEVICE_ORDER;
+ memcpy(&fmkey->lfik_fiemap, fs->fs_fm, sizeof(*fs->fs_fm));
+ *buflen = fiemap_count_to_size(fs->fs_fm->fm_extent_count);
+
+ rc = cl_object_fiemap(env, subobj, fmkey, fs->fs_fm, buflen);
+ if (rc != 0)
+ GOTO(obj_put, rc);
+inactive_tgt:
+ ext_count = fs->fs_fm->fm_mapped_extents;
+ if (ext_count == 0) {
+ ost_done = true;
+ fs->fs_device_done = true;
+ /* If last stripe has hold at the end,
+ * we need to return */
+ if (stripeno == fs->fs_last_stripe) {
+ fiemap->fm_mapped_extents = 0;
+ fs->fs_finish_stripe = true;
+ GOTO(obj_put, rc);
+ }
+ break;
+ } else if (fs->fs_enough) {
+ /*
+ * We've collected enough extents and there are
+ * more extents after it.
+ */
+ GOTO(obj_put, rc);
+ }
+
+ /* If we just need num of extents, got to next device */
+ if (fiemap->fm_extent_count == 0) {
+ fs->fs_cur_extent += ext_count;
+ break;
+ }
+
+ /* prepare to copy retrived map extents */
+ len_mapped_single_call = fm_ext[ext_count - 1].fe_logical +
+ fm_ext[ext_count - 1].fe_length -
+ lun_start;
+
+ /* Have we finished mapping on this device? */
+ if (req_fm_len <= len_mapped_single_call) {
+ ost_done = true;
+ fs->fs_device_done = true;
+ }
+
+ /* Clear the EXTENT_LAST flag which can be present on
+ * the last extent */
+ if (fm_ext[ext_count - 1].fe_flags & FIEMAP_EXTENT_LAST)
+ fm_ext[ext_count - 1].fe_flags &= ~FIEMAP_EXTENT_LAST;
+ if (lov_stripe_size(lsm, index,
+ fm_ext[ext_count - 1].fe_logical +
+ fm_ext[ext_count - 1].fe_length,
+ stripeno) >= fmkey->lfik_oa.o_size) {
+ ost_eof = true;
+ fs->fs_device_done = true;
+ }
+
+ fiemap_prepare_and_copy_exts(fiemap, fm_ext, ost_index,
+ ext_count, fs->fs_cur_extent);
+ fs->fs_cur_extent += ext_count;
+
+ /* Ran out of available extents? */
+ if (fs->fs_cur_extent >= fiemap->fm_extent_count)
+ fs->fs_enough = true;
+ } while (!ost_done && !ost_eof);
+
+ if (stripeno == fs->fs_last_stripe)
+ fs->fs_finish_stripe = true;
+obj_put:
+ cl_object_put(env, subobj);
+
+ return rc;
+}
+
+/**
+ * Break down the FIEMAP request and send appropriate calls to individual OSTs.
+ * This also handles the restarting of FIEMAP calls in case mapping overflows
+ * the available number of extents in single call.
+ *
+ * \param env [in] lustre environment
+ * \param obj [in] file object
+ * \param fmkey [in] fiemap request header and other info
+ * \param fiemap [out] fiemap buffer holding retrived map extents
+ * \param buflen [in/out] max buffer length of @fiemap, when iterate
+ * each OST, it is used to limit max map needed
+ * \retval 0 success
+ * \retval < 0 error
+ */
+static int lov_object_fiemap(const struct lu_env *env, struct cl_object *obj,
+ struct ll_fiemap_info_key *fmkey,
+ struct fiemap *fiemap, size_t *buflen)
+{
+ struct lov_stripe_md_entry *lsme;
+ struct lov_stripe_md *lsm;
+ struct fiemap *fm_local = NULL;
+ loff_t whole_start;
+ loff_t whole_end;
+ int entry;
+ int start_entry;
+ int end_entry;
+ int cur_stripe = 0;
+ int stripe_count;
+ unsigned int buffer_size = FIEMAP_BUFFER_SIZE;
+ int rc = 0;
+ struct fiemap_state fs = { 0 };
+ ENTRY;
+
+ lsm = lov_lsm_addref(cl2lov(obj));
+ if (lsm == NULL) {
+ /* no extent: there is no object for mapping */
+ fiemap->fm_mapped_extents = 0;
+ return 0;
+ }
+
+ if (!(fiemap->fm_flags & FIEMAP_FLAG_DEVICE_ORDER)) {
+ /**
+ * If the entry count > 1 or stripe_count > 1 and the
+ * application does not understand DEVICE_ORDER flag,
+ * it cannot interpret the extents correctly.
+ */
+ if (lsm->lsm_entry_count > 1 ||
+ (lsm->lsm_entry_count == 1 &&
+ lsm->lsm_entries[0]->lsme_stripe_count > 1))
+ GOTO(out_lsm, rc = -ENOTSUPP);
+ }
+
+ /* No support for DOM layout yet. */
+ if (lsme_is_dom(lsm->lsm_entries[0]))
+ GOTO(out_lsm, rc = -ENOTSUPP);
+
+ if (lsm->lsm_is_released) {
+ if (fiemap->fm_start < fmkey->lfik_oa.o_size) {
+ /**
+ * released file, return a minimal FIEMAP if
+ * request fits in file-size.
+ */
+ fiemap->fm_mapped_extents = 1;
+ fiemap->fm_extents[0].fe_logical = fiemap->fm_start;
+ if (fiemap->fm_start + fiemap->fm_length <
+ fmkey->lfik_oa.o_size)
+ fiemap->fm_extents[0].fe_length =
+ fiemap->fm_length;
+ else
+ fiemap->fm_extents[0].fe_length =
+ fmkey->lfik_oa.o_size -
+ fiemap->fm_start;
+ fiemap->fm_extents[0].fe_flags |=
+ FIEMAP_EXTENT_UNKNOWN | FIEMAP_EXTENT_LAST;
+ }
+ GOTO(out_lsm, rc = 0);
+ }
+
+ /* buffer_size is small to hold fm_extent_count of extents. */
+ if (fiemap_count_to_size(fiemap->fm_extent_count) < buffer_size)
+ buffer_size = fiemap_count_to_size(fiemap->fm_extent_count);
+
+ OBD_ALLOC_LARGE(fm_local, buffer_size);
+ if (fm_local == NULL)
+ GOTO(out_lsm, rc = -ENOMEM);
+
+ /**
+ * Requested extent count exceeds the fiemap buffer size, shrink our
+ * ambition.
+ */
+ if (fiemap_count_to_size(fiemap->fm_extent_count) > *buflen)
+ fiemap->fm_extent_count = fiemap_size_to_count(*buflen);
+ if (fiemap->fm_extent_count == 0)
+ fs.fs_cnt_need = 0;
+
+ fs.fs_enough = false;
+ fs.fs_cur_extent = 0;
+ fs.fs_fm = fm_local;
+ fs.fs_cnt_need = fiemap_size_to_count(buffer_size);
+
+ whole_start = fiemap->fm_start;
+ /* whole_start is beyond the end of the file */
+ if (whole_start > fmkey->lfik_oa.o_size)
+ GOTO(out_fm_local, rc = -EINVAL);
+ whole_end = (fiemap->fm_length == OBD_OBJECT_EOF) ?
+ fmkey->lfik_oa.o_size :
+ whole_start + fiemap->fm_length - 1;
+ /**
+ * If fiemap->fm_length != OBD_OBJECT_EOF but whole_end exceeds file
+ * size
+ */
+ if (whole_end > fmkey->lfik_oa.o_size)
+ whole_end = fmkey->lfik_oa.o_size;
+
+ start_entry = lov_lsm_entry(lsm, whole_start);
+ end_entry = lov_lsm_entry(lsm, whole_end);
+ if (end_entry == -1)
+ end_entry = lsm->lsm_entry_count - 1;
+
+ if (start_entry == -1 || end_entry == -1)
+ GOTO(out_fm_local, rc = -EINVAL);
+
+ /* TODO: rewrite it with lov_foreach_io_layout() */
+ for (entry = start_entry; entry <= end_entry; entry++) {
+ lsme = lsm->lsm_entries[entry];
+
+ if (!lsme_inited(lsme))
+ break;
+
+ if (entry == start_entry)
+ fs.fs_ext.e_start = whole_start;
+ else
+ fs.fs_ext.e_start = lsme->lsme_extent.e_start;
+ if (entry == end_entry)
+ fs.fs_ext.e_end = whole_end;
+ else
+ fs.fs_ext.e_end = lsme->lsme_extent.e_end - 1;
+ fs.fs_length = fs.fs_ext.e_end - fs.fs_ext.e_start + 1;
+
+ /* Calculate start stripe, last stripe and length of mapping */
+ fs.fs_start_stripe = lov_stripe_number(lsm, entry,
+ fs.fs_ext.e_start);
+ fs.fs_last_stripe = fiemap_calc_last_stripe(lsm, entry,
+ &fs.fs_ext, fs.fs_start_stripe,
+ &stripe_count);
+ fs.fs_end_offset = fiemap_calc_fm_end_offset(fiemap, lsm, entry,
+ &fs.fs_ext, &fs.fs_start_stripe);
+ /* Check each stripe */
+ for (cur_stripe = fs.fs_start_stripe; stripe_count > 0;
+ --stripe_count,
+ cur_stripe = (cur_stripe + 1) % lsme->lsme_stripe_count) {
+ rc = fiemap_for_stripe(env, obj, lsm, fiemap, buflen,
+ fmkey, entry, cur_stripe, &fs);
+ if (rc < 0)
+ GOTO(out_fm_local, rc);
+ if (fs.fs_enough)
+ GOTO(finish, rc);
+ if (fs.fs_finish_stripe)
+ break;
+ } /* for each stripe */
+ } /* for covering layout component */
+ /*
+ * We've traversed all components, set @entry to the last component
+ * entry, it's for the last stripe check.
+ */
+ entry--;
+finish:
+ /* Indicate that we are returning device offsets unless file just has
+ * single stripe */
+ if (lsm->lsm_entry_count > 1 ||
+ (lsm->lsm_entry_count == 1 &&
+ lsm->lsm_entries[0]->lsme_stripe_count > 1))
+ fiemap->fm_flags |= FIEMAP_FLAG_DEVICE_ORDER;
+
+ if (fiemap->fm_extent_count == 0)
+ goto skip_last_device_calc;
+
+ /* Check if we have reached the last stripe and whether mapping for that
+ * stripe is done. */
+ if ((cur_stripe == fs.fs_last_stripe) && fs.fs_device_done)
+ fiemap->fm_extents[fs.fs_cur_extent - 1].fe_flags |=
+ FIEMAP_EXTENT_LAST;
+skip_last_device_calc:
+ fiemap->fm_mapped_extents = fs.fs_cur_extent;
+out_fm_local:
+ OBD_FREE_LARGE(fm_local, buffer_size);
+
+out_lsm:
+ lov_lsm_put(lsm);
+ return rc;
+}
+
+static int lov_object_getstripe(const struct lu_env *env, struct cl_object *obj,
+ struct lov_user_md __user *lum, size_t size)
+{
+ struct lov_object *lov = cl2lov(obj);
+ struct lov_stripe_md *lsm;
+ int rc = 0;
+ ENTRY;
+
+ lsm = lov_lsm_addref(lov);
+ if (lsm == NULL)
+ RETURN(-ENODATA);
+
+ rc = lov_getstripe(env, cl2lov(obj), lsm, lum, size);
+ lov_lsm_put(lsm);
+ RETURN(rc);
+}
+
+static int lov_object_layout_get(const struct lu_env *env,
+ struct cl_object *obj,
+ struct cl_layout *cl)
+{
+ struct lov_object *lov = cl2lov(obj);
+ struct lov_stripe_md *lsm = lov_lsm_addref(lov);
+ struct lu_buf *buf = &cl->cl_buf;
+ ssize_t rc;
+ ENTRY;
+
+ if (lsm == NULL) {
+ cl->cl_size = 0;
+ cl->cl_layout_gen = CL_LAYOUT_GEN_EMPTY;
+
+ RETURN(0);
+ }
+
+ cl->cl_size = lov_comp_md_size(lsm);
+ cl->cl_layout_gen = lsm->lsm_layout_gen;
+ cl->cl_dom_comp_size = 0;
+ cl->cl_is_released = lsm->lsm_is_released;
+ if (lsm_is_composite(lsm->lsm_magic)) {
+ struct lov_stripe_md_entry *lsme = lsm->lsm_entries[0];
+
+ cl->cl_is_composite = true;
+
+ if (lsme_is_dom(lsme))
+ cl->cl_dom_comp_size = lsme->lsme_extent.e_end;
+ } else {
+ cl->cl_is_composite = false;
+ }
+
+ rc = lov_lsm_pack(lsm, buf->lb_buf, buf->lb_len);
+ lov_lsm_put(lsm);
+
+ RETURN(rc < 0 ? rc : 0);
+}
+
+static loff_t lov_object_maxbytes(struct cl_object *obj)
+{
+ struct lov_object *lov = cl2lov(obj);
+ struct lov_stripe_md *lsm = lov_lsm_addref(lov);
+ loff_t maxbytes;
+
+ if (lsm == NULL)
+ return LLONG_MAX;
+
+ maxbytes = lsm->lsm_maxbytes;
+
+ lov_lsm_put(lsm);
+
+ return maxbytes;
+}
+
+static int lov_object_flush(const struct lu_env *env, struct cl_object *obj,
+ struct ldlm_lock *lock)
+{
+ return LOV_2DISPATCH_NOLOCK(cl2lov(obj), llo_flush, env, obj, lock);
+}
+