162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * NVMe Over Fabrics Target File I/O commands implementation.
462306a36Sopenharmony_ci * Copyright (c) 2017-2018 Western Digital Corporation or its
562306a36Sopenharmony_ci * affiliates.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
862306a36Sopenharmony_ci#include <linux/uio.h>
962306a36Sopenharmony_ci#include <linux/falloc.h>
1062306a36Sopenharmony_ci#include <linux/file.h>
1162306a36Sopenharmony_ci#include <linux/fs.h>
1262306a36Sopenharmony_ci#include "nvmet.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#define NVMET_MIN_MPOOL_OBJ		16
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_civoid nvmet_file_ns_revalidate(struct nvmet_ns *ns)
1762306a36Sopenharmony_ci{
1862306a36Sopenharmony_ci	ns->size = i_size_read(ns->file->f_mapping->host);
1962306a36Sopenharmony_ci}
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_civoid nvmet_file_ns_disable(struct nvmet_ns *ns)
2262306a36Sopenharmony_ci{
2362306a36Sopenharmony_ci	if (ns->file) {
2462306a36Sopenharmony_ci		if (ns->buffered_io)
2562306a36Sopenharmony_ci			flush_workqueue(buffered_io_wq);
2662306a36Sopenharmony_ci		mempool_destroy(ns->bvec_pool);
2762306a36Sopenharmony_ci		ns->bvec_pool = NULL;
2862306a36Sopenharmony_ci		fput(ns->file);
2962306a36Sopenharmony_ci		ns->file = NULL;
3062306a36Sopenharmony_ci	}
3162306a36Sopenharmony_ci}
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ciint nvmet_file_ns_enable(struct nvmet_ns *ns)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	int flags = O_RDWR | O_LARGEFILE;
3662306a36Sopenharmony_ci	int ret = 0;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	if (!ns->buffered_io)
3962306a36Sopenharmony_ci		flags |= O_DIRECT;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	ns->file = filp_open(ns->device_path, flags, 0);
4262306a36Sopenharmony_ci	if (IS_ERR(ns->file)) {
4362306a36Sopenharmony_ci		ret = PTR_ERR(ns->file);
4462306a36Sopenharmony_ci		pr_err("failed to open file %s: (%d)\n",
4562306a36Sopenharmony_ci			ns->device_path, ret);
4662306a36Sopenharmony_ci		ns->file = NULL;
4762306a36Sopenharmony_ci		return ret;
4862306a36Sopenharmony_ci	}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	nvmet_file_ns_revalidate(ns);
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	/*
5362306a36Sopenharmony_ci	 * i_blkbits can be greater than the universally accepted upper bound,
5462306a36Sopenharmony_ci	 * so make sure we export a sane namespace lba_shift.
5562306a36Sopenharmony_ci	 */
5662306a36Sopenharmony_ci	ns->blksize_shift = min_t(u8,
5762306a36Sopenharmony_ci			file_inode(ns->file)->i_blkbits, 12);
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	ns->bvec_pool = mempool_create(NVMET_MIN_MPOOL_OBJ, mempool_alloc_slab,
6062306a36Sopenharmony_ci			mempool_free_slab, nvmet_bvec_cache);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	if (!ns->bvec_pool) {
6362306a36Sopenharmony_ci		ret = -ENOMEM;
6462306a36Sopenharmony_ci		goto err;
6562306a36Sopenharmony_ci	}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	return ret;
6862306a36Sopenharmony_cierr:
6962306a36Sopenharmony_ci	fput(ns->file);
7062306a36Sopenharmony_ci	ns->file = NULL;
7162306a36Sopenharmony_ci	ns->size = 0;
7262306a36Sopenharmony_ci	ns->blksize_shift = 0;
7362306a36Sopenharmony_ci	return ret;
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic ssize_t nvmet_file_submit_bvec(struct nvmet_req *req, loff_t pos,
7762306a36Sopenharmony_ci		unsigned long nr_segs, size_t count, int ki_flags)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	struct kiocb *iocb = &req->f.iocb;
8062306a36Sopenharmony_ci	ssize_t (*call_iter)(struct kiocb *iocb, struct iov_iter *iter);
8162306a36Sopenharmony_ci	struct iov_iter iter;
8262306a36Sopenharmony_ci	int rw;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	if (req->cmd->rw.opcode == nvme_cmd_write) {
8562306a36Sopenharmony_ci		if (req->cmd->rw.control & cpu_to_le16(NVME_RW_FUA))
8662306a36Sopenharmony_ci			ki_flags |= IOCB_DSYNC;
8762306a36Sopenharmony_ci		call_iter = req->ns->file->f_op->write_iter;
8862306a36Sopenharmony_ci		rw = ITER_SOURCE;
8962306a36Sopenharmony_ci	} else {
9062306a36Sopenharmony_ci		call_iter = req->ns->file->f_op->read_iter;
9162306a36Sopenharmony_ci		rw = ITER_DEST;
9262306a36Sopenharmony_ci	}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	iov_iter_bvec(&iter, rw, req->f.bvec, nr_segs, count);
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	iocb->ki_pos = pos;
9762306a36Sopenharmony_ci	iocb->ki_filp = req->ns->file;
9862306a36Sopenharmony_ci	iocb->ki_flags = ki_flags | iocb->ki_filp->f_iocb_flags;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	return call_iter(iocb, &iter);
10162306a36Sopenharmony_ci}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_cistatic void nvmet_file_io_done(struct kiocb *iocb, long ret)
10462306a36Sopenharmony_ci{
10562306a36Sopenharmony_ci	struct nvmet_req *req = container_of(iocb, struct nvmet_req, f.iocb);
10662306a36Sopenharmony_ci	u16 status = NVME_SC_SUCCESS;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	if (req->f.bvec != req->inline_bvec) {
10962306a36Sopenharmony_ci		if (likely(req->f.mpool_alloc == false))
11062306a36Sopenharmony_ci			kfree(req->f.bvec);
11162306a36Sopenharmony_ci		else
11262306a36Sopenharmony_ci			mempool_free(req->f.bvec, req->ns->bvec_pool);
11362306a36Sopenharmony_ci	}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	if (unlikely(ret != req->transfer_len))
11662306a36Sopenharmony_ci		status = errno_to_nvme_status(req, ret);
11762306a36Sopenharmony_ci	nvmet_req_complete(req, status);
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistatic bool nvmet_file_execute_io(struct nvmet_req *req, int ki_flags)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	ssize_t nr_bvec = req->sg_cnt;
12362306a36Sopenharmony_ci	unsigned long bv_cnt = 0;
12462306a36Sopenharmony_ci	bool is_sync = false;
12562306a36Sopenharmony_ci	size_t len = 0, total_len = 0;
12662306a36Sopenharmony_ci	ssize_t ret = 0;
12762306a36Sopenharmony_ci	loff_t pos;
12862306a36Sopenharmony_ci	int i;
12962306a36Sopenharmony_ci	struct scatterlist *sg;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	if (req->f.mpool_alloc && nr_bvec > NVMET_MAX_MPOOL_BVEC)
13262306a36Sopenharmony_ci		is_sync = true;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	pos = le64_to_cpu(req->cmd->rw.slba) << req->ns->blksize_shift;
13562306a36Sopenharmony_ci	if (unlikely(pos + req->transfer_len > req->ns->size)) {
13662306a36Sopenharmony_ci		nvmet_req_complete(req, errno_to_nvme_status(req, -ENOSPC));
13762306a36Sopenharmony_ci		return true;
13862306a36Sopenharmony_ci	}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	memset(&req->f.iocb, 0, sizeof(struct kiocb));
14162306a36Sopenharmony_ci	for_each_sg(req->sg, sg, req->sg_cnt, i) {
14262306a36Sopenharmony_ci		bvec_set_page(&req->f.bvec[bv_cnt], sg_page(sg), sg->length,
14362306a36Sopenharmony_ci			      sg->offset);
14462306a36Sopenharmony_ci		len += req->f.bvec[bv_cnt].bv_len;
14562306a36Sopenharmony_ci		total_len += req->f.bvec[bv_cnt].bv_len;
14662306a36Sopenharmony_ci		bv_cnt++;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci		WARN_ON_ONCE((nr_bvec - 1) < 0);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci		if (unlikely(is_sync) &&
15162306a36Sopenharmony_ci		    (nr_bvec - 1 == 0 || bv_cnt == NVMET_MAX_MPOOL_BVEC)) {
15262306a36Sopenharmony_ci			ret = nvmet_file_submit_bvec(req, pos, bv_cnt, len, 0);
15362306a36Sopenharmony_ci			if (ret < 0)
15462306a36Sopenharmony_ci				goto complete;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci			pos += len;
15762306a36Sopenharmony_ci			bv_cnt = 0;
15862306a36Sopenharmony_ci			len = 0;
15962306a36Sopenharmony_ci		}
16062306a36Sopenharmony_ci		nr_bvec--;
16162306a36Sopenharmony_ci	}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	if (WARN_ON_ONCE(total_len != req->transfer_len)) {
16462306a36Sopenharmony_ci		ret = -EIO;
16562306a36Sopenharmony_ci		goto complete;
16662306a36Sopenharmony_ci	}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	if (unlikely(is_sync)) {
16962306a36Sopenharmony_ci		ret = total_len;
17062306a36Sopenharmony_ci		goto complete;
17162306a36Sopenharmony_ci	}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	/*
17462306a36Sopenharmony_ci	 * A NULL ki_complete ask for synchronous execution, which we want
17562306a36Sopenharmony_ci	 * for the IOCB_NOWAIT case.
17662306a36Sopenharmony_ci	 */
17762306a36Sopenharmony_ci	if (!(ki_flags & IOCB_NOWAIT))
17862306a36Sopenharmony_ci		req->f.iocb.ki_complete = nvmet_file_io_done;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	ret = nvmet_file_submit_bvec(req, pos, bv_cnt, total_len, ki_flags);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	switch (ret) {
18362306a36Sopenharmony_ci	case -EIOCBQUEUED:
18462306a36Sopenharmony_ci		return true;
18562306a36Sopenharmony_ci	case -EAGAIN:
18662306a36Sopenharmony_ci		if (WARN_ON_ONCE(!(ki_flags & IOCB_NOWAIT)))
18762306a36Sopenharmony_ci			goto complete;
18862306a36Sopenharmony_ci		return false;
18962306a36Sopenharmony_ci	case -EOPNOTSUPP:
19062306a36Sopenharmony_ci		/*
19162306a36Sopenharmony_ci		 * For file systems returning error -EOPNOTSUPP, handle
19262306a36Sopenharmony_ci		 * IOCB_NOWAIT error case separately and retry without
19362306a36Sopenharmony_ci		 * IOCB_NOWAIT.
19462306a36Sopenharmony_ci		 */
19562306a36Sopenharmony_ci		if ((ki_flags & IOCB_NOWAIT))
19662306a36Sopenharmony_ci			return false;
19762306a36Sopenharmony_ci		break;
19862306a36Sopenharmony_ci	}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_cicomplete:
20162306a36Sopenharmony_ci	nvmet_file_io_done(&req->f.iocb, ret);
20262306a36Sopenharmony_ci	return true;
20362306a36Sopenharmony_ci}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_cistatic void nvmet_file_buffered_io_work(struct work_struct *w)
20662306a36Sopenharmony_ci{
20762306a36Sopenharmony_ci	struct nvmet_req *req = container_of(w, struct nvmet_req, f.work);
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	nvmet_file_execute_io(req, 0);
21062306a36Sopenharmony_ci}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_cistatic void nvmet_file_submit_buffered_io(struct nvmet_req *req)
21362306a36Sopenharmony_ci{
21462306a36Sopenharmony_ci	INIT_WORK(&req->f.work, nvmet_file_buffered_io_work);
21562306a36Sopenharmony_ci	queue_work(buffered_io_wq, &req->f.work);
21662306a36Sopenharmony_ci}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_cistatic void nvmet_file_execute_rw(struct nvmet_req *req)
21962306a36Sopenharmony_ci{
22062306a36Sopenharmony_ci	ssize_t nr_bvec = req->sg_cnt;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	if (!nvmet_check_transfer_len(req, nvmet_rw_data_len(req)))
22362306a36Sopenharmony_ci		return;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	if (!req->sg_cnt || !nr_bvec) {
22662306a36Sopenharmony_ci		nvmet_req_complete(req, 0);
22762306a36Sopenharmony_ci		return;
22862306a36Sopenharmony_ci	}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	if (nr_bvec > NVMET_MAX_INLINE_BIOVEC)
23162306a36Sopenharmony_ci		req->f.bvec = kmalloc_array(nr_bvec, sizeof(struct bio_vec),
23262306a36Sopenharmony_ci				GFP_KERNEL);
23362306a36Sopenharmony_ci	else
23462306a36Sopenharmony_ci		req->f.bvec = req->inline_bvec;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	if (unlikely(!req->f.bvec)) {
23762306a36Sopenharmony_ci		/* fallback under memory pressure */
23862306a36Sopenharmony_ci		req->f.bvec = mempool_alloc(req->ns->bvec_pool, GFP_KERNEL);
23962306a36Sopenharmony_ci		req->f.mpool_alloc = true;
24062306a36Sopenharmony_ci	} else
24162306a36Sopenharmony_ci		req->f.mpool_alloc = false;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	if (req->ns->buffered_io) {
24462306a36Sopenharmony_ci		if (likely(!req->f.mpool_alloc) &&
24562306a36Sopenharmony_ci		    (req->ns->file->f_mode & FMODE_NOWAIT) &&
24662306a36Sopenharmony_ci		    nvmet_file_execute_io(req, IOCB_NOWAIT))
24762306a36Sopenharmony_ci			return;
24862306a36Sopenharmony_ci		nvmet_file_submit_buffered_io(req);
24962306a36Sopenharmony_ci	} else
25062306a36Sopenharmony_ci		nvmet_file_execute_io(req, 0);
25162306a36Sopenharmony_ci}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ciu16 nvmet_file_flush(struct nvmet_req *req)
25462306a36Sopenharmony_ci{
25562306a36Sopenharmony_ci	return errno_to_nvme_status(req, vfs_fsync(req->ns->file, 1));
25662306a36Sopenharmony_ci}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_cistatic void nvmet_file_flush_work(struct work_struct *w)
25962306a36Sopenharmony_ci{
26062306a36Sopenharmony_ci	struct nvmet_req *req = container_of(w, struct nvmet_req, f.work);
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	nvmet_req_complete(req, nvmet_file_flush(req));
26362306a36Sopenharmony_ci}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_cistatic void nvmet_file_execute_flush(struct nvmet_req *req)
26662306a36Sopenharmony_ci{
26762306a36Sopenharmony_ci	if (!nvmet_check_transfer_len(req, 0))
26862306a36Sopenharmony_ci		return;
26962306a36Sopenharmony_ci	INIT_WORK(&req->f.work, nvmet_file_flush_work);
27062306a36Sopenharmony_ci	queue_work(nvmet_wq, &req->f.work);
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_cistatic void nvmet_file_execute_discard(struct nvmet_req *req)
27462306a36Sopenharmony_ci{
27562306a36Sopenharmony_ci	int mode = FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE;
27662306a36Sopenharmony_ci	struct nvme_dsm_range range;
27762306a36Sopenharmony_ci	loff_t offset, len;
27862306a36Sopenharmony_ci	u16 status = 0;
27962306a36Sopenharmony_ci	int ret;
28062306a36Sopenharmony_ci	int i;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	for (i = 0; i <= le32_to_cpu(req->cmd->dsm.nr); i++) {
28362306a36Sopenharmony_ci		status = nvmet_copy_from_sgl(req, i * sizeof(range), &range,
28462306a36Sopenharmony_ci					sizeof(range));
28562306a36Sopenharmony_ci		if (status)
28662306a36Sopenharmony_ci			break;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci		offset = le64_to_cpu(range.slba) << req->ns->blksize_shift;
28962306a36Sopenharmony_ci		len = le32_to_cpu(range.nlb);
29062306a36Sopenharmony_ci		len <<= req->ns->blksize_shift;
29162306a36Sopenharmony_ci		if (offset + len > req->ns->size) {
29262306a36Sopenharmony_ci			req->error_slba = le64_to_cpu(range.slba);
29362306a36Sopenharmony_ci			status = errno_to_nvme_status(req, -ENOSPC);
29462306a36Sopenharmony_ci			break;
29562306a36Sopenharmony_ci		}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci		ret = vfs_fallocate(req->ns->file, mode, offset, len);
29862306a36Sopenharmony_ci		if (ret && ret != -EOPNOTSUPP) {
29962306a36Sopenharmony_ci			req->error_slba = le64_to_cpu(range.slba);
30062306a36Sopenharmony_ci			status = errno_to_nvme_status(req, ret);
30162306a36Sopenharmony_ci			break;
30262306a36Sopenharmony_ci		}
30362306a36Sopenharmony_ci	}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	nvmet_req_complete(req, status);
30662306a36Sopenharmony_ci}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_cistatic void nvmet_file_dsm_work(struct work_struct *w)
30962306a36Sopenharmony_ci{
31062306a36Sopenharmony_ci	struct nvmet_req *req = container_of(w, struct nvmet_req, f.work);
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	switch (le32_to_cpu(req->cmd->dsm.attributes)) {
31362306a36Sopenharmony_ci	case NVME_DSMGMT_AD:
31462306a36Sopenharmony_ci		nvmet_file_execute_discard(req);
31562306a36Sopenharmony_ci		return;
31662306a36Sopenharmony_ci	case NVME_DSMGMT_IDR:
31762306a36Sopenharmony_ci	case NVME_DSMGMT_IDW:
31862306a36Sopenharmony_ci	default:
31962306a36Sopenharmony_ci		/* Not supported yet */
32062306a36Sopenharmony_ci		nvmet_req_complete(req, 0);
32162306a36Sopenharmony_ci		return;
32262306a36Sopenharmony_ci	}
32362306a36Sopenharmony_ci}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_cistatic void nvmet_file_execute_dsm(struct nvmet_req *req)
32662306a36Sopenharmony_ci{
32762306a36Sopenharmony_ci	if (!nvmet_check_data_len_lte(req, nvmet_dsm_len(req)))
32862306a36Sopenharmony_ci		return;
32962306a36Sopenharmony_ci	INIT_WORK(&req->f.work, nvmet_file_dsm_work);
33062306a36Sopenharmony_ci	queue_work(nvmet_wq, &req->f.work);
33162306a36Sopenharmony_ci}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_cistatic void nvmet_file_write_zeroes_work(struct work_struct *w)
33462306a36Sopenharmony_ci{
33562306a36Sopenharmony_ci	struct nvmet_req *req = container_of(w, struct nvmet_req, f.work);
33662306a36Sopenharmony_ci	struct nvme_write_zeroes_cmd *write_zeroes = &req->cmd->write_zeroes;
33762306a36Sopenharmony_ci	int mode = FALLOC_FL_ZERO_RANGE | FALLOC_FL_KEEP_SIZE;
33862306a36Sopenharmony_ci	loff_t offset;
33962306a36Sopenharmony_ci	loff_t len;
34062306a36Sopenharmony_ci	int ret;
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	offset = le64_to_cpu(write_zeroes->slba) << req->ns->blksize_shift;
34362306a36Sopenharmony_ci	len = (((sector_t)le16_to_cpu(write_zeroes->length) + 1) <<
34462306a36Sopenharmony_ci			req->ns->blksize_shift);
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	if (unlikely(offset + len > req->ns->size)) {
34762306a36Sopenharmony_ci		nvmet_req_complete(req, errno_to_nvme_status(req, -ENOSPC));
34862306a36Sopenharmony_ci		return;
34962306a36Sopenharmony_ci	}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	ret = vfs_fallocate(req->ns->file, mode, offset, len);
35262306a36Sopenharmony_ci	nvmet_req_complete(req, ret < 0 ? errno_to_nvme_status(req, ret) : 0);
35362306a36Sopenharmony_ci}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_cistatic void nvmet_file_execute_write_zeroes(struct nvmet_req *req)
35662306a36Sopenharmony_ci{
35762306a36Sopenharmony_ci	if (!nvmet_check_transfer_len(req, 0))
35862306a36Sopenharmony_ci		return;
35962306a36Sopenharmony_ci	INIT_WORK(&req->f.work, nvmet_file_write_zeroes_work);
36062306a36Sopenharmony_ci	queue_work(nvmet_wq, &req->f.work);
36162306a36Sopenharmony_ci}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ciu16 nvmet_file_parse_io_cmd(struct nvmet_req *req)
36462306a36Sopenharmony_ci{
36562306a36Sopenharmony_ci	switch (req->cmd->common.opcode) {
36662306a36Sopenharmony_ci	case nvme_cmd_read:
36762306a36Sopenharmony_ci	case nvme_cmd_write:
36862306a36Sopenharmony_ci		req->execute = nvmet_file_execute_rw;
36962306a36Sopenharmony_ci		return 0;
37062306a36Sopenharmony_ci	case nvme_cmd_flush:
37162306a36Sopenharmony_ci		req->execute = nvmet_file_execute_flush;
37262306a36Sopenharmony_ci		return 0;
37362306a36Sopenharmony_ci	case nvme_cmd_dsm:
37462306a36Sopenharmony_ci		req->execute = nvmet_file_execute_dsm;
37562306a36Sopenharmony_ci		return 0;
37662306a36Sopenharmony_ci	case nvme_cmd_write_zeroes:
37762306a36Sopenharmony_ci		req->execute = nvmet_file_execute_write_zeroes;
37862306a36Sopenharmony_ci		return 0;
37962306a36Sopenharmony_ci	default:
38062306a36Sopenharmony_ci		return nvmet_report_invalid_opcode(req);
38162306a36Sopenharmony_ci	}
38262306a36Sopenharmony_ci}
383