[RHEL7,COMMIT] fuse/kio_pcs: FIEMAP support

Submitted by Konstantin Khorenko on April 27, 2018, 9:09 a.m.

Details

Message ID 201804270909.w3R991XU031463@finist_ce7.work
State New
Series "fuse/kio_pcs: FIEMAP support"
Headers show

Commit Message

Konstantin Khorenko April 27, 2018, 9:09 a.m.
The commit is pushed to "branch-rh7-3.10.0-693.21.1.vz7.47.x-ovz" and will appear at https://src.openvz.org/scm/ovz/vzkernel.git
after rh7-3.10.0-693.21.1.vz7.46.7
------>
commit 9227091012d80d24aecbbae42c13c3510e73d4c4
Author: Alexey Kuznetsov <kuznet@virtuozzo.com>
Date:   Fri Apr 27 12:09:01 2018 +0300

    fuse/kio_pcs: FIEMAP support
    
    Signed-off-by: Alexey Kuznetsov <kuznet@virtuozzo.com>
    Signed-off-by: Dmitry Monakhov <dmonakhov@openvz.org>
---
 fs/fuse/file.c                     |   1 +
 fs/fuse/kio/pcs/fuse_io.c          |  42 +++++++-
 fs/fuse/kio/pcs/pcs_cluster.c      | 207 +++++++++++++++++++++++++++++++++++++
 fs/fuse/kio/pcs/pcs_cluster.h      |   2 +-
 fs/fuse/kio/pcs/pcs_cs.c           |  22 +++-
 fs/fuse/kio/pcs/pcs_fuse_kdirect.c |  63 ++++++++---
 fs/fuse/kio/pcs/pcs_map.c          |  28 +++--
 fs/fuse/kio/pcs/pcs_req.c          |   6 ++
 fs/fuse/kio/pcs/pcs_req.h          |   1 +
 9 files changed, 344 insertions(+), 28 deletions(-)

Patch hide | download patch | download mbox

diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 43733920fd7e..d84dd402c83c 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -3794,6 +3794,7 @@  static int fuse_request_fiemap(struct inode *inode, u32 cur_max,
 	if (IS_ERR(req))
 		return PTR_ERR(req);
 
+	req->io_inode = inode;
 	req->in.h.opcode = FUSE_IOCTL;
 	req->in.h.nodeid = get_node_id(inode);
 
diff --git a/fs/fuse/kio/pcs/fuse_io.c b/fs/fuse/kio/pcs/fuse_io.c
index 47950d26757f..65c4a1881d89 100644
--- a/fs/fuse/kio/pcs/fuse_io.c
+++ b/fs/fuse/kio/pcs/fuse_io.c
@@ -84,6 +84,16 @@  static void on_fallocate_done(struct pcs_fuse_req *r, off_t pos, size_t size)
 	request_end(pfc->fc, &r->req);
 }
 
+static void on_fiemap_done(struct pcs_fuse_req *r)
+{
+	struct pcs_fuse_cluster *pfc = cl_from_req(r);
+
+	DTRACE("do fuse_request_end req:%p op:%d err:%d\n", &r->req, r->req.in.h.opcode, r->req.out.h.error);
+
+	inode_dio_end(r->req.io_inode);
+	request_end(pfc->fc, &r->req);
+}
+
 static void req_get_iter(void *data, unsigned int offset, struct iov_iter *it)
 {
 	struct pcs_fuse_req *r = data;
@@ -92,6 +102,22 @@  static void req_get_iter(void *data, unsigned int offset, struct iov_iter *it)
 	iov_iter_advance(it, offset);
 }
 
+static void req_fiemap_get_iter(void *data, unsigned int offset, struct iov_iter *it)
+{
+	struct pcs_fuse_req * r = data;
+	struct pcs_int_request *ireq = &r->exec.ireq;
+
+	if (offset < sizeof(struct fiemap)) {
+		iov_iter_init_plain(it, (char *)r->req.out.args[1].value,
+				    sizeof(struct fiemap), 0);
+	} else {
+		offset -= sizeof(struct fiemap);
+		iov_iter_init_bvec(it, r->exec.io.bvec, r->exec.io.num_bvecs,
+				   ireq->apireq.aux*sizeof(struct fiemap_extent), 0);
+	}
+	iov_iter_advance(it, offset);
+}
+
 static inline void set_io_buff(struct pcs_fuse_req *r, off_t offset, size_t size,
 			       int is_bvec, int zeroing)
 {
@@ -126,7 +152,7 @@  static inline void set_io_buff(struct pcs_fuse_req *r, off_t offset, size_t size
 	r->exec.io.req.size = size;
 }
 
-static void prepare_io_(struct pcs_fuse_req *r, unsigned short type, off_t offset, size_t size,
+static void prepare_io_(struct pcs_fuse_req *r, unsigned short type, off_t offset, size_t size, u64 aux,
 		       void (*complete)(struct _pcs_api_iorequest_t *))
 {
 	/* Use inline request structure */
@@ -150,6 +176,9 @@  static void prepare_io_(struct pcs_fuse_req *r, unsigned short type, off_t offse
 		r->exec.io.req.pos = offset;
 		r->exec.io.req.size = size;
 		break;
+	case PCS_REQ_T_FIEMAP:
+		set_io_buff(r, offset, size, 0, 0);
+		break;
 	}
 
 	r->exec.io.req.type = type;
@@ -157,10 +186,14 @@  static void prepare_io_(struct pcs_fuse_req *r, unsigned short type, off_t offse
 	r->exec.io.req.get_iter = req_get_iter;
 	r->exec.io.req.complete = complete;
 
+	if (type == PCS_REQ_T_FIEMAP)
+		r->exec.io.req.get_iter = req_fiemap_get_iter;
+
 	/* Initialize internal request structure */
 	ireq->type = PCS_IREQ_API;
 	ireq->ts = ktime_get();
 	ireq->apireq.req = &r->exec.io.req;
+	ireq->apireq.aux = aux;
 	ireq->complete_cb = intreq_complete;
 	ireq->completion_data.parent = 0;
 	ireq->completion_data.ctx = r;
@@ -196,15 +229,18 @@  static void ioreq_complete(pcs_api_iorequest_t *ioreq)
 	case PCS_REQ_T_WRITE_ZERO:
 		on_fallocate_done(r, ioreq->pos, ioreq->size);
 		break;
+	case PCS_REQ_T_FIEMAP:
+		on_fiemap_done(r);
+		break;
 	default:
 		BUG();
 	}
 
 }
 
-void pcs_fuse_prep_io(struct pcs_fuse_req *r, unsigned short type, off_t offset, size_t size)
+void pcs_fuse_prep_io(struct pcs_fuse_req *r, unsigned short type, off_t offset, size_t size, u64 aux)
 {
-	prepare_io_(r, type, offset, size, ioreq_complete);
+	prepare_io_(r, type, offset, size, aux, ioreq_complete);
 }
 
 static void falloc_req_complete(struct pcs_int_request *ireq)
diff --git a/fs/fuse/kio/pcs/pcs_cluster.c b/fs/fuse/kio/pcs/pcs_cluster.c
index 2d988484ec94..b995135a35cc 100644
--- a/fs/fuse/kio/pcs/pcs_cluster.c
+++ b/fs/fuse/kio/pcs/pcs_cluster.c
@@ -3,6 +3,7 @@ 
 #include <linux/kthread.h>
 #include <linux/types.h>
 #include <linux/rbtree.h>
+#include <linux/highmem.h>
 
 #include "pcs_types.h"
 #include "pcs_sock_io.h"
@@ -16,6 +17,8 @@ 
 
 #include "../../fuse_i.h"
 
+void pcs_cc_process_ireq_chunk(struct pcs_int_request *ireq);
+
 static inline int is_file_inline(struct pcs_dentry_info *di)
 {
 	return di->fileinfo.attr.attrib & PCS_FATTR_INLINE;
@@ -75,6 +78,192 @@  void pcs_sreq_complete(struct pcs_int_request *sreq)
 	ireq_destroy(sreq);
 }
 
+struct fiemap_iterator
+{
+	struct pcs_int_request 	*orig_ireq;
+	wait_queue_head_t	wq;
+	char			*buffer;
+	unsigned int		fiemap_max;
+	u32			*mapped;
+
+	struct pcs_int_request	ireq;
+	pcs_api_iorequest_t	apireq;
+};
+
+static void fiemap_iter_done(struct pcs_int_request * ireq)
+{
+	struct fiemap_iterator * fiter = container_of(ireq, struct fiemap_iterator, ireq);
+
+	wake_up(&fiter->wq);
+}
+
+static void fiemap_get_iter(void * datasource, unsigned int offset, struct iov_iter *it)
+{
+	struct fiemap_iterator * iter = datasource;
+
+	BUG_ON(offset >= PCS_FIEMAP_BUFSIZE);
+	iov_iter_init_plain(it, iter->buffer, PCS_FIEMAP_BUFSIZE, 0);
+	iov_iter_advance(it, offset);
+}
+
+static void xfer_fiemap_extents(struct fiemap_iterator * iter, u64 pos, char * buffer, unsigned int count)
+{
+	struct pcs_cs_fiemap_rec * r = (struct pcs_cs_fiemap_rec *)buffer;
+
+	BUG_ON(count % sizeof(struct pcs_cs_fiemap_rec));
+	count /= sizeof(struct pcs_cs_fiemap_rec);
+
+	if (r[count - 1].flags & PCS_CS_FIEMAP_FL_OVFL) {
+		/* Adjust next scan pos in case of overflow, overwriting size */
+		u64 end = iter->apireq.pos + r[count - 1].offset + r[count - 1].size;
+		if (end < pos + iter->apireq.size) {
+			u64 adjusted_size = end - pos;
+			if (adjusted_size < iter->apireq.size)
+				iter->apireq.size = adjusted_size;
+		}
+	}
+
+	if (iter->fiemap_max == 0) {
+		*iter->mapped += count;
+	} else {
+		int i;
+
+		for (i = 0; i < count; i++) {
+			struct fiemap_extent e;
+			struct iov_iter it;
+			void * buf;
+			size_t len;
+
+			if (*iter->mapped >= iter->fiemap_max)
+				return;
+
+			memset(&e, 0, sizeof(e));
+			e.fe_logical = e.fe_physical = iter->apireq.pos + r[i].offset;
+			e.fe_length = r[i].size;
+			if (r[i].flags & PCS_CS_FIEMAP_FL_ZERO)
+				e.fe_flags |= FIEMAP_EXTENT_UNWRITTEN;
+			if (r[i].flags & PCS_CS_FIEMAP_FL_CACHE)
+				e.fe_flags |= FIEMAP_EXTENT_DELALLOC;
+
+			iter->orig_ireq->apireq.req->get_iter(iter->orig_ireq->apireq.req->datasource,
+							      offsetof(struct fiemap, fm_extents) +
+							      *iter->mapped * sizeof(struct fiemap_extent),
+							      &it);
+			iov_iter_truncate(&it, sizeof(e));
+
+			iov_iter_kmap_atomic(&it, &buf, &len);
+			memcpy(buf, &e, len);
+			kunmap_atomic(buf);
+			if (len != sizeof(e)) {
+				size_t fraglen;
+				iov_iter_advance(&it, len);
+				iov_iter_kmap_atomic(&it, &buf, &fraglen);
+				BUG_ON(len + fraglen != sizeof(e));
+				memcpy(buf, (char*)&e + len, fraglen);
+				kunmap_atomic(buf);
+			}
+			(*iter->mapped)++;
+		}
+	}
+}
+
+static int fiemap_worker(void * arg)
+{
+	struct pcs_int_request * orig_ireq = arg;
+	struct pcs_dentry_info * di;
+	struct fiemap_iterator * fiter;
+	struct iov_iter it;
+	u64 pos, end;
+
+	fiter = kmalloc(sizeof(struct fiemap_iterator), GFP_KERNEL);
+	if (fiter == NULL) {
+		pcs_set_local_error(&orig_ireq->error, PCS_ERR_NOMEM);
+		ireq_complete(orig_ireq);
+		return 0;
+	}
+
+	fiter->orig_ireq = orig_ireq;
+	init_waitqueue_head(&fiter->wq);
+	di = orig_ireq->dentry;
+	ireq_init(di, &fiter->ireq);
+	fiter->ireq.type = PCS_IREQ_API;
+	fiter->ireq.apireq.req = &fiter->apireq;
+	fiter->ireq.completion_data.parent = NULL;
+	fiter->ireq.complete_cb = fiemap_iter_done;
+	fiter->apireq.datasource = fiter;
+	fiter->apireq.get_iter = fiemap_get_iter;
+	fiter->apireq.complete = NULL;
+	fiter->buffer = kvmalloc(PCS_FIEMAP_BUFSIZE, GFP_KERNEL);
+	if (fiter->buffer == NULL) {
+		pcs_set_local_error(&orig_ireq->error, PCS_ERR_NOMEM);
+		ireq_complete(orig_ireq);
+		kfree(fiter);
+		return 0;
+	}
+	fiter->fiemap_max = orig_ireq->apireq.aux;
+	orig_ireq->apireq.req->get_iter(orig_ireq->apireq.req->datasource, 0, &it);
+	fiter->mapped = &((struct fiemap*)it.data)->fm_mapped_extents;
+
+	pos = fiter->orig_ireq->apireq.req->pos;
+	end = pos + fiter->orig_ireq->apireq.req->size;
+	while (pos < end) {
+		struct pcs_int_request * sreq;
+
+		if (fiter->fiemap_max && *fiter->mapped >= fiter->fiemap_max)
+			break;
+
+		fiter->apireq.pos = pos;
+		fiter->apireq.size = end - pos;
+		fiter->ireq.ts = ktime_get();
+
+		sreq = ireq_alloc(di);
+		if (!sreq) {
+			pcs_set_local_error(&orig_ireq->error, PCS_ERR_NOMEM);
+			goto out;
+		}
+
+		atomic_set(&fiter->ireq.iocount, 0);
+
+		sreq->dentry = di;
+		sreq->type = PCS_IREQ_IOCHUNK;
+		sreq->iochunk.map = NULL;
+		sreq->iochunk.flow = pcs_flow_record(&di->mapping.ftab, 0, pos, end-pos, &di->cluster->maps.ftab);
+		sreq->iochunk.cmd = PCS_REQ_T_FIEMAP;
+		sreq->iochunk.cs_index = 0;
+		sreq->iochunk.chunk = pos;
+		sreq->iochunk.offset = 0;
+		sreq->iochunk.dio_offset = 0;
+		sreq->iochunk.size = end - pos;
+		sreq->iochunk.csl = NULL;
+		sreq->iochunk.banned_cs.val = 0;
+		sreq->iochunk.msg.destructor = NULL;
+		sreq->iochunk.msg.rpc = NULL;
+
+		pcs_sreq_attach(sreq, &fiter->ireq);
+		sreq->complete_cb = pcs_sreq_complete;
+
+		pcs_cc_process_ireq_chunk(sreq);
+
+		wait_event(fiter->wq, atomic_read(&fiter->ireq.iocount) == 0);
+
+		if (pcs_if_error(&fiter->ireq.error)) {
+			fiter->orig_ireq->error = fiter->ireq.error;
+			goto out;
+		}
+
+		if (fiter->ireq.apireq.aux)
+			xfer_fiemap_extents(fiter, pos, fiter->buffer, fiter->ireq.apireq.aux);
+
+		pos += fiter->apireq.size;
+	}
+
+out:
+	kvfree(fiter->buffer);
+	kfree(fiter);
+	ireq_complete(orig_ireq);
+	return 0;
+}
+
 void pcs_cc_process_ireq_chunk(struct pcs_int_request *ireq)
 {
 	struct pcs_map_entry *map;
@@ -167,12 +356,30 @@  static noinline void __pcs_cc_process_ireq_rw(struct pcs_int_request *ireq)
 		ireq_complete(ireq);
 }
 
+static void process_ireq_fiemap(struct pcs_int_request * ireq)
+{
+	struct task_struct * tsk;
+
+	tsk = kthread_run(fiemap_worker, ireq, "fiemap-worker");
+
+	if (IS_ERR(tsk)) {
+		pcs_set_local_error(&ireq->error, PCS_ERR_NOMEM);
+		ireq_complete(ireq);
+	}
+}
+
 static void pcs_cc_process_ireq_ioreq(struct pcs_int_request *ireq)
 {
 	if (ireq->apireq.req->type == PCS_REQ_T_SYNC) {
 		map_inject_flush_req(ireq);
 		return;
 	}
+
+	if (ireq->apireq.req->type == PCS_REQ_T_FIEMAP) {
+		process_ireq_fiemap(ireq);
+		return;
+	}
+
 	if (ireq->apireq.req->type != PCS_REQ_T_READ &&
 	    ireq->apireq.req->type != PCS_REQ_T_WRITE &&
 	    ireq->apireq.req->type != PCS_REQ_T_WRITE_HOLE &&
diff --git a/fs/fuse/kio/pcs/pcs_cluster.h b/fs/fuse/kio/pcs/pcs_cluster.h
index f1c20797d951..0f13345f919f 100644
--- a/fs/fuse/kio/pcs/pcs_cluster.h
+++ b/fs/fuse/kio/pcs/pcs_cluster.h
@@ -101,7 +101,7 @@  int pcs_cc_init(struct pcs_cluster_core *cc, struct workqueue_struct *wq,
 		const char *cluster_name, struct pcs_cluster_core_attr *attr);
 void pcs_cc_fini(struct pcs_cluster_core *cc);
 
-void pcs_fuse_prep_io(struct pcs_fuse_req *r, unsigned short type, off_t offset, size_t size);
+void pcs_fuse_prep_io(struct pcs_fuse_req *r, unsigned short type, off_t offset, size_t size, u64 aux);
 void pcs_fuse_prep_fallocate(struct pcs_fuse_req *r);
 int fuse_pcs_csconn_send(struct fuse_conn *fc, struct pcs_rpc *ep, int flags);
 
diff --git a/fs/fuse/kio/pcs/pcs_cs.c b/fs/fuse/kio/pcs/pcs_cs.c
index 1fecaeaff16b..ba3c0fea5fe4 100644
--- a/fs/fuse/kio/pcs/pcs_cs.c
+++ b/fs/fuse/kio/pcs/pcs_cs.c
@@ -317,6 +317,7 @@  void pcs_cs_update_stat(struct pcs_cs *cs, u32 iolat, u32 netlat, int op_type)
 static void cs_response_done(struct pcs_msg *msg)
 {
 	struct pcs_int_request *ireq = ireq_from_msg(msg);
+	unsigned int resp_size = 0;
 
 	if (!pcs_if_error(&msg->error)) {
 		struct pcs_cs_iohdr *h = (struct pcs_cs_iohdr *)msg_inline_head(msg->response);
@@ -324,6 +325,8 @@  static void cs_response_done(struct pcs_msg *msg)
 		if (h->sync.misc & PCS_CS_IO_CACHED)
 			ireq->flags |= IREQ_F_CACHED;
 
+		resp_size = h->hdr.len - sizeof(struct pcs_cs_iohdr);
+
 		pcs_map_verify_sync_state(ireq->dentry, ireq, msg);
 	} else {
 		TRACE(XID_FMT " IO error %d %lu : %llu:%u+%u\n", XID_ARGS(ireq->iochunk.hbuf.hdr.xid),
@@ -336,6 +339,10 @@  static void cs_response_done(struct pcs_msg *msg)
 		pcs_rpc_put(msg->rpc);
 		msg->rpc = NULL;
 	}
+	if (ireq->type == PCS_IREQ_IOCHUNK && ireq->iochunk.cmd == PCS_REQ_T_FIEMAP) {
+		ireq->completion_data.parent->apireq.aux = resp_size;
+		ireq->completion_data.parent->apireq.req->pos = ireq->iochunk.chunk;
+	}
 	ireq_complete(ireq);
 }
 
@@ -360,7 +367,7 @@  static void cs_get_read_response_iter(struct pcs_msg *msg, int offset, struct io
 
 			offset -= (unsigned int)sizeof(struct pcs_cs_iohdr);
 			ar->get_iter(ar->datasource, ireq->iochunk.dio_offset, it);
-			iov_iter_truncate(it, ireq->iochunk.size);
+			iov_iter_truncate(it, msg->size - sizeof(struct pcs_cs_iohdr));
 			iov_iter_advance(it, offset);
 
 			TRACE("return msg:%p->size:%d off:%d it_len:%ld\n\n", msg, msg->size, offset, iov_iter_count(it));
@@ -384,6 +391,7 @@  static struct pcs_msg *cs_get_hdr(struct pcs_rpc *ep, struct pcs_rpc_hdr *h)
 {
 	struct pcs_msg *msg, *resp;
 	struct pcs_rpc_hdr *req_h;
+	struct pcs_int_request *ireq;
 
 	if (!RPC_IS_RESPONSE(h->type))
 		return NULL;
@@ -401,6 +409,18 @@  static struct pcs_msg *cs_get_hdr(struct pcs_rpc *ep, struct pcs_rpc_hdr *h)
 	if (req_h->type != (h->type & ~PCS_RPC_DIRECTION))
 		return NULL;
 
+	ireq = msg->private2;
+	if (ireq->type != PCS_IREQ_IOCHUNK)
+		return NULL;
+	if (ireq->iochunk.cmd == PCS_REQ_T_READ) {
+		if (ireq->iochunk.size + sizeof(struct pcs_cs_iohdr) != msg->size)
+			return NULL;
+	} else if (ireq->iochunk.cmd == PCS_REQ_T_FIEMAP) {
+		if (PCS_FIEMAP_BUFSIZE + sizeof(struct pcs_cs_iohdr) < msg->size)
+			return NULL;
+	} else
+		return NULL;
+
 	resp = pcs_rpc_alloc_input_msg(ep, sizeof(struct pcs_cs_iohdr));
 	if (!resp)
 		return NULL;
diff --git a/fs/fuse/kio/pcs/pcs_fuse_kdirect.c b/fs/fuse/kio/pcs/pcs_fuse_kdirect.c
index 70a7b38e3692..c371fe0ef51f 100644
--- a/fs/fuse/kio/pcs/pcs_fuse_kdirect.c
+++ b/fs/fuse/kio/pcs/pcs_fuse_kdirect.c
@@ -748,7 +748,7 @@  static int pcs_fuse_prep_rw(struct pcs_fuse_req *r)
 			}
 			size = di->fileinfo.attr.size - in->offset;
 		}
-		pcs_fuse_prep_io(r, PCS_REQ_T_READ, in->offset, size);
+		pcs_fuse_prep_io(r, PCS_REQ_T_READ, in->offset, size, 0);
 	} else if (r->req.in.h.opcode == FUSE_WRITE) {
 		struct fuse_write_in *in = &r->req.misc.write.in;
 
@@ -756,7 +756,26 @@  static int pcs_fuse_prep_rw(struct pcs_fuse_req *r)
 			wait_grow(r, di, in->offset + in->size);
 			ret = 1;
 		}
-		pcs_fuse_prep_io(r, PCS_REQ_T_WRITE, in->offset, in->size);
+		pcs_fuse_prep_io(r, PCS_REQ_T_WRITE, in->offset, in->size, 0);
+	} else if (r->req.in.h.opcode == FUSE_IOCTL) {
+		size_t size;
+		struct fiemap const *in = r->req.in.args[1].value;
+		struct fiemap *out = r->req.out.args[1].value;
+
+		*out = *in;
+		out->fm_mapped_extents = 0;
+
+		size = in->fm_length;
+		if (in->fm_start + size > di->fileinfo.attr.size) {
+			if (in->fm_start >= di->fileinfo.attr.size) {
+				spin_unlock(&di->lock);
+				return -1;
+			}
+			size = di->fileinfo.attr.size - in->fm_start;
+		}
+		pcs_fuse_prep_io(r, PCS_REQ_T_FIEMAP, in->fm_start, in->fm_extent_count*sizeof(struct fiemap_extent),
+				 in->fm_extent_count);
+		r->exec.io.req.size = size;
 	} else {
 		struct fuse_fallocate_in const *in = r->req.in.args[0].value;
 
@@ -766,9 +785,9 @@  static int pcs_fuse_prep_rw(struct pcs_fuse_req *r)
 		}
 
 		if (in->mode & FALLOC_FL_PUNCH_HOLE)
-			pcs_fuse_prep_io(r, PCS_REQ_T_WRITE_HOLE, in->offset, in->length);
+			pcs_fuse_prep_io(r, PCS_REQ_T_WRITE_HOLE, in->offset, in->length, 0);
 		else if (in->mode & FALLOC_FL_ZERO_RANGE)
-			pcs_fuse_prep_io(r, PCS_REQ_T_WRITE_ZERO, in->offset, in->length);
+			pcs_fuse_prep_io(r, PCS_REQ_T_WRITE_ZERO, in->offset, in->length, 0);
 		else {
 			if (ret) {
 				pcs_fuse_prep_fallocate(r);
@@ -846,8 +865,24 @@  static void pcs_fuse_submit(struct pcs_fuse_cluster *pfc, struct fuse_req *req,
 		break;
 	}
 	case FUSE_FSYNC:
-		pcs_fuse_prep_io(r, PCS_REQ_T_SYNC, 0, 0);
+		pcs_fuse_prep_io(r, PCS_REQ_T_SYNC, 0, 0, 0);
 		goto submit;
+	case FUSE_IOCTL: {
+		int ret;
+
+		if (pfc->fc->no_fiemap) {
+			r->req.out.h.error = -EOPNOTSUPP;
+			goto error;
+		}
+
+		ret = pcs_fuse_prep_rw(r);
+		if (!ret)
+			goto submit;
+		if (ret > 0)
+			/* Pended, nothing to do. */
+			return;
+		break;
+	}
 	}
 	r->req.out.h.error = 0;
 error:
@@ -926,6 +961,7 @@  static int kpcs_req_send(struct fuse_conn* fc, struct fuse_req *req, bool bg, bo
 {
 	struct pcs_fuse_cluster *pfc = (struct pcs_fuse_cluster*)fc->kio.ctx;
 	struct fuse_inode *fi = get_fuse_inode(req->io_inode);
+
 	if (!fc->initialized || fc->conn_error)
 		return 1;
 
@@ -946,6 +982,10 @@  static int kpcs_req_send(struct fuse_conn* fc, struct fuse_req *req, bool bg, bo
 		spin_unlock(&fc->lock);
 		return 1;
 	}
+
+	if (!fi || !fi->private)
+		return 1;
+
 	switch (req->in.h.opcode) {
 	case FUSE_SETATTR: {
 		struct pcs_fuse_req *r = pcs_req_from_fuse(req);
@@ -953,10 +993,6 @@  static int kpcs_req_send(struct fuse_conn* fc, struct fuse_req *req, bool bg, bo
 		struct pcs_dentry_info *di;
 		int shrink = 0;
 
-		/* Skip speciall inodes */
-		if (!fi->private)
-			return 1;
-
 		if (!(inarg->valid & FATTR_SIZE))
 			return 1;
 
@@ -986,16 +1022,19 @@  static int kpcs_req_send(struct fuse_conn* fc, struct fuse_req *req, bool bg, bo
 	case FUSE_WRITE:
 	case FUSE_FSYNC:
 	case FUSE_FALLOCATE:
-		fi = get_fuse_inode(req->io_inode);
-		if (!fi->private)
+		break;
+	case FUSE_IOCTL: {
+		struct fuse_ioctl_in const * inarg = req->in.args[0].value;
+
+		if (inarg->cmd != FS_IOC_FIEMAP)
 			return 1;
 
 		break;
+	}
 	default:
 		return 1;
 	}
 
-
 	__clear_bit(FR_BACKGROUND, &req->flags);
 	__clear_bit(FR_PENDING, &req->flags);
 	/* request_end below will do fuse_put_request() */
diff --git a/fs/fuse/kio/pcs/pcs_map.c b/fs/fuse/kio/pcs/pcs_map.c
index 65b71c69ec8d..705743056462 100644
--- a/fs/fuse/kio/pcs/pcs_map.c
+++ b/fs/fuse/kio/pcs/pcs_map.c
@@ -2204,17 +2204,23 @@  void map_submit(struct pcs_map_entry * m, struct pcs_int_request *ireq, int requ
 			BUG_ON(ireq->iochunk.chunk != map_chunk_start(m));
 			BUG_ON(ireq->iochunk.offset != pos - ireq->iochunk.chunk);
 			if (ireq->iochunk.size > len) {
-				struct pcs_int_request * sreq;
-
-				sreq = pcs_ireq_split(ireq, len, 0);
-				if (ireq->iochunk.map) {
-					pcs_map_put(ireq->iochunk.map);
-					ireq->iochunk.map = NULL;
+				if (ireq->iochunk.cmd == PCS_REQ_T_FIEMAP) {
+					pcs_api_iorequest_t * ar = ireq->completion_data.parent->apireq.req;
+					ireq->iochunk.size = len;
+					ar->size = ireq->iochunk.size;
+				} else {
+					struct pcs_int_request * sreq;
+
+					sreq = pcs_ireq_split(ireq, len, 0);
+					if (ireq->iochunk.map) {
+						pcs_map_put(ireq->iochunk.map);
+						ireq->iochunk.map = NULL;
+					}
+					ireq->iochunk.chunk = map_chunk_end(m);
+					ireq->iochunk.offset = 0;
+					pcs_cc_submit(ireq->dentry->cluster, ireq);
+					ireq = sreq;
 				}
-				ireq->iochunk.chunk = map_chunk_end(m);
-				ireq->iochunk.offset = 0;
-				pcs_cc_submit(ireq->dentry->cluster, ireq);
-				ireq = sreq;
 			}
 		}
 
@@ -2549,7 +2555,7 @@  static int commit_sync_info(struct pcs_int_request *req,
 	update_net_latency(csl, resp->rpc->peer_id, &h->sync, lat);
 	max_iolat = h->sync.ts_io;
 
-	if (h->hdr.type != PCS_CS_READ_RESP) {
+	if (h->hdr.type != PCS_CS_READ_RESP && h->hdr.type != PCS_CS_FIEMAP_RESP) {
 		struct pcs_cs_sync_resp * srec;
 		lat = h->sync.ts_net;
 		for (srec = (struct pcs_cs_sync_resp*)(h + 1);
diff --git a/fs/fuse/kio/pcs/pcs_req.c b/fs/fuse/kio/pcs/pcs_req.c
index f1c364fe5f00..a05f1e43c94c 100644
--- a/fs/fuse/kio/pcs/pcs_req.c
+++ b/fs/fuse/kio/pcs/pcs_req.c
@@ -89,6 +89,12 @@  void ireq_handle_hole(struct pcs_int_request *ireq)
 	BUG_ON(ireq->type != PCS_IREQ_IOCHUNK);
 	BUG_ON(pcs_req_direction(ireq->iochunk.cmd));
 
+	if (ireq->iochunk.cmd == PCS_REQ_T_FIEMAP) {
+		ireq->completion_data.parent->apireq.aux = 0;
+		ireq_complete(ireq);
+		return;
+	}
+
 	len = ireq->iochunk.size;
 	offset = 0;
 	iov_iter_init_bad(&it);
diff --git a/fs/fuse/kio/pcs/pcs_req.h b/fs/fuse/kio/pcs/pcs_req.h
index bf603e0af414..fba74e9c4a56 100644
--- a/fs/fuse/kio/pcs/pcs_req.h
+++ b/fs/fuse/kio/pcs/pcs_req.h
@@ -150,6 +150,7 @@  struct pcs_int_request
 		struct {
 			pcs_api_iorequest_t *	req;		/* Client request */
 			unsigned int		dio_offset;	/* MBZ */
+			u64			aux;
 			void*			h;		/* API handle */
 		} apireq;