Viewing: lov_io.c

// SPDX-License-Identifier: GPL-2.0

/*
 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
 * Use is subject to license terms.
 *
 * Copyright (c) 2011, 2017, Intel Corporation.
 */

/*
 * This file is part of Lustre, http://www.lustre.org/
 *
 * Implementation of cl_io for LOV layer.
 *
 * Author: Nikita Danilov <nikita.danilov@sun.com>
 * Author: Jinshan Xiong <jinshan.xiong@whamcloud.com>
 */

#define DEBUG_SUBSYSTEM S_LOV

#include "lov_cl_internal.h"

/** \addtogroup lov
 *  @{
 */

/**
 * lov_sub_alloc() - Allocate a new sub IO
 * @lio: top level lov IO structure
 * @index: index into lov (stripe)
 *
 * Return Pointer to allocated lov_io_sub structure
 */
static inline struct lov_io_sub *lov_sub_alloc(struct lov_io *lio, int index)
{
	struct lov_io_sub *sub;

	if (lio->lis_nr_subios == 0) {
		LASSERT(lio->lis_single_subio_index == -1);
		sub = &lio->lis_single_subio;
		lio->lis_single_subio_index = index;
		memset(sub, 0, sizeof(*sub));
	} else {
		OBD_ALLOC_PTR(sub);
	}

	if (sub) {
		INIT_LIST_HEAD(&sub->sub_list);
		INIT_LIST_HEAD(&sub->sub_linkage);
		sub->sub_subio_index = index;
	}

	return sub;
}

/**
 * lov_sub_free() - Release a sub IO
 * @lio: top level lov IO structure
 * @sub: sub io to individual stripe
 */
static inline void lov_sub_free(struct lov_io *lio, struct lov_io_sub *sub)
{
	if (sub->sub_subio_index == lio->lis_single_subio_index) {
		LASSERT(sub == &lio->lis_single_subio);
		lio->lis_single_subio_index = -1;
	} else {
		OBD_FREE_PTR(sub);
	}
}

static void lov_io_sub_fini(const struct lu_env *env, struct lov_io *lio,
			    struct lov_io_sub *sub)
{
	ENTRY;

	cl_io_fini(sub->sub_env, &sub->sub_io);

	if (sub->sub_env && !IS_ERR(sub->sub_env)) {
		cl_env_put(sub->sub_env, &sub->sub_refcheck);
		sub->sub_env = NULL;
	}
	EXIT;
}

static inline bool
is_index_within_mirror(struct lov_object *lov, int index, int mirror_index)
{
	struct lov_mirror_entry *lre = lov_mirror_entry(lov, mirror_index);

	return (index >= lre->lre_start && index <= lre->lre_end);
}

static int lov_io_sub_init(const struct lu_env *env, struct lov_io *lio,
			   struct lov_io_sub *sub)
{
	struct lov_object *lov = lio->lis_object;
	struct cl_io *sub_io;
	struct cl_object *sub_obj;
	struct cl_io *io = lio->lis_cl.cis_io;
	int index = lov_comp_entry(sub->sub_subio_index);
	int stripe = lov_comp_stripe(sub->sub_subio_index);
	int result = 0;

	LASSERT(sub->sub_env == NULL);
	ENTRY;

	if (unlikely(!lov_r0(lov, index)->lo_sub ||
		     !lov_r0(lov, index)->lo_sub[stripe]))
		RETURN(-EIO);

	LASSERTF(ergo(lov_is_flr(lov),
		      is_index_within_mirror(lov, index,
					     lio->lis_mirror_index)),
		 DFID "iot = %d, index = %d, mirror = %d\n",
		 PFID(lu_object_fid(lov2lu(lov))), io->ci_type, index,
		 lio->lis_mirror_index);

	/* obtain new environment */
	sub->sub_env = cl_env_get(&sub->sub_refcheck);
	if (IS_ERR(sub->sub_env)) {
		result = PTR_ERR(sub->sub_env);
		RETURN(result);
	}

	sub_obj = lovsub2cl(lov_r0(lov, index)->lo_sub[stripe]);
	sub_io  = &sub->sub_io;

	sub_io->ci_obj    = sub_obj;
	sub_io->ci_result = 0;

	sub_io->ci_parent  = io;
	sub_io->ci_lockreq = io->ci_lockreq;
	sub_io->ci_type    = io->ci_type;
	sub_io->ci_no_srvlock = io->ci_no_srvlock;
	sub_io->ci_noatime = io->ci_noatime;
	sub_io->ci_async_readahead = io->ci_async_readahead;
	sub_io->ci_lock_no_expand = io->ci_lock_no_expand;
	sub_io->ci_ndelay = io->ci_ndelay;
	sub_io->ci_layout_version = io->ci_layout_version;
	sub_io->ci_tried_all_mirrors = io->ci_tried_all_mirrors;
	sub_io->ci_parity_io = io->ci_parity_io;
	sub_io->ci_parity_eof = io->ci_parity_eof;

	result = cl_io_sub_init(sub->sub_env, sub_io, io->ci_type, sub_obj);

	if (result < 0)
		lov_io_sub_fini(env, lio, sub);

	RETURN(result);
}

struct lov_io_sub *lov_sub_get(const struct lu_env *env,
			       struct lov_io *lio, int index)
{
	struct lov_io_sub *sub;
	int rc = 0;

	ENTRY;

	list_for_each_entry(sub, &lio->lis_subios, sub_list) {
		if (sub->sub_subio_index == index) {
			rc = 1;
			break;
		}
	}

	if (rc == 0) {
		sub = lov_sub_alloc(lio, index);
		if (!sub)
			GOTO(out, rc = -ENOMEM);

		rc = lov_io_sub_init(env, lio, sub);
		if (rc < 0) {
			lov_sub_free(lio, sub);
			GOTO(out, rc);
		}

		list_add_tail(&sub->sub_list, &lio->lis_subios);
		lio->lis_nr_subios++;
	}
out:
	if (rc < 0)
		sub = ERR_PTR(rc);
	else
		sub->sub_io.ci_noquota = lio->lis_cl.cis_io->ci_noquota;
	RETURN(sub);
}

/*
 * Lov io operations.
 */

/**
 * lov_io_subio_init() -  Initilize LOV I/O operation
 * @env: lustre environment
 * @lio: Pointer to struct lov_io
 * @io: highlevel I/O request
 *
 * Return:
 * * %0 on success
 * * %negative on failure
 */
static int lov_io_subio_init(const struct lu_env *env, struct lov_io *lio,
			     struct cl_io *io)
{
	ENTRY;

	LASSERT(lio->lis_object != NULL);

	INIT_LIST_HEAD(&lio->lis_subios);
	lio->lis_single_subio_index = -1;
	lio->lis_nr_subios = 0;

	RETURN(0);
}

/*
 * Decide if it will need write intent RPC
 */
static int lov_io_mirror_write_intent(struct lov_io *lio,
	struct lov_object *obj, struct cl_io *io)
{
	struct lu_object *lobj = lov2lu(obj);
	struct lov_layout_composite *comp = &obj->u.composite;
	struct lu_extent *ext = &io->ci_write_intent;
	struct lov_mirror_entry *lre;
	struct lov_mirror_entry *primary;
	struct lov_layout_entry *lle;
	size_t count = 0;

	ENTRY;

	*ext = (typeof(*ext)) { lio->lis_pos, lio->lis_endpos };
	io->ci_need_write_intent = 0;

	if (!(io->ci_type == CIT_WRITE || cl_io_is_mkwrite(io) ||
	      cl_io_is_fallocate(io) || cl_io_is_trunc(io)))
		RETURN(0);

	/*
	 * FLR: check if it needs to send a write intent RPC to server.
	 * Writing to sync_pending file needs write intent RPC to change
	 * the file state back to write_pending, so that the layout version
	 * can be increased when the state changes to sync_pending at a later
	 * time. Otherwise there exists a chance that an evicted client may
	 * dirty the file data while resync client is working on it.
	 * Designated I/O is allowed for resync workload.
	 */
	if (lov_flr_state(obj) == LCM_FL_RDONLY ||
	    (lov_flr_state(obj) == LCM_FL_SYNC_PENDING &&
	     io->ci_designated_mirror == 0)) {
		io->ci_need_write_intent = 1;
		RETURN(0);
	}

	LASSERT((lov_flr_state(obj) == LCM_FL_WRITE_PENDING));
	LASSERT(comp->lo_preferred_mirror >= 0);

	/*
	 * need to iterate all components to see if there are
	 * multiple components covering the writing component
	 */
	primary = &comp->lo_mirrors[comp->lo_preferred_mirror];
	if (primary->lre_stale || !primary->lre_valid) {
		/**
		 * new server could pick a primary mirror which old client
		 * does not recognize, and old client would mark it as
		 * invalid.
		 */
		CERROR(DFID ": cannot find known valid non-stale mirror, could be new server picked a mirror which this client "
		       "does not recognize.\n",
		       PFID(lu_object_fid(lobj)));
		RETURN(-EIO);
	}

	lov_foreach_mirror_layout_entry(obj, lle, primary) {
		LASSERT(lle->lle_valid);
		if (!lu_extent_is_overlapped(ext, lle->lle_extent))
			continue;

		ext->e_start = min(ext->e_start, lle->lle_extent->e_start);
		ext->e_end = max(ext->e_end, lle->lle_extent->e_end);
		++count;
	}
	if (count == 0) {
		CERROR(DFID ": cannot find any valid components covering file extent "
		       DEXT", mirror: %d\n",
		       PFID(lu_object_fid(lobj)), PEXT(ext),
		       primary->lre_mirror_id);
		RETURN(-EIO);
	}

	count = 0;
	lov_foreach_mirror_entry(obj, lre) {
		if (lre == primary)
			continue;

		lov_foreach_mirror_layout_entry(obj, lle, lre) {
			if (!lle->lle_valid)
				continue;

			if (lu_extent_is_overlapped(ext, lle->lle_extent)) {
				++count;
				break;
			}
		}
	}

	CDEBUG(D_VFSTRACE, DFID "there are %zd components to be staled to modify file extent "
	       DEXT", iot: %d\n",
	       PFID(lu_object_fid(lobj)), count, PEXT(ext), io->ci_type);

	io->ci_need_write_intent = count > 0;

	RETURN(0);
}

/**
 * lov_io_parity_size_comp() - Parity byte span for one data/parity pair
 * @data_lsme: data component stripe metadata
 * @parity_lsme: parity component stripe metadata
 * @data_end: exclusive end of data in file offset space
 *
 * Returns how many bytes of parity are written for data in
 * [data_lsme->lsme_extent.e_start, data_end).
 * May overestimate (holes) but must not be short.
 */
static loff_t lov_io_parity_size_comp(struct lov_stripe_md_entry *data_lsme,
				      struct lov_stripe_md_entry *parity_lsme,
				      loff_t data_end)
{
	struct ec_split_comp sc;
	loff_t comp_start = data_lsme->lsme_extent.e_start;
	loff_t data_upto;
	__u64 stripe_set_size;
	__u64 num_stripe_sets;
	__u64 data_in_extent;
	int num_raid_sets;

	if (data_end <= comp_start)
		return 0;

	data_upto = min_t(__u64, (__u64)data_end,
			  data_lsme->lsme_extent.e_end);
	ec_split_stripes(data_lsme->lsme_stripe_count,
			 parity_lsme->lsme_dstripe_count, &sc);
	num_raid_sets = sc.esc_n0 + sc.esc_n1;

	stripe_set_size = (__u64)data_lsme->lsme_stripe_count *
			  data_lsme->lsme_stripe_size;
	data_in_extent = data_upto - comp_start;
	num_stripe_sets = (data_in_extent + stripe_set_size - 1) /
			  stripe_set_size;

	return num_stripe_sets * num_raid_sets *
	       parity_lsme->lsme_cstripe_count *
	       parity_lsme->lsme_stripe_size;
}

/**
 * lov_io_find_data_comp() - Find data component matching parity extent
 * @obj: lov object
 * @data_lre: data mirror entry
 * @parity_ext: parity component extent
 *
 * Return: matching layout entry, or NULL
 */
static struct lov_layout_entry *
lov_io_find_data_comp(struct lov_object *obj, struct lov_mirror_entry *data_lre,
		      struct lu_extent *parity_ext)
{
	struct lov_layout_entry *data_lle;

	lov_foreach_mirror_layout_entry(obj, data_lle, data_lre) {
		if (lu_extent_is_equal(parity_ext,
				       &data_lle->lle_lsme->lsme_extent))
			return data_lle;
	}

	return NULL;
}

/**
 * lov_io_parity_eof() - Parity mirror EOF from @data_size and RAID geometry
 * @obj: lov object
 * @mirror_index: parity mirror index
 * @data_size: data mirror file size
 *
 * For each parity component below @data_size, add lov_io_parity_size_comp() to
 * its extent start; return the maximum. May overestimate but not underestimate.
 *
 * Return: parity EOF in file offset space, or 0 on error
 */
static loff_t lov_io_parity_eof(struct lov_object *obj, int mirror_index,
				loff_t data_size)
{
	struct lov_mirror_entry *parity_lre;
	struct lov_mirror_entry *data_lre;
	struct lov_layout_entry *parity_lle;
	struct lov_layout_entry *data_lle;
	struct lu_extent *ext;
	__u16 data_mirror_id;
	loff_t parity_eof = 0;

	if (data_size == 0)
		return 0;

	parity_lre = lov_mirror_entry(obj, mirror_index);
	parity_lle = lov_entry(obj, parity_lre->lre_start);

	if (!(parity_lle->lle_lsme->lsme_pattern & LOV_PATTERN_PARITY))
		return 0;

	data_mirror_id = parity_lle->lle_lsme->lsme_mirror_link_id;
	data_lre = lov_mirror_by_id(obj, data_mirror_id);
	if (data_lre == NULL)
		return 0;

	lov_foreach_mirror_layout_entry(obj, parity_lle, parity_lre) {
		loff_t seg_eof;

		ext = &parity_lle->lle_lsme->lsme_extent;
		if (data_size <= ext->e_start)
			continue;

		data_lle = lov_io_find_data_comp(obj, data_lre, ext);
		if (data_lle == NULL) {
			CERROR(DFID ": no data component matching parity extent "DEXT" (parity mirror %u, data mirror %u)\n",
			       PFID(lu_object_fid(lov2lu(obj))), PEXT(ext),
			       parity_lre->lre_mirror_id, data_mirror_id);
			continue;
		}

		/*
		 * Or we could just find the last valid data component,
		 * e.g. the one with actual data written? For security, I
		 * prefer not to do this here.
		 */
		seg_eof = ext->e_start +
			  lov_io_parity_size_comp(data_lle->lle_lsme,
						  parity_lle->lle_lsme,
						  data_size);
		if (seg_eof > parity_eof)
			parity_eof = seg_eof;
	}

	CDEBUG(D_INODE, DFID ": parity EOF calc: data_size=%lld parity_eof=%lld\n",
	       PFID(lu_object_fid(lov2lu(obj))), data_size, parity_eof);

	return parity_eof;
}

static loff_t lov_io_top_file_size(const struct lu_env *env,
				   struct lov_object *obj)
{
	struct cl_object *top_obj = cl_object_top(&obj->lo_cl);
	struct cl_attr attr = { 0 };

	cl_object_attr_lock(top_obj);
	if (top_obj->co_ops->coo_attr_get)
		top_obj->co_ops->coo_attr_get(env, top_obj, &attr);
	cl_object_attr_unlock(top_obj);

	return attr.cat_size;
}

static int lov_io_mirror_init(const struct lu_env *env, struct lov_io *lio,
			      struct lov_object *obj, struct cl_io *io)
{
	struct lov_layout_composite *comp = &obj->u.composite;
	bool skipped_parity = false;
	int index;
	int i;
	int result;

	ENTRY;

	if (!lov_is_flr(obj)) {
		/* only locks/pages are manipulated for CIT_MISC op, no
		 * cl_io_loop() will be called, don't check/set mirror info.
		 */
		if (io->ci_type != CIT_MISC) {
			LASSERT(comp->lo_preferred_mirror == 0);
			lio->lis_mirror_index = comp->lo_preferred_mirror;
		}
		io->ci_ndelay = 0;
		RETURN(0);
	}

	/* transfer the layout version for verification */
	if (io->ci_layout_version == 0)
		io->ci_layout_version = obj->lo_lsm->lsm_layout_gen;

	/* find the corresponding mirror for designated mirror IO */
	if (io->ci_designated_mirror > 0) {
		struct lov_mirror_entry *entry;

		LASSERT(!io->ci_ndelay);

		CDEBUG(D_LAYOUT, "designated I/O mirror state: %d\n",
		      lov_flr_state(obj));

		if ((cl_io_is_trunc(io) || io->ci_type == CIT_WRITE ||
		     cl_io_is_fallocate(io)) &&
		    (io->ci_layout_version != obj->lo_lsm->lsm_layout_gen)) {
			/*
			 * For resync I/O, the ci_layout_version was the layout
			 * version when resync starts. If it doesn't match the
			 * current object layout version, it means the layout
			 * has been changed
			 */
			RETURN(-ESTALE);
		}

		io->ci_layout_version |= LU_LAYOUT_RESYNC;

		index = 0;
		lio->lis_mirror_index = -1;
		lov_foreach_mirror_entry(obj, entry) {
			if (entry->lre_mirror_id ==
			    io->ci_designated_mirror) {
				lio->lis_mirror_index = index;
				break;
			}

			index++;
		}

		if (lio->lis_mirror_index < 0)
			RETURN(-EINVAL);

		/* Set ci_parity_io if this is a parity mirror */
		io->ci_parity_io =
			lov_mirror_entry(obj, lio->lis_mirror_index)->lre_parity;

		/* Calculate parity EOF if this is a parity mirror */
		if (io->ci_parity_io) {
			loff_t file_size;

			/*
			 * Get file size from the top (VVP) layer only.
			 * We can't use cl_object_attr_get() here because it
			 * walks through all layers and the LOV layer would
			 * reset cat_size to 0 before recalculating from
			 * stripe attributes (which may not be populated yet).
			 * The VVP layer's coo_attr_get reads i_size directly
			 * from the inode.
			 */
			file_size = lov_io_top_file_size(env, obj);

			io->ci_parity_eof = lov_io_parity_eof(obj,
					lio->lis_mirror_index, file_size);
			CDEBUG(D_INODE, "designated parity IO: mirror_idx=%d, "
			       "file_size=%llu, parity_eof=%lld\n",
			       lio->lis_mirror_index, file_size,
			       io->ci_parity_eof);
		}

		RETURN(0);
	}

	result = lov_io_mirror_write_intent(lio, obj, io);
	if (result)
		RETURN(result);

	if (io->ci_need_write_intent) {
		CDEBUG(D_VFSTRACE, DFID " need write intent for [%llu, %llu)\n",
		       PFID(lu_object_fid(lov2lu(obj))),
		       lio->lis_pos, lio->lis_endpos);

		if (cl_io_is_trunc(io)) {
			/**
			 * for truncate, we uses [size, EOF) to judge whether
			 * a write intent needs to be send, but we need to
			 * restore the write extent to [0, size], in truncate,
			 * the byte in the size position is accessed.
			 */
			io->ci_write_intent.e_start = 0;
			io->ci_write_intent.e_end =
					io->u.ci_setattr.sa_attr.lvb_size + 1;
		}
		/* stop cl_io_init() loop */
		RETURN(1);
	}

	if (io->ci_ndelay_tried == 0 || /* first time to try */
	    /* reset the mirror index if layout has changed */
	    lio->lis_mirror_layout_gen != obj->lo_lsm->lsm_layout_gen) {
		lio->lis_mirror_layout_gen = obj->lo_lsm->lsm_layout_gen;
		index = lio->lis_mirror_index = comp->lo_preferred_mirror;
	} else {
		index = lio->lis_mirror_index;
		LASSERT(index >= 0);

		/* move mirror index to the next one */
		index = (index + 1) % comp->lo_mirror_count;
	}

	for (i = 0; i < comp->lo_mirror_count; i++) {
		struct lu_extent ext = { .e_start = lio->lis_pos,
					 .e_end   = lio->lis_pos + 1 };
		struct lov_mirror_entry *lre;
		struct lov_layout_entry *lle;
		bool found = false;

		lre = lov_mirror_entry(obj, (index + i) % comp->lo_mirror_count);
		if (!lre->lre_valid)
			continue;

		if (lre->lre_foreign)
			continue;

		/* skip parity mirrors for read IOs unless designated */
		if (lre->lre_parity &&
		    (io->ci_type == CIT_READ || io->ci_type == CIT_FAULT) &&
		    !io->ci_designated_mirror) {
			skipped_parity = true;
			continue;
		}

		lov_foreach_mirror_layout_entry(obj, lle, lre) {
			if (!lle->lle_valid)
				continue;

			if (lu_extent_is_overlapped(&ext, lle->lle_extent)) {
				found = true;
				break;
			}
		} /* each component of the mirror */
		if (found) {
			index = (index + i) % comp->lo_mirror_count;
			break;
		}
	} /* each mirror */

	if (i == comp->lo_mirror_count) {
		/* If we only skipped parity mirrors, return EINVAL */
		if (skipped_parity) {
			CERROR(DFID": only parity mirrors available for read I/O at %llu\n",
			       PFID(lu_object_fid(lov2lu(obj))), lio->lis_pos);
			RETURN(-EINVAL);
		}

		CERROR(DFID": failed to find a component covering I/O region at %llu\n",
		       PFID(lu_object_fid(lov2lu(obj))), lio->lis_pos);

		dump_lsm(D_ERROR, obj->lo_lsm);

		RETURN(-EIO);
	}

	CDEBUG(D_VFSTRACE, DFID ": flr state: %d, move mirror from %d to %d, have retried: %d, mirror count: %d\n",
	       PFID(lu_object_fid(lov2lu(obj))), lov_flr_state(obj),
	       lio->lis_mirror_index, index, io->ci_ndelay_tried,
	       comp->lo_mirror_count);

	lio->lis_mirror_index = index;

	/* Set ci_parity_io if this is a parity mirror */
	io->ci_parity_io = lov_mirror_entry(obj, index)->lre_parity;

	/* Calculate parity EOF if this is a parity mirror */
	if (io->ci_parity_io) {
		loff_t file_size;

		/*
		 * Get file size from the top (VVP) layer only.
		 * See comment in designated mirror path above.
		 */
		file_size = lov_io_top_file_size(env, obj);

		io->ci_parity_eof = lov_io_parity_eof(obj, index, file_size);
		CDEBUG(D_INODE, "mirror selection parity IO: mirror_idx=%d, "
		       "file_size=%llu, parity_eof=%lld\n",
		       index, file_size, io->ci_parity_eof);
	}

	/* we can't use parity mirrors for write unless designated */
	if (lov_mirror_entry(obj, index)->lre_parity &&
	    io->ci_type == CIT_WRITE &&
	    io->ci_designated_mirror == 0) {
		CERROR(DFID": trying to use parity mirror %d for write\n",
		       PFID(lu_object_fid(lov2lu(obj))), index);
		RETURN(-EINVAL);
	}

	/*
	 * FLR: if all mirrors have been tried once, most likely the network
	 * of this client has been partitioned. We should relinquish CPU for
	 * a while before trying again.
	 */
	if (io->ci_ndelay && io->ci_ndelay_tried > 0 &&
	    (io->ci_ndelay_tried % comp->lo_mirror_count == 0)) {
		schedule_timeout_interruptible(cfs_time_seconds(1) / 100);
		if (signal_pending(current))
			RETURN(-EINTR);

		/**
		 * we'd set ci_tried_all_mirrors to turn off fast mirror
		 * switching for read after we've tried all mirrors several
		 * rounds.
		 */
		io->ci_tried_all_mirrors = io->ci_ndelay_tried %
					   (comp->lo_mirror_count * 4) == 0;
	}
	++io->ci_ndelay_tried;

	CDEBUG(D_VFSTRACE, "use %sdelayed RPC state for this IO\n",
	       io->ci_ndelay ? "non-" : "");

	RETURN(0);
}

static int lov_io_slice_init(const struct lu_env *env, struct lov_io *lio,
			     struct lov_object *obj, struct cl_io *io)
{
	int index;
	int result = 0;
	bool rdonly;

	ENTRY;

	io->ci_result = 0;
	lio->lis_object = obj;
	lio->lis_cached_entry = LIS_CACHE_ENTRY_NONE;

	rdonly = lsm_is_rdonly(obj->lo_lsm);
	switch (io->ci_type) {
	case CIT_READ:
	case CIT_WRITE:
		if (io->ci_type == CIT_WRITE && rdonly) {
			io->ci_need_pccro_clear = 1;
			GOTO(out, result = 1);
		}
		lio->lis_pos = io->u.ci_rw.crw_pos;
		lio->lis_endpos = io->u.ci_rw.crw_pos + io->u.ci_rw.crw_bytes;
		lio->lis_io_endpos = lio->lis_endpos;
		if (cl_io_is_append(io)) {
			/*
			 * If there is LOV EA hole, then we may cannot locate
			 * the current file-tail exactly.
			 */
			if (unlikely(obj->lo_lsm->lsm_entries[0]->lsme_pattern &
				     LOV_PATTERN_F_HOLE))
				GOTO(out, result = -EIO);
		}
		break;

	case CIT_SETATTR:
		if (cl_io_is_fallocate(io)) {
			if (rdonly) {
				io->ci_need_pccro_clear = 1;
				GOTO(out, result = 1);
			}
			lio->lis_pos = io->u.ci_setattr.sa_falloc_offset;
			lio->lis_endpos = io->u.ci_setattr.sa_falloc_end;
		} else if (cl_io_is_trunc(io)) {
			if (rdonly) {
				io->ci_need_pccro_clear = 1;
				GOTO(out, result = 1);
			}
			lio->lis_pos = io->u.ci_setattr.sa_attr.lvb_size;
			lio->lis_endpos = OBD_OBJECT_EOF;
		} else {
			lio->lis_pos = 0;
			lio->lis_endpos = OBD_OBJECT_EOF;
		}
		break;

	case CIT_DATA_VERSION:
		lio->lis_pos = 0;
		lio->lis_endpos = OBD_OBJECT_EOF;
		break;

	case CIT_FAULT: {
		pgoff_t index = io->u.ci_fault.ft_index;

		if (cl_io_is_mkwrite(io) && rdonly) {
			io->ci_need_pccro_clear = 1;
			GOTO(out, result = -ENODATA);
		}

		lio->lis_pos = index << PAGE_SHIFT;
		lio->lis_endpos = (index + 1) << PAGE_SHIFT;
		break;
	}

	case CIT_FSYNC: {
		lio->lis_pos = io->u.ci_fsync.fi_start;
		lio->lis_endpos = io->u.ci_fsync.fi_end;
		break;
	}

	case CIT_LADVISE: {
		lio->lis_pos = io->u.ci_ladvise.lio_start;
		lio->lis_endpos = io->u.ci_ladvise.lio_end;
		break;
	}

	case CIT_LSEEK: {
		lio->lis_pos = io->u.ci_lseek.ls_start;
		lio->lis_endpos = OBD_OBJECT_EOF;
		break;
	}

	case CIT_GLIMPSE:
		lio->lis_pos = 0;
		lio->lis_endpos = OBD_OBJECT_EOF;
		break;

	case CIT_MISC:
		lio->lis_pos = 0;
		lio->lis_endpos = OBD_OBJECT_EOF;
		break;

	default:
		LBUG();
	}

	/*
	 * CIT_MISC + ci_ignore_layout can identify the I/O from the OSC layer,
	 * it won't care/access lov layout related info.
	 */
	if (io->ci_ignore_layout && io->ci_type == CIT_MISC)
		GOTO(out, result = 0);

	LASSERT(obj->lo_lsm != NULL);

	result = lov_io_mirror_init(env, lio, obj, io);
	if (result)
		GOTO(out, result);

	/* check if it needs to instantiate layout */
	if (!(io->ci_type == CIT_WRITE || cl_io_is_mkwrite(io) ||
	      cl_io_is_fallocate(io) ||
	      (cl_io_is_trunc(io) && io->u.ci_setattr.sa_attr.lvb_size > 0)))
		GOTO(out, result = 0);

	/*
	 * for truncate, it only needs to instantiate the components
	 * before the truncated size.
	 */
	if (cl_io_is_trunc(io)) {
		io->ci_write_intent.e_start = 0;
		/* for writes, e_end is endpos, the location of the file
		 * pointer after the write is completed, so it is not accessed.
		 * For truncate, 'end' is the size, and *is* acccessed.
		 * In other words, writes are [start, end), but truncate is
		 * [start, size], where both are included.  So add 1 to the
		 * size when creating the write intent to account for this.
		 */
		io->ci_write_intent.e_end =
			io->u.ci_setattr.sa_attr.lvb_size + 1;
	} else {
		io->ci_write_intent.e_start = lio->lis_pos;
		io->ci_write_intent.e_end = lio->lis_endpos;
	}

	CDEBUG(D_LAYOUT, "%llu %llu\n", io->ci_write_intent.e_start,
	       io->ci_write_intent.e_end);

	index = 0;
	lov_foreach_io_layout(index, lio, &io->ci_write_intent) {
		if (!lsm_entry_inited(obj->lo_lsm, index)) {
			io->ci_need_write_intent = 1;
			break;
		}
	}

	if (io->ci_need_write_intent && io->ci_designated_mirror > 0) {
		/*
		 * REINT_SYNC RPC has already tried to instantiate all of the
		 * components involved, obviously it didn't succeed. Skip this
		 * mirror for now. The server won't be able to figure out
		 * which mirror it should instantiate components
		 */
		CERROR(DFID": trying to instantiate components for designated I/O, file state: %d\n",
		       PFID(lu_object_fid(lov2lu(obj))), lov_flr_state(obj));

		io->ci_need_write_intent = 0;
		GOTO(out, result = -EIO);
	}

	if (io->ci_need_write_intent)
		GOTO(out, result = 1);

	EXIT;

out:
	return result;
}

static void lov_io_fini(const struct lu_env *env, const struct cl_io_slice *ios)
{
	struct lov_io *lio = cl2lov_io(env, ios);
	struct lov_object *lov = cl2lov(ios->cis_obj);
	struct lov_io_sub *sub;
	struct cl_io *io = lio->lis_cl.cis_io;

	ENTRY;
	LASSERT(list_empty(&lio->lis_active));

	while ((sub = list_first_entry_or_null(&lio->lis_subios,
					       struct lov_io_sub,
					       sub_list)) != NULL) {
		list_del_init(&sub->sub_list);
		lio->lis_nr_subios--;

		lov_io_sub_fini(env, lio, sub);
		lov_sub_free(lio, sub);
	}
	LASSERT(lio->lis_nr_subios == 0);

	if (!(io->ci_ignore_layout && io->ci_type == CIT_MISC)) {
		LASSERT(atomic_read(&lov->lo_active_ios) > 0);
		if (atomic_dec_and_test(&lov->lo_active_ios))
			wake_up(&lov->lo_waitq);
	}
	EXIT;
}

static void lov_io_sub_inherit(struct lov_io_sub *sub, struct lov_io *lio,
			       loff_t start, loff_t end)
{
	struct cl_io *io = &sub->sub_io;
	struct lov_stripe_md *lsm = lio->lis_object->lo_lsm;
	struct cl_io *parent = lio->lis_cl.cis_io;
	int index = lov_comp_entry(sub->sub_subio_index);
	int stripe = lov_comp_stripe(sub->sub_subio_index);

	switch (io->ci_type) {
	case CIT_SETATTR: {
		io->u.ci_setattr.sa_attr = parent->u.ci_setattr.sa_attr;
		io->u.ci_setattr.sa_attr_flags =
			parent->u.ci_setattr.sa_attr_flags;
		io->u.ci_setattr.sa_avalid = parent->u.ci_setattr.sa_avalid;
		io->u.ci_setattr.sa_xvalid = parent->u.ci_setattr.sa_xvalid;
		io->u.ci_setattr.sa_falloc_mode =
			parent->u.ci_setattr.sa_falloc_mode;
		io->u.ci_setattr.sa_stripe_index = stripe;
		io->u.ci_setattr.sa_parent_fid =
					parent->u.ci_setattr.sa_parent_fid;
		/* For SETATTR(fallocate) pass the subtype to lower IO */
		io->u.ci_setattr.sa_subtype = parent->u.ci_setattr.sa_subtype;
		if (cl_io_is_fallocate(io)) {
			io->u.ci_setattr.sa_falloc_offset = start;
			io->u.ci_setattr.sa_falloc_end = end;
			io->u.ci_setattr.sa_attr_uid =
				parent->u.ci_setattr.sa_attr_uid;
			io->u.ci_setattr.sa_attr_gid =
				parent->u.ci_setattr.sa_attr_gid;
			io->u.ci_setattr.sa_attr_projid =
				parent->u.ci_setattr.sa_attr_projid;
		}
		if (cl_io_is_trunc(io)) {
			loff_t new_size = parent->u.ci_setattr.sa_attr.lvb_size;

			new_size = lov_size_to_stripe(lsm, index, new_size,
						      stripe);
			io->u.ci_setattr.sa_attr.lvb_size = new_size;
			io->u.ci_setattr.sa_attr_uid =
				parent->u.ci_setattr.sa_attr_uid;
			io->u.ci_setattr.sa_attr_gid =
				parent->u.ci_setattr.sa_attr_gid;
			io->u.ci_setattr.sa_attr_projid =
				parent->u.ci_setattr.sa_attr_projid;
		}
		lov_lsm2layout(lsm, lsm->lsm_entries[index],
			       &io->u.ci_setattr.sa_layout);
		break;
	}
	case CIT_DATA_VERSION: {
		io->u.ci_data_version.dv_data_version = 0;
		io->u.ci_data_version.dv_flags =
			parent->u.ci_data_version.dv_flags;
		break;
	}
	case CIT_FAULT: {
		loff_t off = parent->u.ci_fault.ft_index << PAGE_SHIFT;

		io->u.ci_fault = parent->u.ci_fault;
		off = lov_size_to_stripe(lsm, index, off, stripe);
		io->u.ci_fault.ft_index = off >> PAGE_SHIFT;
		break;
	}
	case CIT_FSYNC: {
		io->u.ci_fsync.fi_start = start;
		io->u.ci_fsync.fi_end = end;
		io->u.ci_fsync.fi_fid = parent->u.ci_fsync.fi_fid;
		io->u.ci_fsync.fi_mode = parent->u.ci_fsync.fi_mode;
		io->u.ci_fsync.fi_prio = parent->u.ci_fsync.fi_prio;
		break;
	}
	case CIT_READ:
	case CIT_WRITE: {
		io->u.ci_wr.wr_sync = cl_io_is_sync_write(parent);
		io->ci_tried_all_mirrors = parent->ci_tried_all_mirrors;
		if (cl_io_is_append(parent))
			io->u.ci_wr.wr_append = 1;

		io->u.ci_rw.crw_pos = start;
		io->u.ci_rw.crw_bytes = end - start;
		break;
	}
	case CIT_LADVISE: {
		io->u.ci_ladvise.lio_start = start;
		io->u.ci_ladvise.lio_end = end;
		io->u.ci_ladvise.lio_fid = parent->u.ci_ladvise.lio_fid;
		io->u.ci_ladvise.lio_advice = parent->u.ci_ladvise.lio_advice;
		io->u.ci_ladvise.lio_flags = parent->u.ci_ladvise.lio_flags;
		break;
	}
	case CIT_LSEEK: {
		io->u.ci_lseek.ls_start = start;
		io->u.ci_lseek.ls_whence = parent->u.ci_lseek.ls_whence;
		io->u.ci_lseek.ls_result = parent->u.ci_lseek.ls_result;
		break;
	}
	case CIT_GLIMPSE:
	case CIT_MISC:
	default:
		break;
	}
}

static loff_t lov_offset_mod(loff_t val, int delta)
{
	if (val != OBD_OBJECT_EOF)
		val += delta;
	return val;
}

static int lov_io_add_sub(const struct lu_env *env, struct lov_io *lio,
			  struct lov_io_sub *sub, u64 start, u64 end)
{
	int rc;

	end = lov_offset_mod(end, 1);
	lov_io_sub_inherit(sub, lio, start, end);
	rc = cl_io_iter_init(sub->sub_env, &sub->sub_io);
	if (rc != 0) {
		cl_io_iter_fini(sub->sub_env, &sub->sub_io);
		return rc;
	}

	list_add_tail(&sub->sub_linkage, &lio->lis_active);

	return rc;
}
static int lov_io_iter_init(const struct lu_env *env,
			    const struct cl_io_slice *ios)
{
	struct lov_io *lio = cl2lov_io(env, ios);
	struct lov_stripe_md *lsm = lio->lis_object->lo_lsm;
	bool is_trunc = cl_io_is_trunc(ios->cis_io);
	struct lov_io_sub *sub;
	struct lu_extent ext;
	int index;
	int rc = 0;

	ENTRY;

	ext.e_start = lio->lis_pos;
	ext.e_end = lio->lis_endpos;

	if (is_trunc) {
		OBD_ALLOC_PTR_ARRAY(lio->lis_trunc_stripe_index,
				    lio->lis_object->u.composite.lo_entry_count);
		if (lio->lis_trunc_stripe_index == NULL)
			RETURN(-ENOMEM);
	}

	lov_foreach_io_layout(index, lio, &ext) {
		struct lov_layout_entry *le = lov_entry(lio->lis_object, index);
		struct lov_layout_raid0 *r0 = &le->lle_raid0;
		u64 start;
		u64 end;
		int stripe;
		bool tested_trunc_stripe = false;

		if (is_trunc)
			lio->lis_trunc_stripe_index[index] = -1;

		CDEBUG(D_VFSTRACE, "component[%d] flags %#x\n",
		       index, lsm->lsm_entries[index]->lsme_flags);
		if (!lsm_entry_inited(lsm, index)) {
			/*
			 * Read from uninitialized components should return
			 * zero filled pages.
			 */
			continue;
		}

		if (lsm_entry_is_foreign(lsm, index))
			continue;

		if (!le->lle_valid && !ios->cis_io->ci_designated_mirror) {
			CERROR("I/O to invalid component: %d, mirror: %d\n",
			       index, lio->lis_mirror_index);
			RETURN(-EIO);
		}

		for (stripe = 0; stripe < r0->lo_nr; stripe++) {
			if (!lov_stripe_intersects(lsm, index, stripe,
						   &ext, &start, &end))
				continue;

			if (unlikely(!r0->lo_sub[stripe])) {
				if (ios->cis_io->ci_type == CIT_READ ||
				    ios->cis_io->ci_type == CIT_WRITE ||
				    ios->cis_io->ci_type == CIT_FAULT)
					RETURN(-EIO);

				continue;
			}

			if (is_trunc && !tested_trunc_stripe) {
				int prev;
				u64 tr_start;

				prev = (stripe == 0) ? r0->lo_nr - 1 :
							stripe - 1;
				/**
				 * Only involving previous stripe if the
				 * truncate in this component is at the
				 * beginning of this stripe.
				 */
				tested_trunc_stripe = true;
				if (ext.e_start < lsm->lsm_entries[index]->
							lsme_extent.e_start) {
					/* need previous stripe involvement */
					lio->lis_trunc_stripe_index[index] = prev;
				} else {
					div64_u64_rem(ext.e_start,
						      stripe_width(lsm, index),
						      &tr_start);
					/* tr_start %= stripe_swidth */
					if (tr_start == stripe * lsm->
							lsm_entries[index]->
							lsme_stripe_size)
						lio->lis_trunc_stripe_index[index] = prev;
				}
			}

			/* if the last stripe is the trunc stripeno */
			if (is_trunc &&
			    lio->lis_trunc_stripe_index[index] == stripe)
				lio->lis_trunc_stripe_index[index] = -1;

			sub = lov_sub_get(env, lio,
					  lov_comp_index(index, stripe));
			if (IS_ERR(sub))
				return PTR_ERR(sub);

			rc = lov_io_add_sub(env, lio, sub, start, end);
			if (rc != 0)
				break;
		}
		if (rc != 0)
			break;

		if (is_trunc && lio->lis_trunc_stripe_index[index] != -1) {
			stripe = lio->lis_trunc_stripe_index[index];
			if (unlikely(!r0->lo_sub[stripe])) {
				lio->lis_trunc_stripe_index[index] = -1;
				continue;
			}
			sub = lov_sub_get(env, lio,
					  lov_comp_index(index, stripe));
			if (IS_ERR(sub))
				return PTR_ERR(sub);

			/**
			 * the prev sub could be used by another truncate, we'd
			 * skip it. LU-14128 happends when expand truncate +
			 * read get wrong kms.
			 */
			if (!list_empty(&sub->sub_linkage)) {
				lio->lis_trunc_stripe_index[index] = -1;
				continue;
			}

			(void)lov_stripe_intersects(lsm, index, stripe, &ext,
						    &start, &end);
			rc = lov_io_add_sub(env, lio, sub, start, end);
			if (rc != 0)
				break;

		}
	}
	RETURN(rc);
}

static int lov_io_rw_iter_init(const struct lu_env *env,
			       const struct cl_io_slice *ios)
{
	struct lov_io *lio = cl2lov_io(env, ios);
	struct cl_io *io = ios->cis_io;
	struct lov_stripe_md_entry *lse;
	loff_t start = io->u.ci_rw.crw_pos;
	loff_t next;
	int index;

	LASSERT(io->ci_type == CIT_READ || io->ci_type == CIT_WRITE);
	ENTRY;

	if (cl_io_is_append(io)) {
		struct lov_layout_entry *lle;
		struct lov_mirror_entry *lre;
		loff_t endpos = 0;

		/* cover the whole inited region for append */
		LASSERT(lio->lis_mirror_index >= 0);
		lre = lov_mirror_entry(lio->lis_object, lio->lis_mirror_index);
		lov_foreach_mirror_layout_entry(lio->lis_object, lle, lre) {
			if (!lsme_inited(lle->lle_lsme))
				break;

			endpos = lle->lle_lsme->lsme_extent.e_end;
		}

		lio->lis_pos = 0;
		if (endpos == OBD_OBJECT_EOF || lio->lis_endpos < endpos)
			lio->lis_endpos = endpos;

		io->u.ci_wr.wr_append_lockpos = lio->lis_endpos;
		RETURN(lov_io_iter_init(env, ios));
	}

	index = lov_io_layout_at(lio, io->u.ci_rw.crw_pos);
	if (index < 0) { /* non-existing layout component */
		if (io->ci_type == CIT_READ) {
			/*
			 * TODO: it needs to detect the next component and
			 * then set the next pos
			 */
			io->ci_continue = 0;

			RETURN(lov_io_iter_init(env, ios));
		}

		RETURN(-ENODATA);
	}

	if (!lov_entry(lio->lis_object, index)->lle_valid &&
	    !io->ci_designated_mirror)
		RETURN(io->ci_type == CIT_READ ? -EAGAIN : -EIO);

	lse = lov_lse(lio->lis_object, index);

	if (lsme_is_foreign(lse))
		RETURN(-EINVAL);

	next = MAX_LFS_FILESIZE;
	if (lse->lsme_stripe_count > 1) {
		unsigned long ssize = lse->lsme_stripe_size;

		start = div64_u64(start, ssize);
		next = (start + 1) * ssize;
		if (next <= start * ssize)
			next = MAX_LFS_FILESIZE;
	}

	LASSERTF(io->u.ci_rw.crw_pos >= lse->lsme_extent.e_start,
		 "pos %lld, [%lld, %lld)\n", io->u.ci_rw.crw_pos,
		 lse->lsme_extent.e_start, lse->lsme_extent.e_end);
	next = min_t(__u64, next, lse->lsme_extent.e_end);
	next = min_t(__u64, next, lio->lis_io_endpos);

	io->ci_continue = next < lio->lis_io_endpos;
	io->u.ci_rw.crw_bytes = next - io->u.ci_rw.crw_pos;
	lio->lis_pos    = io->u.ci_rw.crw_pos;
	lio->lis_endpos = io->u.ci_rw.crw_pos + io->u.ci_rw.crw_bytes;
	CDEBUG(D_VFSTRACE,
	       "stripe: %llu chunk: [%llu, %llu) %llu, %zd\n",
	       (__u64)start, lio->lis_pos, lio->lis_endpos,
	       (__u64)lio->lis_io_endpos, io->u.ci_rw.crw_bytes);

	/*
	 * XXX The following call should be optimized: we know, that
	 * [lio->lis_pos, lio->lis_endpos) intersects with exactly one stripe.
	 */
	RETURN(lov_io_iter_init(env, ios));
}

static int lov_io_setattr_iter_init(const struct lu_env *env,
				    const struct cl_io_slice *ios)
{
	struct lov_io *lio = cl2lov_io(env, ios);
	struct cl_io *io = ios->cis_io;
	int index;

	ENTRY;

	if (cl_io_is_trunc(io) && lio->lis_pos > 0) {
		index = lov_io_layout_at(lio, lio->lis_pos - 1);
		/* no entry found for such offset */
		if (index < 0)
			RETURN(io->ci_result = -ENODATA);
	}

	RETURN(lov_io_iter_init(env, ios));
}

static int lov_io_call(const struct lu_env *env, struct lov_io *lio,
		       int (*iofunc)(const struct lu_env *, struct cl_io *))
{
	struct cl_io *parent = lio->lis_cl.cis_io;
	struct lov_io_sub *sub;
	int rc = 0;

	ENTRY;
	list_for_each_entry(sub, &lio->lis_active, sub_linkage) {
		rc = iofunc(sub->sub_env, &sub->sub_io);
		if (rc) {
			/**
			 * fsync race with truncate, we'd continue to other
			 * OST object's fsync to potentially discard
			 * caching pages (osc_cache_writeback_range).
			 */
			if (rc == -ENOENT && parent->ci_type == CIT_FSYNC)
				continue;
			break;
		}

		if (parent->ci_result == 0)
			parent->ci_result = sub->sub_io.ci_result;
	}
	RETURN(rc);
}

static int lov_io_lock(const struct lu_env *env, const struct cl_io_slice *ios)
{
	ENTRY;
	RETURN(lov_io_call(env, cl2lov_io(env, ios), cl_io_lock));
}

static int lov_io_start(const struct lu_env *env, const struct cl_io_slice *ios)
{
	ENTRY;
	RETURN(lov_io_call(env, cl2lov_io(env, ios), cl_io_start));
}

static int lov_io_end_wrapper(const struct lu_env *env, struct cl_io *io)
{
	ENTRY;
	/*
	 * It's possible that lov_io_start() wasn't called against this
	 * sub-io, either because previous sub-io failed, or upper layer
	 * completed IO.
	 */
	if (io->ci_state == CIS_IO_GOING)
		cl_io_end(env, io);
	else
		io->ci_state = CIS_IO_FINISHED;
	RETURN(0);
}

static int lov_io_iter_fini_wrapper(const struct lu_env *env, struct cl_io *io)
{
	cl_io_iter_fini(env, io);
	RETURN(0);
}

static int lov_io_unlock_wrapper(const struct lu_env *env, struct cl_io *io)
{
	cl_io_unlock(env, io);
	RETURN(0);
}

static void lov_io_end(const struct lu_env *env, const struct cl_io_slice *ios)
{
	int rc;

	/* Before ending each i/o, we must set lis_cached_entry to tell the
	 * next i/o not to use stale cached lis information.
	 */
	cl2lov_io(env, ios)->lis_cached_entry = LIS_CACHE_ENTRY_NONE;

	rc = lov_io_call(env, cl2lov_io(env, ios), lov_io_end_wrapper);
	LASSERT(rc == 0);
}

static void
lov_io_data_version_end(const struct lu_env *env, const struct cl_io_slice *ios)
{
	struct lov_io *lio = cl2lov_io(env, ios);
	struct cl_io *parent = lio->lis_cl.cis_io;
	struct cl_data_version_io *pdv = &parent->u.ci_data_version;
	struct lov_io_sub *sub;

	ENTRY;
	list_for_each_entry(sub, &lio->lis_active, sub_linkage) {
		struct cl_data_version_io *sdv = &sub->sub_io.u.ci_data_version;

		lov_io_end_wrapper(sub->sub_env, &sub->sub_io);

		pdv->dv_data_version += sdv->dv_data_version;
		if (pdv->dv_layout_version > sdv->dv_layout_version)
			pdv->dv_layout_version = sdv->dv_layout_version;

		if (parent->ci_result == 0)
			parent->ci_result = sub->sub_io.ci_result;
	}

	EXIT;
}

static void lov_io_iter_fini(const struct lu_env *env,
			     const struct cl_io_slice *ios)
{
	struct lov_io *lio = cl2lov_io(env, ios);
	int rc;

	ENTRY;

	if (lio->lis_trunc_stripe_index != NULL)
		OBD_FREE_PTR_ARRAY(lio->lis_trunc_stripe_index,
				   lio->lis_object->u.composite.lo_entry_count);
	lio->lis_trunc_stripe_index = NULL;

	rc = lov_io_call(env, lio, lov_io_iter_fini_wrapper);
	LASSERT(rc == 0);
	while (!list_empty(&lio->lis_active))
		list_del_init(lio->lis_active.next);
	EXIT;
}

static void lov_io_unlock(const struct lu_env *env,
			  const struct cl_io_slice *ios)
{
	int rc;

	ENTRY;
	rc = lov_io_call(env, cl2lov_io(env, ios), lov_io_unlock_wrapper);
	LASSERT(rc == 0);
	EXIT;
}

static int lov_io_read_ahead_prep(const struct lu_env *env,
				  const struct cl_io_slice *ios,
				  pgoff_t start, struct cl_read_ahead *ra)
{
	struct lov_io		*lio = cl2lov_io(env, ios);
	struct lov_object	*loo = lio->lis_object;
	struct lov_layout_raid0 *r0;
	struct lov_io_sub	*sub;
	loff_t			 offset;
	loff_t			 suboff;
	pgoff_t			 ra_end;
	unsigned int		 pps; /* pages per stripe */
	int			 stripe;
	int			 index;
	int			 rc;

	ENTRY;

	offset = start << PAGE_SHIFT;
	index = lov_io_layout_at(lio, offset);
	if (index < 0 || !lsm_entry_inited(loo->lo_lsm, index) ||
	    lsm_entry_is_foreign(loo->lo_lsm, index))
		RETURN(-ENODATA);

	/* avoid readahead to expand to stale components */
	if (!lov_entry(loo, index)->lle_valid)
		RETURN(-EIO);

	stripe = lov_stripe_number(loo->lo_lsm, index, offset);

	r0 = lov_r0(loo, index);
	if (unlikely(!r0->lo_sub[stripe]))
		RETURN(-EIO);

	sub = lov_sub_get(env, lio, lov_comp_index(index, stripe));
	if (IS_ERR(sub))
		RETURN(PTR_ERR(sub));

	lov_stripe_offset(loo->lo_lsm, index, offset, stripe, &suboff);
	rc = cl_io_read_ahead_prep(sub->sub_env, &sub->sub_io,
				   suboff >> PAGE_SHIFT, ra);

	CDEBUG(D_READA, DFID " cra_end = %lu, stripes = %d, rc = %d\n",
	       PFID(lu_object_fid(lov2lu(loo))), ra->cra_end_idx,
		    r0->lo_nr, rc);
	if (rc != 0)
		RETURN(rc);

	/**
	 * Adjust the stripe index by layout of comp. ra->cra_end is the
	 * maximum page index covered by an underlying DLM lock.
	 * This function converts cra_end from stripe level to file level, and
	 * make sure it's not beyond stripe and component boundary.
	 */

	/* cra_end is stripe level, convert it into file level */
	ra_end = ra->cra_end_idx;
	if (ra_end != CL_PAGE_EOF)
		ra->cra_end_idx = lov_stripe_pgoff(loo->lo_lsm, index,
						   ra_end, stripe);

	/* boundary of current component */
	ra_end = lov_io_extent(lio, index)->e_end >> PAGE_SHIFT;
	if (ra_end != CL_PAGE_EOF && ra->cra_end_idx >= ra_end)
		ra->cra_end_idx = ra_end - 1;

	if (r0->lo_nr == 1) /* single stripe file */
		RETURN(0);

	pps = lov_lse(loo, index)->lsme_stripe_size >> PAGE_SHIFT;

	CDEBUG(D_READA, DFID " max_index = %lu, pps = %u, index = %d, stripe_size = %u, stripe no = %u, start index = %lu\n",
	       PFID(lu_object_fid(lov2lu(loo))), ra->cra_end_idx, pps, index,
	       lov_lse(loo, index)->lsme_stripe_size, stripe, start);

	/* never exceed the end of the stripe */
	ra->cra_end_idx = min_t(pgoff_t, ra->cra_end_idx,
				start + pps - start % pps - 1);
	RETURN(0);
}

static int lov_io_lru_reserve(const struct lu_env *env,
			      const struct cl_io_slice *ios, loff_t pos,
			      size_t bytes)
{
	struct lov_io *lio = cl2lov_io(env, ios);
	struct lov_stripe_md *lsm = lio->lis_object->lo_lsm;
	struct lov_io_sub *sub;
	struct lu_extent ext;
	int index;
	int rc = 0;

	ENTRY;

	ext.e_start = pos;
	ext.e_end = pos + bytes;
	lov_foreach_io_layout(index, lio, &ext) {
		struct lov_layout_entry *le = lov_entry(lio->lis_object, index);
		struct lov_layout_raid0 *r0 = &le->lle_raid0;
		u64 start;
		u64 end;
		int stripe;

		if (!lsm_entry_inited(lsm, index))
			continue;

		if (!le->lle_valid && !ios->cis_io->ci_designated_mirror) {
			CERROR(DFID": I/O to invalid component: %d, mirror: %d\n",
			       PFID(lu_object_fid(lov2lu(lio->lis_object))),
			       index, lio->lis_mirror_index);
			RETURN(-EIO);
		}

		for (stripe = 0; stripe < r0->lo_nr; stripe++) {
			if (!lov_stripe_intersects(lsm, index, stripe,
						   &ext, &start, &end))
				continue;

			if (unlikely(!r0->lo_sub[stripe]))
				RETURN(-EIO);

			sub = lov_sub_get(env, lio,
					  lov_comp_index(index, stripe));
			if (IS_ERR(sub))
				return PTR_ERR(sub);

			rc = cl_io_lru_reserve(sub->sub_env, &sub->sub_io, start,
					       end - start + 1);
			if (rc != 0)
				RETURN(rc);
		}
	}

	RETURN(0);
}

static int lov_dio_submit(const struct lu_env *env,
			  struct cl_io *io,
			  const struct cl_io_slice *ios,
			  enum cl_req_type crt, struct cl_dio_pages *cdp)
{
	struct lov_io *lio = cl2lov_io(env, ios);
	struct lov_io_sub *sub;
	int rc = 0;
	int index;

	ENTRY;

	if (lov_pages_is_empty(cdp)) {
		cl_dio_pages_complete(env, cdp, cdp->cdp_page_count, 0);
		RETURN(0);
	}

	index = cdp->cdp_lov_index;

	sub = lov_sub_get(env, lio, index);
	if (!IS_ERR(sub)) {
		rc = cl_dio_submit_rw(sub->sub_env, &sub->sub_io,
				      crt, cdp);
	} else {
		rc = PTR_ERR(sub);
	}

	RETURN(rc);
}

/**
 * lov_io_submit() - lov implementation of cl_operations::cio_submit() method.
 * @env: lustre execution environment
 * @io: highlevel I/O request
 * @ios: LOV specific IO
 * @crt: Requested transfer type
 * @queue: Page queue
 *
 * lov implementation of cl_operations::cio_submit() method. It takes a list
 * of pages in @queue, splits it into per-stripe sub-lists, invokes
 * cl_io_submit() on underlying devices to submit sub-lists, and then splices
 * everything back.
 *
 * Major complication of this function is a need to handle memory cleansing:
 * cl_io_submit() is called to write out pages as a part of VM memory
 * reclamation, and hence it may not fail due to memory shortages (system
 * dead-locks otherwise). To deal with this, some resources (sub-lists,
 * sub-environment, etc.) are allocated per-device on "startup" (i.e., in a
 * not-memory cleansing context), and in case of memory shortage, these
 * pre-allocated resources are used by lov_io_submit() under
 * lov_device::ld_mutex mutex.
 *
 * Return:
 * * %0 on success
 * * %negative on failure
 */
static int lov_io_submit(const struct lu_env *env,
			 struct cl_io *io,
			 const struct cl_io_slice *ios,
			 enum cl_req_type crt, struct cl_2queue *queue)
{
	struct cl_page_list	*qin = &queue->c2_qin;
	struct lov_io		*lio = cl2lov_io(env, ios);
	struct lov_io_sub	*sub;
	struct cl_page_list	*plist = &lov_env_info(env)->lti_plist;
	struct cl_page		*page = cl_page_list_first(qin);
	struct cl_page		*tmp;
	bool dio = false;
	int index;
	int rc = 0;

	ENTRY;

	if (page->cp_type == CPT_TRANSIENT)
		dio = true;

	cl_page_list_init(plist);
	while (qin->pl_nr > 0) {
		struct cl_2queue  *cl2q = &lov_env_info(env)->lti_cl2q;

		page = cl_page_list_first(qin);
		if (lov_page_is_empty(page)) {
			cl_page_list_move(&queue->c2_qout, qin, page);

			/*
			 * it could only be mirror read to get here therefore
			 * the pages will be transient. We don't care about
			 * the return code of cl_page_prep() at all.
			 */
			LASSERT(page->cp_type == CPT_TRANSIENT);
			cl_page_complete(env, page, crt, 0);
			continue;
		}

		cl_2queue_init(cl2q);
		cl_page_list_move(&cl2q->c2_qin, qin, page);

		index = page->cp_lov_index;
		/* DIO is already split by stripe */
		if (!dio) {
			cl_page_list_for_each_safe(page, tmp, qin) {
				/* this page is not on this stripe */
				if (index != page->cp_lov_index)
					continue;

				cl_page_list_move(&cl2q->c2_qin, qin, page);
			}
		} else {
			cl_page_list_splice(qin, &cl2q->c2_qin);
		}

		sub = lov_sub_get(env, lio, index);
		if (!IS_ERR(sub)) {
			rc = cl_io_submit_rw(sub->sub_env, &sub->sub_io,
					     crt, cl2q);
		} else {
			rc = PTR_ERR(sub);
		}

		cl_page_list_splice(&cl2q->c2_qin, plist);
		cl_page_list_splice(&cl2q->c2_qout, &queue->c2_qout);
		cl_2queue_fini(env, cl2q);

		if (rc != 0)
			break;
	}

	cl_page_list_splice(plist, qin);
	cl_page_list_fini(env, plist);

	RETURN(rc);
}

static int lov_io_commit_async(const struct lu_env *env,
			       const struct cl_io_slice *ios,
			       struct cl_page_list *queue, int from, int to,
			       cl_commit_cbt cb, enum cl_io_priority prio)
{
	struct cl_page_list *plist = &lov_env_info(env)->lti_plist;
	struct lov_io *lio = cl2lov_io(env, ios);
	bool hp = cl_io_high_prio(prio);
	struct lov_io_sub *sub;
	struct cl_page *page;
	int rc = 0;

	ENTRY;

	if (lio->lis_nr_subios == 1) {
		int idx = lio->lis_single_subio_index;

		LASSERT(!lov_page_is_empty(cl_page_list_first(queue)));

		sub = lov_sub_get(env, lio, idx);
		LASSERT(!IS_ERR(sub));
		LASSERT(sub == &lio->lis_single_subio);
		rc = cl_io_commit_async(sub->sub_env, &sub->sub_io, queue,
					from, to, cb, prio);
		RETURN(rc);
	}

	cl_page_list_init(plist);
	while (queue->pl_nr > 0) {
		int stripe_to = to;
		int index;

		LASSERT(plist->pl_nr == 0);
		page = cl_page_list_first(queue);
		LASSERT(!lov_page_is_empty(page));

		cl_page_list_move(plist, queue, page);

		index = page->cp_lov_index;
		while (queue->pl_nr > 0) {
			page = cl_page_list_first(queue);
			if (index != page->cp_lov_index)
				break;

			cl_page_list_move(plist, queue, page);
		}

		if (queue->pl_nr > 0) /* still has more pages */
			stripe_to = PAGE_SIZE;

		sub = lov_sub_get(env, lio, index);
		if (!IS_ERR(sub)) {
			rc = cl_io_commit_async(sub->sub_env, &sub->sub_io,
						plist, from, stripe_to, cb,
						prio);
		} else {
			rc = PTR_ERR(sub);
			break;
		}

		if (plist->pl_nr > 0) /* short write */
			break;

		from = 0;

		if (!hp && lov_comp_entry(index) !=
		    lov_comp_entry(page->cp_lov_index))
			cl_io_extent_release(sub->sub_env, &sub->sub_io, prio);
	}

	if (rc == 0 && hp) {
		list_for_each_entry(sub, &lio->lis_subios, sub_list)
			cl_io_extent_release(sub->sub_env, &sub->sub_io, prio);
	}

	/* for error case, add the page back into the qin list */
	LASSERT(ergo(rc == 0, plist->pl_nr == 0));
	while (plist->pl_nr > 0) {
		/* error occurred, add the uncommitted pages back into queue */
		page = cl_page_list_last(plist);
		cl_page_list_move_head(queue, plist, page);
	}

	RETURN(rc);
}

static int lov_io_fault_start(const struct lu_env *env,
			      const struct cl_io_slice *ios)
{
	struct cl_fault_io *fio;
	struct lov_io      *lio;
	struct lov_io_sub  *sub;
	loff_t offset;
	int entry;
	int stripe;

	ENTRY;

	fio = &ios->cis_io->u.ci_fault;
	lio = cl2lov_io(env, ios);

	/**
	 * LU-14502: ft_page could be an existing cl_page associated with
	 * the vmpage covering the fault index, and the page may still
	 * refer to another mirror of an old IO.
	 */
	if (lov_is_flr(lio->lis_object)) {
		offset = fio->ft_index << PAGE_SHIFT;
		entry = lov_io_layout_at(lio, offset);
		if (entry < 0) {
			CERROR(DFID": page fault index %lu invalid component: %d, mirror: %d\n",
			       PFID(lu_object_fid(&ios->cis_obj->co_lu)),
			       fio->ft_index, entry,
			       lio->lis_mirror_index);
			RETURN(-EIO);
		}
		stripe = lov_stripe_number(lio->lis_object->lo_lsm,
					   entry, offset);

		if (fio->ft_page->cp_lov_index !=
		    lov_comp_index(entry, stripe)) {
			CDEBUG(D_INFO, DFID": page fault at index %lu, at mirror %u comp entry %u stripe %u, "
			       "been used with comp entry %u stripe %u\n",
			       PFID(lu_object_fid(&ios->cis_obj->co_lu)),
			       fio->ft_index, lio->lis_mirror_index,
			       entry, stripe,
			       lov_comp_entry(fio->ft_page->cp_lov_index),
			       lov_comp_stripe(fio->ft_page->cp_lov_index));

			fio->ft_page->cp_lov_index =
					lov_comp_index(entry, stripe);
		}
	}

	sub = lov_sub_get(env, lio, fio->ft_page->cp_lov_index);
	sub->sub_io.u.ci_fault.ft_bytes = fio->ft_bytes;

	RETURN(lov_io_start(env, ios));
}

static int lov_io_setattr_start(const struct lu_env *env,
				const struct cl_io_slice *ios)
{
	struct lov_io *lio = cl2lov_io(env, ios);
	struct cl_io *parent = ios->cis_io;
	struct lov_io_sub *sub;
	struct lov_stripe_md *lsm = lio->lis_object->lo_lsm;

	ENTRY;

	if (cl_io_is_fallocate(parent)) {
		list_for_each_entry(sub, &lio->lis_active, sub_linkage) {
			loff_t size = parent->u.ci_setattr.sa_attr.lvb_size;
			int index = lov_comp_entry(sub->sub_subio_index);
			int stripe = lov_comp_stripe(sub->sub_subio_index);

			size = lov_size_to_stripe(lsm, index, size, stripe);
			sub->sub_io.u.ci_setattr.sa_attr.lvb_size = size;
			sub->sub_io.u.ci_setattr.sa_avalid =
						parent->u.ci_setattr.sa_avalid;
		}
	}

	RETURN(lov_io_start(env, ios));
}

static void lov_io_fsync_end(const struct lu_env *env,
			     const struct cl_io_slice *ios)
{
	struct lov_io *lio = cl2lov_io(env, ios);
	struct lov_io_sub *sub;
	unsigned int *written = &ios->cis_io->u.ci_fsync.fi_nr_written;

	ENTRY;

	*written = 0;
	list_for_each_entry(sub, &lio->lis_active, sub_linkage) {
		struct cl_io *subio = &sub->sub_io;

		lov_io_end_wrapper(sub->sub_env, subio);

		if (subio->ci_result == 0)
			*written += subio->u.ci_fsync.fi_nr_written;
	}
	RETURN_EXIT;
}

static void lov_io_lseek_end(const struct lu_env *env,
			     const struct cl_io_slice *ios)
{
	struct lov_io *lio = cl2lov_io(env, ios);
	struct cl_io *io = lio->lis_cl.cis_io;
	struct lov_stripe_md *lsm = lio->lis_object->lo_lsm;
	struct lov_io_sub *sub;
	loff_t offset = -ENXIO;
	__u64 hole_off = 0;
	bool seek_hole = io->u.ci_lseek.ls_whence == SEEK_HOLE;

	ENTRY;

	list_for_each_entry(sub, &lio->lis_active, sub_linkage) {
		struct cl_io *subio = &sub->sub_io;
		int index = lov_comp_entry(sub->sub_subio_index);
		int stripe = lov_comp_stripe(sub->sub_subio_index);
		loff_t sub_off, lov_off;
		__u64 comp_end = lsm->lsm_entries[index]->lsme_extent.e_end;

		lov_io_end_wrapper(sub->sub_env, subio);

		if (io->ci_result == 0)
			io->ci_result = sub->sub_io.ci_result;

		if (io->ci_result)
			continue;

		CDEBUG(D_INFO, DFID": entry %x stripe %u: SEEK_%s from %lld\n",
		       PFID(lu_object_fid(lov2lu(lio->lis_object))),
		       index, stripe, seek_hole ? "HOLE" : "DATA",
		       subio->u.ci_lseek.ls_start);

		/* first subio with positive result is what we need */
		sub_off = subio->u.ci_lseek.ls_result;
		/* Expected error, offset is out of stripe file size */
		if (sub_off == -ENXIO)
			continue;
		/* Any other errors are not expected with ci_result == 0 */
		if (sub_off < 0) {
			CDEBUG(D_INFO, "unexpected error: rc = %lld\n",
			       sub_off);
			io->ci_result = sub_off;
			continue;
		}
		lov_off = lov_stripe_size(lsm, index, sub_off + 1, stripe) - 1;
		if (lov_off < 0) {
			/* the only way to get negatove lov_off here is too big
			 * result. Return -EOVERFLOW then.
			 */
			io->ci_result = -EOVERFLOW;
			CDEBUG(D_INFO, "offset %llu is too big: rc = %d\n",
			       (u64)lov_off, io->ci_result);
			continue;
		}
		if (lov_off < io->u.ci_lseek.ls_start) {
			io->ci_result = -EINVAL;
			CDEBUG(D_INFO, "offset %lld < start %lld: rc = %d\n",
			       sub_off, io->u.ci_lseek.ls_start, io->ci_result);
			continue;
		}
		/* resulting offset can be out of component range if stripe
		 * object is full and its file size was returned as virtual
		 * hole start. Skip this result, the next component will give
		 * us correct lseek result but keep possible hole offset in
		 * case there is no more components ahead
		 */
		if (lov_off >= comp_end) {
			/* must be SEEK_HOLE case */
			if (likely(seek_hole)) {
				/* save comp end as potential hole offset */
				hole_off = max_t(__u64, comp_end, hole_off);
			} else {
				io->ci_result = -EINVAL;
				CDEBUG(D_INFO,
				       "off %lld >= comp_end %llu: rc = %d\n",
				       lov_off, comp_end, io->ci_result);
			}
			continue;
		}

		CDEBUG(D_INFO, "SEEK_%s: %lld->%lld/%lld: rc = %d\n",
		       seek_hole ? "HOLE" : "DATA",
		       subio->u.ci_lseek.ls_start, sub_off, lov_off,
		       sub->sub_io.ci_result);
		offset = min_t(__u64, offset, lov_off);
	}
	/* no result but some component returns hole as component end */
	if (seek_hole && offset == -ENXIO && hole_off > 0)
		offset = hole_off;

	io->u.ci_lseek.ls_result = offset;
	RETURN_EXIT;
}

static const struct cl_io_operations lov_io_ops = {
	.op = {
		[CIT_READ] = {
			.cio_fini      = lov_io_fini,
			.cio_iter_init = lov_io_rw_iter_init,
			.cio_iter_fini = lov_io_iter_fini,
			.cio_lock      = lov_io_lock,
			.cio_unlock    = lov_io_unlock,
			.cio_start     = lov_io_start,
			.cio_end       = lov_io_end
		},
		[CIT_WRITE] = {
			.cio_fini      = lov_io_fini,
			.cio_iter_init = lov_io_rw_iter_init,
			.cio_iter_fini = lov_io_iter_fini,
			.cio_lock      = lov_io_lock,
			.cio_unlock    = lov_io_unlock,
			.cio_start     = lov_io_start,
			.cio_end       = lov_io_end
		},
		[CIT_SETATTR] = {
			.cio_fini      = lov_io_fini,
			.cio_iter_init = lov_io_setattr_iter_init,
			.cio_iter_fini = lov_io_iter_fini,
			.cio_lock      = lov_io_lock,
			.cio_unlock    = lov_io_unlock,
			.cio_start     = lov_io_setattr_start,
			.cio_end       = lov_io_end
		},
		[CIT_DATA_VERSION] = {
			.cio_fini       = lov_io_fini,
			.cio_iter_init  = lov_io_iter_init,
			.cio_iter_fini  = lov_io_iter_fini,
			.cio_lock       = lov_io_lock,
			.cio_unlock     = lov_io_unlock,
			.cio_start      = lov_io_start,
			.cio_end        = lov_io_data_version_end,
		},
		[CIT_FAULT] = {
			.cio_fini      = lov_io_fini,
			.cio_iter_init = lov_io_iter_init,
			.cio_iter_fini = lov_io_iter_fini,
			.cio_lock      = lov_io_lock,
			.cio_unlock    = lov_io_unlock,
			.cio_start     = lov_io_fault_start,
			.cio_end       = lov_io_end
		},
		[CIT_FSYNC] = {
			.cio_fini      = lov_io_fini,
			.cio_iter_init = lov_io_iter_init,
			.cio_iter_fini = lov_io_iter_fini,
			.cio_lock      = lov_io_lock,
			.cio_unlock    = lov_io_unlock,
			.cio_start     = lov_io_start,
			.cio_end       = lov_io_fsync_end
		},
		[CIT_LADVISE] = {
			.cio_fini      = lov_io_fini,
			.cio_iter_init = lov_io_iter_init,
			.cio_iter_fini = lov_io_iter_fini,
			.cio_lock      = lov_io_lock,
			.cio_unlock    = lov_io_unlock,
			.cio_start     = lov_io_start,
			.cio_end       = lov_io_end
		},
		[CIT_LSEEK] = {
			.cio_fini      = lov_io_fini,
			.cio_iter_init = lov_io_iter_init,
			.cio_iter_fini = lov_io_iter_fini,
			.cio_lock      = lov_io_lock,
			.cio_unlock    = lov_io_unlock,
			.cio_start     = lov_io_start,
			.cio_end       = lov_io_lseek_end
		},
		[CIT_GLIMPSE] = {
			.cio_fini      = lov_io_fini,
		},
		[CIT_MISC] = {
			.cio_fini      = lov_io_fini
		}
	},
	.cio_read_ahead_prep		= lov_io_read_ahead_prep,
	.cio_lru_reserve		= lov_io_lru_reserve,
	.cio_submit			= lov_io_submit,
	.cio_dio_submit			= lov_dio_submit,
	.cio_commit_async		= lov_io_commit_async,
};

/*
 * Empty lov io operations.
 */
static void lov_empty_io_fini(const struct lu_env *env,
			      const struct cl_io_slice *ios)
{
	struct lov_object *lov = cl2lov(ios->cis_obj);
	struct lov_io *lio = cl2lov_io(env, ios);
	struct cl_io *io = lio->lis_cl.cis_io;

	ENTRY;

	if (!(io->ci_type == CIT_MISC && io->ci_ignore_layout) &&
		atomic_dec_and_test(&lov->lo_active_ios))
		wake_up(&lov->lo_waitq);
	EXIT;
}

static int lov_empty_dio_submit(const struct lu_env *env,
				struct cl_io *io,
				const struct cl_io_slice *ios,
				enum cl_req_type crt, struct cl_dio_pages *cdp)
{
	return -EBADF;
}

static int lov_empty_io_submit(const struct lu_env *env,
			       struct cl_io *io,
			       const struct cl_io_slice *ios,
			       enum cl_req_type crt, struct cl_2queue *queue)
{
	return -EBADF;
}

static void lov_empty_impossible(const struct lu_env *env,
				 struct cl_io_slice *ios)
{
	LBUG();
}

#define LOV_EMPTY_IMPOSSIBLE ((void *)lov_empty_impossible)

/*
 * An io operation vector for files without stripes.
 */
static const struct cl_io_operations lov_empty_io_ops = {
	.op = {
		[CIT_READ] = {
			.cio_fini       = lov_empty_io_fini,
#if 0
			.cio_iter_init  = LOV_EMPTY_IMPOSSIBLE,
			.cio_lock       = LOV_EMPTY_IMPOSSIBLE,
			.cio_start      = LOV_EMPTY_IMPOSSIBLE,
			.cio_end        = LOV_EMPTY_IMPOSSIBLE
#endif
		},
		[CIT_WRITE] = {
			.cio_fini      = lov_empty_io_fini,
			.cio_iter_init = LOV_EMPTY_IMPOSSIBLE,
			.cio_lock      = LOV_EMPTY_IMPOSSIBLE,
			.cio_start     = LOV_EMPTY_IMPOSSIBLE,
			.cio_end       = LOV_EMPTY_IMPOSSIBLE
		},
		[CIT_SETATTR] = {
			.cio_fini      = lov_empty_io_fini,
			.cio_iter_init = LOV_EMPTY_IMPOSSIBLE,
			.cio_lock      = LOV_EMPTY_IMPOSSIBLE,
			.cio_start     = LOV_EMPTY_IMPOSSIBLE,
			.cio_end       = LOV_EMPTY_IMPOSSIBLE
		},
		[CIT_FAULT] = {
			.cio_fini      = lov_empty_io_fini,
			.cio_iter_init = LOV_EMPTY_IMPOSSIBLE,
			.cio_lock      = LOV_EMPTY_IMPOSSIBLE,
			.cio_start     = LOV_EMPTY_IMPOSSIBLE,
			.cio_end       = LOV_EMPTY_IMPOSSIBLE
		},
		[CIT_FSYNC] = {
			.cio_fini      = lov_empty_io_fini
		},
		[CIT_LADVISE] = {
			.cio_fini   = lov_empty_io_fini
		},
		[CIT_GLIMPSE] = {
			.cio_fini      = lov_empty_io_fini
		},
		[CIT_MISC] = {
			.cio_fini      = lov_empty_io_fini
		}
	},
	.cio_submit                    = lov_empty_io_submit,
	.cio_dio_submit                = lov_empty_dio_submit,
	.cio_commit_async              = LOV_EMPTY_IMPOSSIBLE
};

int lov_io_init_composite(const struct lu_env *env, struct cl_object *obj,
			  struct cl_io *io)
{
	struct lov_io *lio = lov_env_io(env);
	struct lov_object *lov = cl2lov(obj);
	int result;

	ENTRY;

	INIT_LIST_HEAD(&lio->lis_active);
	result = lov_io_slice_init(env, lio, lov, io);
	if (result)
		GOTO(out, result);

	result = lov_io_subio_init(env, lio, io);
	if (!result) {
		cl_io_slice_add(io, &lio->lis_cl, obj, &lov_io_ops);
		if (!(io->ci_ignore_layout && io->ci_type == CIT_MISC))
			atomic_inc(&lov->lo_active_ios);
	}
	EXIT;
out:
	io->ci_result = result < 0 ? result : 0;
	return result;
}

int lov_io_init_empty(const struct lu_env *env, struct cl_object *obj,
		      struct cl_io *io)
{
	struct lov_object *lov = cl2lov(obj);
	struct lov_io *lio = lov_env_io(env);
	int result = 0;

	ENTRY;

	lio->lis_object = lov;
	switch (io->ci_type) {
	case CIT_MISC:
	case CIT_GLIMPSE:
	case CIT_READ:
		result = 0;
		break;
	case CIT_FSYNC:
	case CIT_LADVISE:
	case CIT_LSEEK:
	case CIT_SETATTR:
	case CIT_DATA_VERSION:
		result = +1;
		break;
	case CIT_WRITE:
		result = -EBADF;
		break;
	case CIT_FAULT:
		result = -EFAULT;
		CERROR("Page fault on a file without stripes: "DFID"\n",
		       PFID(lu_object_fid(&obj->co_lu)));
		break;
	default:
		LBUG();
	}
	if (result == 0) {
		cl_io_slice_add(io, &lio->lis_cl, obj, &lov_empty_io_ops);
		if (!(io->ci_ignore_layout && io->ci_type == CIT_MISC))
			atomic_inc(&lov->lo_active_ios);
	}

	io->ci_result = result < 0 ? result : 0;
	RETURN(result);
}

int lov_io_init_released(const struct lu_env *env, struct cl_object *obj,
			 struct cl_io *io)
{
	struct lov_object *lov = cl2lov(obj);
	struct lov_io *lio = lov_env_io(env);
	int result;

	ENTRY;

	LASSERT(lov->lo_lsm != NULL);
	lio->lis_object = lov;

	switch (io->ci_type) {
	default:
		LASSERTF(0, "invalid type %d\n", io->ci_type);
		result = -EOPNOTSUPP;
		break;
	case CIT_GLIMPSE:
	case CIT_MISC:
	case CIT_FSYNC:
	case CIT_LADVISE:
	case CIT_DATA_VERSION:
		result = 1;
		break;
	case CIT_SETATTR:
		/*
		 * the truncate to 0 is managed by MDT:
		 * - in open, for open O_TRUNC
		 * - in setattr, for truncate
		 */
		/*
		 * the truncate is for size > 0 so triggers a restore,
		 * also trigger a restore for prealloc/punch
		 */
		if (cl_io_is_trunc(io) || cl_io_is_fallocate(io)) {
			io->ci_restore_needed = 1;
			result = -ENODATA;
		} else
			result = 1;
		break;
	case CIT_READ:
	case CIT_WRITE:
	case CIT_FAULT:
	case CIT_LSEEK:
		io->ci_restore_needed = 1;
		result = -ENODATA;
		break;
	}

	if (result == 0) {
		cl_io_slice_add(io, &lio->lis_cl, obj, &lov_empty_io_ops);
		if (!(io->ci_ignore_layout && io->ci_type == CIT_MISC))
			atomic_inc(&lov->lo_active_ios);
	}

	io->ci_result = result < 0 ? result : 0;
	RETURN(result);
}

/* confirm this offset is in the given layout entry */
bool lov_io_layout_at_confirm(struct lov_io *lio, int entry, __u64 offset)
{
	struct lov_object *lov = lio->lis_object;
	struct lov_layout_entry *lle = lov_entry(lov, entry);

	if ((offset >= lle->lle_extent->e_start &&
	     offset < lle->lle_extent->e_end) ||
	    (offset == OBD_OBJECT_EOF &&
	     lle->lle_extent->e_end == OBD_OBJECT_EOF))
		return true;
	return false;
}

/**
 * lov_io_layout_at() - Return the index in composite layout given file offset
 * @lio: Pointer to struct lov_io
 * @offset: Offset in the composite layout
 *
 * Return the index in composite:lo_entries by the file offset
 *
 * Return:
 * * %>=0 composite index where offset is located
 * * %negative on error
 */
int lov_io_layout_at(struct lov_io *lio, __u64 offset)
{
	struct lov_object *lov = lio->lis_object;
	struct lov_layout_composite *comp = &lov->u.composite;
	int start_index = 0;
	int end_index = comp->lo_entry_count - 1;
	int i;

	LASSERT(lov->lo_type == LLT_COMP);

	/* This is actual file offset so nothing can cover eof. */
	if (offset == LUSTRE_EOF)
		return -1;

	if (lov_is_flr(lov)) {
		struct lov_mirror_entry *lre;

		LASSERT(lio->lis_mirror_index >= 0);

		lre = lov_mirror_entry(lov, lio->lis_mirror_index);
		start_index = lre->lre_start;
		end_index = lre->lre_end;
	}

	for (i = start_index; i <= end_index; i++) {
		struct lov_layout_entry *lle = lov_entry(lov, i);

		LASSERT(!lsme_is_foreign(lle->lle_lsme));

		if ((offset >= lle->lle_extent->e_start &&
		     offset < lle->lle_extent->e_end) ||
		    (offset == OBD_OBJECT_EOF &&
		     lle->lle_extent->e_end == OBD_OBJECT_EOF))
			return i;
	}

	return -1;
}

/** @} lov */