cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

io-cmd-file.c (9678B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * NVMe Over Fabrics Target File I/O commands implementation.
      4 * Copyright (c) 2017-2018 Western Digital Corporation or its
      5 * affiliates.
      6 */
      7#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
      8#include <linux/uio.h>
      9#include <linux/falloc.h>
     10#include <linux/file.h>
     11#include <linux/fs.h>
     12#include "nvmet.h"
     13
     14#define NVMET_MAX_MPOOL_BVEC		16
     15#define NVMET_MIN_MPOOL_OBJ		16
     16
     17void nvmet_file_ns_revalidate(struct nvmet_ns *ns)
     18{
     19	ns->size = i_size_read(ns->file->f_mapping->host);
     20}
     21
     22void nvmet_file_ns_disable(struct nvmet_ns *ns)
     23{
     24	if (ns->file) {
     25		if (ns->buffered_io)
     26			flush_workqueue(buffered_io_wq);
     27		mempool_destroy(ns->bvec_pool);
     28		ns->bvec_pool = NULL;
     29		kmem_cache_destroy(ns->bvec_cache);
     30		ns->bvec_cache = NULL;
     31		fput(ns->file);
     32		ns->file = NULL;
     33	}
     34}
     35
     36int nvmet_file_ns_enable(struct nvmet_ns *ns)
     37{
     38	int flags = O_RDWR | O_LARGEFILE;
     39	int ret = 0;
     40
     41	if (!ns->buffered_io)
     42		flags |= O_DIRECT;
     43
     44	ns->file = filp_open(ns->device_path, flags, 0);
     45	if (IS_ERR(ns->file)) {
     46		ret = PTR_ERR(ns->file);
     47		pr_err("failed to open file %s: (%d)\n",
     48			ns->device_path, ret);
     49		ns->file = NULL;
     50		return ret;
     51	}
     52
     53	nvmet_file_ns_revalidate(ns);
     54
     55	/*
     56	 * i_blkbits can be greater than the universally accepted upper bound,
     57	 * so make sure we export a sane namespace lba_shift.
     58	 */
     59	ns->blksize_shift = min_t(u8,
     60			file_inode(ns->file)->i_blkbits, 12);
     61
     62	ns->bvec_cache = kmem_cache_create("nvmet-bvec",
     63			NVMET_MAX_MPOOL_BVEC * sizeof(struct bio_vec),
     64			0, SLAB_HWCACHE_ALIGN, NULL);
     65	if (!ns->bvec_cache) {
     66		ret = -ENOMEM;
     67		goto err;
     68	}
     69
     70	ns->bvec_pool = mempool_create(NVMET_MIN_MPOOL_OBJ, mempool_alloc_slab,
     71			mempool_free_slab, ns->bvec_cache);
     72
     73	if (!ns->bvec_pool) {
     74		ret = -ENOMEM;
     75		goto err;
     76	}
     77
     78	return ret;
     79err:
     80	ns->size = 0;
     81	ns->blksize_shift = 0;
     82	nvmet_file_ns_disable(ns);
     83	return ret;
     84}
     85
     86static void nvmet_file_init_bvec(struct bio_vec *bv, struct scatterlist *sg)
     87{
     88	bv->bv_page = sg_page(sg);
     89	bv->bv_offset = sg->offset;
     90	bv->bv_len = sg->length;
     91}
     92
     93static ssize_t nvmet_file_submit_bvec(struct nvmet_req *req, loff_t pos,
     94		unsigned long nr_segs, size_t count, int ki_flags)
     95{
     96	struct kiocb *iocb = &req->f.iocb;
     97	ssize_t (*call_iter)(struct kiocb *iocb, struct iov_iter *iter);
     98	struct iov_iter iter;
     99	int rw;
    100
    101	if (req->cmd->rw.opcode == nvme_cmd_write) {
    102		if (req->cmd->rw.control & cpu_to_le16(NVME_RW_FUA))
    103			ki_flags |= IOCB_DSYNC;
    104		call_iter = req->ns->file->f_op->write_iter;
    105		rw = WRITE;
    106	} else {
    107		call_iter = req->ns->file->f_op->read_iter;
    108		rw = READ;
    109	}
    110
    111	iov_iter_bvec(&iter, rw, req->f.bvec, nr_segs, count);
    112
    113	iocb->ki_pos = pos;
    114	iocb->ki_filp = req->ns->file;
    115	iocb->ki_flags = ki_flags | iocb_flags(req->ns->file);
    116
    117	return call_iter(iocb, &iter);
    118}
    119
    120static void nvmet_file_io_done(struct kiocb *iocb, long ret)
    121{
    122	struct nvmet_req *req = container_of(iocb, struct nvmet_req, f.iocb);
    123	u16 status = NVME_SC_SUCCESS;
    124
    125	if (req->f.bvec != req->inline_bvec) {
    126		if (likely(req->f.mpool_alloc == false))
    127			kfree(req->f.bvec);
    128		else
    129			mempool_free(req->f.bvec, req->ns->bvec_pool);
    130	}
    131
    132	if (unlikely(ret != req->transfer_len))
    133		status = errno_to_nvme_status(req, ret);
    134	nvmet_req_complete(req, status);
    135}
    136
    137static bool nvmet_file_execute_io(struct nvmet_req *req, int ki_flags)
    138{
    139	ssize_t nr_bvec = req->sg_cnt;
    140	unsigned long bv_cnt = 0;
    141	bool is_sync = false;
    142	size_t len = 0, total_len = 0;
    143	ssize_t ret = 0;
    144	loff_t pos;
    145	int i;
    146	struct scatterlist *sg;
    147
    148	if (req->f.mpool_alloc && nr_bvec > NVMET_MAX_MPOOL_BVEC)
    149		is_sync = true;
    150
    151	pos = le64_to_cpu(req->cmd->rw.slba) << req->ns->blksize_shift;
    152	if (unlikely(pos + req->transfer_len > req->ns->size)) {
    153		nvmet_req_complete(req, errno_to_nvme_status(req, -ENOSPC));
    154		return true;
    155	}
    156
    157	memset(&req->f.iocb, 0, sizeof(struct kiocb));
    158	for_each_sg(req->sg, sg, req->sg_cnt, i) {
    159		nvmet_file_init_bvec(&req->f.bvec[bv_cnt], sg);
    160		len += req->f.bvec[bv_cnt].bv_len;
    161		total_len += req->f.bvec[bv_cnt].bv_len;
    162		bv_cnt++;
    163
    164		WARN_ON_ONCE((nr_bvec - 1) < 0);
    165
    166		if (unlikely(is_sync) &&
    167		    (nr_bvec - 1 == 0 || bv_cnt == NVMET_MAX_MPOOL_BVEC)) {
    168			ret = nvmet_file_submit_bvec(req, pos, bv_cnt, len, 0);
    169			if (ret < 0)
    170				goto complete;
    171
    172			pos += len;
    173			bv_cnt = 0;
    174			len = 0;
    175		}
    176		nr_bvec--;
    177	}
    178
    179	if (WARN_ON_ONCE(total_len != req->transfer_len)) {
    180		ret = -EIO;
    181		goto complete;
    182	}
    183
    184	if (unlikely(is_sync)) {
    185		ret = total_len;
    186		goto complete;
    187	}
    188
    189	/*
    190	 * A NULL ki_complete ask for synchronous execution, which we want
    191	 * for the IOCB_NOWAIT case.
    192	 */
    193	if (!(ki_flags & IOCB_NOWAIT))
    194		req->f.iocb.ki_complete = nvmet_file_io_done;
    195
    196	ret = nvmet_file_submit_bvec(req, pos, bv_cnt, total_len, ki_flags);
    197
    198	switch (ret) {
    199	case -EIOCBQUEUED:
    200		return true;
    201	case -EAGAIN:
    202		if (WARN_ON_ONCE(!(ki_flags & IOCB_NOWAIT)))
    203			goto complete;
    204		return false;
    205	case -EOPNOTSUPP:
    206		/*
    207		 * For file systems returning error -EOPNOTSUPP, handle
    208		 * IOCB_NOWAIT error case separately and retry without
    209		 * IOCB_NOWAIT.
    210		 */
    211		if ((ki_flags & IOCB_NOWAIT))
    212			return false;
    213		break;
    214	}
    215
    216complete:
    217	nvmet_file_io_done(&req->f.iocb, ret);
    218	return true;
    219}
    220
    221static void nvmet_file_buffered_io_work(struct work_struct *w)
    222{
    223	struct nvmet_req *req = container_of(w, struct nvmet_req, f.work);
    224
    225	nvmet_file_execute_io(req, 0);
    226}
    227
    228static void nvmet_file_submit_buffered_io(struct nvmet_req *req)
    229{
    230	INIT_WORK(&req->f.work, nvmet_file_buffered_io_work);
    231	queue_work(buffered_io_wq, &req->f.work);
    232}
    233
    234static void nvmet_file_execute_rw(struct nvmet_req *req)
    235{
    236	ssize_t nr_bvec = req->sg_cnt;
    237
    238	if (!nvmet_check_transfer_len(req, nvmet_rw_data_len(req)))
    239		return;
    240
    241	if (!req->sg_cnt || !nr_bvec) {
    242		nvmet_req_complete(req, 0);
    243		return;
    244	}
    245
    246	if (nr_bvec > NVMET_MAX_INLINE_BIOVEC)
    247		req->f.bvec = kmalloc_array(nr_bvec, sizeof(struct bio_vec),
    248				GFP_KERNEL);
    249	else
    250		req->f.bvec = req->inline_bvec;
    251
    252	if (unlikely(!req->f.bvec)) {
    253		/* fallback under memory pressure */
    254		req->f.bvec = mempool_alloc(req->ns->bvec_pool, GFP_KERNEL);
    255		req->f.mpool_alloc = true;
    256	} else
    257		req->f.mpool_alloc = false;
    258
    259	if (req->ns->buffered_io) {
    260		if (likely(!req->f.mpool_alloc) &&
    261		    (req->ns->file->f_mode & FMODE_NOWAIT) &&
    262		    nvmet_file_execute_io(req, IOCB_NOWAIT))
    263			return;
    264		nvmet_file_submit_buffered_io(req);
    265	} else
    266		nvmet_file_execute_io(req, 0);
    267}
    268
    269u16 nvmet_file_flush(struct nvmet_req *req)
    270{
    271	return errno_to_nvme_status(req, vfs_fsync(req->ns->file, 1));
    272}
    273
    274static void nvmet_file_flush_work(struct work_struct *w)
    275{
    276	struct nvmet_req *req = container_of(w, struct nvmet_req, f.work);
    277
    278	nvmet_req_complete(req, nvmet_file_flush(req));
    279}
    280
    281static void nvmet_file_execute_flush(struct nvmet_req *req)
    282{
    283	if (!nvmet_check_transfer_len(req, 0))
    284		return;
    285	INIT_WORK(&req->f.work, nvmet_file_flush_work);
    286	queue_work(nvmet_wq, &req->f.work);
    287}
    288
    289static void nvmet_file_execute_discard(struct nvmet_req *req)
    290{
    291	int mode = FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE;
    292	struct nvme_dsm_range range;
    293	loff_t offset, len;
    294	u16 status = 0;
    295	int ret;
    296	int i;
    297
    298	for (i = 0; i <= le32_to_cpu(req->cmd->dsm.nr); i++) {
    299		status = nvmet_copy_from_sgl(req, i * sizeof(range), &range,
    300					sizeof(range));
    301		if (status)
    302			break;
    303
    304		offset = le64_to_cpu(range.slba) << req->ns->blksize_shift;
    305		len = le32_to_cpu(range.nlb);
    306		len <<= req->ns->blksize_shift;
    307		if (offset + len > req->ns->size) {
    308			req->error_slba = le64_to_cpu(range.slba);
    309			status = errno_to_nvme_status(req, -ENOSPC);
    310			break;
    311		}
    312
    313		ret = vfs_fallocate(req->ns->file, mode, offset, len);
    314		if (ret && ret != -EOPNOTSUPP) {
    315			req->error_slba = le64_to_cpu(range.slba);
    316			status = errno_to_nvme_status(req, ret);
    317			break;
    318		}
    319	}
    320
    321	nvmet_req_complete(req, status);
    322}
    323
    324static void nvmet_file_dsm_work(struct work_struct *w)
    325{
    326	struct nvmet_req *req = container_of(w, struct nvmet_req, f.work);
    327
    328	switch (le32_to_cpu(req->cmd->dsm.attributes)) {
    329	case NVME_DSMGMT_AD:
    330		nvmet_file_execute_discard(req);
    331		return;
    332	case NVME_DSMGMT_IDR:
    333	case NVME_DSMGMT_IDW:
    334	default:
    335		/* Not supported yet */
    336		nvmet_req_complete(req, 0);
    337		return;
    338	}
    339}
    340
    341static void nvmet_file_execute_dsm(struct nvmet_req *req)
    342{
    343	if (!nvmet_check_data_len_lte(req, nvmet_dsm_len(req)))
    344		return;
    345	INIT_WORK(&req->f.work, nvmet_file_dsm_work);
    346	queue_work(nvmet_wq, &req->f.work);
    347}
    348
    349static void nvmet_file_write_zeroes_work(struct work_struct *w)
    350{
    351	struct nvmet_req *req = container_of(w, struct nvmet_req, f.work);
    352	struct nvme_write_zeroes_cmd *write_zeroes = &req->cmd->write_zeroes;
    353	int mode = FALLOC_FL_ZERO_RANGE | FALLOC_FL_KEEP_SIZE;
    354	loff_t offset;
    355	loff_t len;
    356	int ret;
    357
    358	offset = le64_to_cpu(write_zeroes->slba) << req->ns->blksize_shift;
    359	len = (((sector_t)le16_to_cpu(write_zeroes->length) + 1) <<
    360			req->ns->blksize_shift);
    361
    362	if (unlikely(offset + len > req->ns->size)) {
    363		nvmet_req_complete(req, errno_to_nvme_status(req, -ENOSPC));
    364		return;
    365	}
    366
    367	ret = vfs_fallocate(req->ns->file, mode, offset, len);
    368	nvmet_req_complete(req, ret < 0 ? errno_to_nvme_status(req, ret) : 0);
    369}
    370
    371static void nvmet_file_execute_write_zeroes(struct nvmet_req *req)
    372{
    373	if (!nvmet_check_transfer_len(req, 0))
    374		return;
    375	INIT_WORK(&req->f.work, nvmet_file_write_zeroes_work);
    376	queue_work(nvmet_wq, &req->f.work);
    377}
    378
    379u16 nvmet_file_parse_io_cmd(struct nvmet_req *req)
    380{
    381	switch (req->cmd->common.opcode) {
    382	case nvme_cmd_read:
    383	case nvme_cmd_write:
    384		req->execute = nvmet_file_execute_rw;
    385		return 0;
    386	case nvme_cmd_flush:
    387		req->execute = nvmet_file_execute_flush;
    388		return 0;
    389	case nvme_cmd_dsm:
    390		req->execute = nvmet_file_execute_dsm;
    391		return 0;
    392	case nvme_cmd_write_zeroes:
    393		req->execute = nvmet_file_execute_write_zeroes;
    394		return 0;
    395	default:
    396		return nvmet_report_invalid_opcode(req);
    397	}
    398}