2018-05-23 00:34:39 -04:00
// SPDX-License-Identifier: GPL-2.0
/*
* NVMe Over Fabrics Target File I / O commands implementation .
* Copyright ( c ) 2017 - 2018 Western Digital Corporation or its
* affiliates .
*/
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
# include <linux/uio.h>
# include <linux/falloc.h>
# include <linux/file.h>
# include "nvmet.h"
# define NVMET_MAX_MPOOL_BVEC 16
# define NVMET_MIN_MPOOL_OBJ 16
2020-04-19 16:48:50 -07:00
int nvmet_file_ns_revalidate ( struct nvmet_ns * ns )
{
struct kstat stat ;
int ret ;
ret = vfs_getattr ( & ns - > file - > f_path , & stat , STATX_SIZE ,
AT_STATX_FORCE_SYNC ) ;
if ( ! ret )
ns - > size = stat . size ;
return ret ;
}
2018-05-23 00:34:39 -04:00
void nvmet_file_ns_disable ( struct nvmet_ns * ns )
{
if ( ns - > file ) {
2018-06-20 00:01:41 -04:00
if ( ns - > buffered_io )
flush_workqueue ( buffered_io_wq ) ;
2018-05-23 00:34:39 -04:00
mempool_destroy ( ns - > bvec_pool ) ;
ns - > bvec_pool = NULL ;
kmem_cache_destroy ( ns - > bvec_cache ) ;
ns - > bvec_cache = NULL ;
fput ( ns - > file ) ;
ns - > file = NULL ;
}
}
int nvmet_file_ns_enable ( struct nvmet_ns * ns )
{
2018-06-20 00:01:41 -04:00
int flags = O_RDWR | O_LARGEFILE ;
int ret ;
if ( ! ns - > buffered_io )
flags | = O_DIRECT ;
2018-05-23 00:34:39 -04:00
2018-06-20 00:01:41 -04:00
ns - > file = filp_open ( ns - > device_path , flags , 0 ) ;
2018-05-23 00:34:39 -04:00
if ( IS_ERR ( ns - > file ) ) {
pr_err ( " failed to open file %s: (%ld) \n " ,
2018-05-31 01:55:58 +00:00
ns - > device_path , PTR_ERR ( ns - > file ) ) ;
2018-05-23 00:34:39 -04:00
return PTR_ERR ( ns - > file ) ;
}
2020-04-19 16:48:50 -07:00
ret = nvmet_file_ns_revalidate ( ns ) ;
2018-05-23 00:34:39 -04:00
if ( ret )
goto err ;
2019-04-24 11:43:23 -07:00
/*
* i_blkbits can be greater than the universally accepted upper bound ,
* so make sure we export a sane namespace lba_shift .
*/
ns - > blksize_shift = min_t ( u8 ,
file_inode ( ns - > file ) - > i_blkbits , 12 ) ;
2018-05-23 00:34:39 -04:00
ns - > bvec_cache = kmem_cache_create ( " nvmet-bvec " ,
NVMET_MAX_MPOOL_BVEC * sizeof ( struct bio_vec ) ,
0 , SLAB_HWCACHE_ALIGN , NULL ) ;
2018-05-31 11:41:30 +00:00
if ( ! ns - > bvec_cache ) {
ret = - ENOMEM ;
2018-05-23 00:34:39 -04:00
goto err ;
2018-05-31 11:41:30 +00:00
}
2018-05-23 00:34:39 -04:00
ns - > bvec_pool = mempool_create ( NVMET_MIN_MPOOL_OBJ , mempool_alloc_slab ,
mempool_free_slab , ns - > bvec_cache ) ;
2018-05-31 11:41:30 +00:00
if ( ! ns - > bvec_pool ) {
ret = - ENOMEM ;
2018-05-23 00:34:39 -04:00
goto err ;
2018-05-31 11:41:30 +00:00
}
2018-05-23 00:34:39 -04:00
return ret ;
err :
ns - > size = 0 ;
ns - > blksize_shift = 0 ;
nvmet_file_ns_disable ( ns ) ;
return ret ;
}
2019-03-27 17:07:22 +08:00
static void nvmet_file_init_bvec ( struct bio_vec * bv , struct scatterlist * sg )
2018-05-23 00:34:39 -04:00
{
2019-03-27 17:07:22 +08:00
bv - > bv_page = sg_page ( sg ) ;
bv - > bv_offset = sg - > offset ;
bv - > bv_len = sg - > length ;
2018-05-23 00:34:39 -04:00
}
static ssize_t nvmet_file_submit_bvec ( struct nvmet_req * req , loff_t pos ,
2018-11-14 01:12:19 -05:00
unsigned long nr_segs , size_t count , int ki_flags )
2018-05-23 00:34:39 -04:00
{
struct kiocb * iocb = & req - > f . iocb ;
ssize_t ( * call_iter ) ( struct kiocb * iocb , struct iov_iter * iter ) ;
struct iov_iter iter ;
2018-11-14 01:12:19 -05:00
int rw ;
2018-05-23 00:34:39 -04:00
if ( req - > cmd - > rw . opcode = = nvme_cmd_write ) {
if ( req - > cmd - > rw . control & cpu_to_le16 ( NVME_RW_FUA ) )
2018-11-14 01:12:19 -05:00
ki_flags | = IOCB_DSYNC ;
2018-05-23 00:34:39 -04:00
call_iter = req - > ns - > file - > f_op - > write_iter ;
rw = WRITE ;
} else {
call_iter = req - > ns - > file - > f_op - > read_iter ;
rw = READ ;
}
2018-10-20 00:57:56 +01:00
iov_iter_bvec ( & iter , rw , req - > f . bvec , nr_segs , count ) ;
2018-05-23 00:34:39 -04:00
iocb - > ki_pos = pos ;
iocb - > ki_filp = req - > ns - > file ;
2018-06-20 00:01:41 -04:00
iocb - > ki_flags = ki_flags | iocb_flags ( req - > ns - > file ) ;
2018-05-23 00:34:39 -04:00
2018-11-14 01:12:19 -05:00
return call_iter ( iocb , & iter ) ;
2018-05-23 00:34:39 -04:00
}
static void nvmet_file_io_done ( struct kiocb * iocb , long ret , long ret2 )
{
struct nvmet_req * req = container_of ( iocb , struct nvmet_req , f . iocb ) ;
2018-12-12 15:11:43 -08:00
u16 status = NVME_SC_SUCCESS ;
2018-05-23 00:34:39 -04:00
if ( req - > f . bvec ! = req - > inline_bvec ) {
if ( likely ( req - > f . mpool_alloc = = false ) )
kfree ( req - > f . bvec ) ;
else
mempool_free ( req - > f . bvec , req - > ns - > bvec_pool ) ;
}
2019-10-23 10:35:44 -06:00
if ( unlikely ( ret ! = req - > transfer_len ) )
2018-12-12 15:11:43 -08:00
status = errno_to_nvme_status ( req , ret ) ;
nvmet_req_complete ( req , status ) ;
2018-05-23 00:34:39 -04:00
}
2018-11-14 01:12:19 -05:00
static bool nvmet_file_execute_io ( struct nvmet_req * req , int ki_flags )
2018-05-23 00:34:39 -04:00
{
2019-03-27 17:07:22 +08:00
ssize_t nr_bvec = req - > sg_cnt ;
2018-05-23 00:34:39 -04:00
unsigned long bv_cnt = 0 ;
bool is_sync = false ;
size_t len = 0 , total_len = 0 ;
ssize_t ret = 0 ;
loff_t pos ;
2019-03-27 17:07:22 +08:00
int i ;
struct scatterlist * sg ;
2018-11-14 01:12:19 -05:00
if ( req - > f . mpool_alloc & & nr_bvec > NVMET_MAX_MPOOL_BVEC )
is_sync = true ;
2018-05-23 00:34:39 -04:00
2018-07-11 16:13:04 +03:00
pos = le64_to_cpu ( req - > cmd - > rw . slba ) < < req - > ns - > blksize_shift ;
2019-10-23 10:35:44 -06:00
if ( unlikely ( pos + req - > transfer_len > req - > ns - > size ) ) {
2018-12-12 15:11:43 -08:00
nvmet_req_complete ( req , errno_to_nvme_status ( req , - ENOSPC ) ) ;
2018-11-14 01:12:19 -05:00
return true ;
2018-05-23 00:34:39 -04:00
}
memset ( & req - > f . iocb , 0 , sizeof ( struct kiocb ) ) ;
2019-03-27 17:07:22 +08:00
for_each_sg ( req - > sg , sg , req - > sg_cnt , i ) {
nvmet_file_init_bvec ( & req - > f . bvec [ bv_cnt ] , sg ) ;
2018-05-23 00:34:39 -04:00
len + = req - > f . bvec [ bv_cnt ] . bv_len ;
total_len + = req - > f . bvec [ bv_cnt ] . bv_len ;
bv_cnt + + ;
WARN_ON_ONCE ( ( nr_bvec - 1 ) < 0 ) ;
if ( unlikely ( is_sync ) & &
( nr_bvec - 1 = = 0 | | bv_cnt = = NVMET_MAX_MPOOL_BVEC ) ) {
2018-11-14 01:12:19 -05:00
ret = nvmet_file_submit_bvec ( req , pos , bv_cnt , len , 0 ) ;
2018-05-23 00:34:39 -04:00
if ( ret < 0 )
2018-11-14 01:12:19 -05:00
goto complete ;
2018-05-23 00:34:39 -04:00
pos + = len ;
bv_cnt = 0 ;
len = 0 ;
}
nr_bvec - - ;
}
2019-10-23 10:35:44 -06:00
if ( WARN_ON_ONCE ( total_len ! = req - > transfer_len ) ) {
2018-05-23 00:34:39 -04:00
ret = - EIO ;
2018-11-14 01:12:19 -05:00
goto complete ;
}
if ( unlikely ( is_sync ) ) {
ret = total_len ;
goto complete ;
2018-05-23 00:34:39 -04:00
}
2018-11-14 01:12:19 -05:00
/*
* A NULL ki_complete ask for synchronous execution , which we want
* for the IOCB_NOWAIT case .
*/
if ( ! ( ki_flags & IOCB_NOWAIT ) )
req - > f . iocb . ki_complete = nvmet_file_io_done ;
ret = nvmet_file_submit_bvec ( req , pos , bv_cnt , total_len , ki_flags ) ;
switch ( ret ) {
case - EIOCBQUEUED :
return true ;
case - EAGAIN :
if ( WARN_ON_ONCE ( ! ( ki_flags & IOCB_NOWAIT ) ) )
goto complete ;
return false ;
case - EOPNOTSUPP :
/*
* For file systems returning error - EOPNOTSUPP , handle
* IOCB_NOWAIT error case separately and retry without
* IOCB_NOWAIT .
*/
if ( ( ki_flags & IOCB_NOWAIT ) )
return false ;
break ;
}
complete :
nvmet_file_io_done ( & req - > f . iocb , ret , 0 ) ;
return true ;
2018-05-23 00:34:39 -04:00
}
2018-06-20 00:01:41 -04:00
static void nvmet_file_buffered_io_work ( struct work_struct * w )
{
struct nvmet_req * req = container_of ( w , struct nvmet_req , f . work ) ;
2018-11-14 01:12:19 -05:00
nvmet_file_execute_io ( req , 0 ) ;
2018-06-20 00:01:41 -04:00
}
2018-11-14 01:12:19 -05:00
static void nvmet_file_submit_buffered_io ( struct nvmet_req * req )
2018-06-20 00:01:41 -04:00
{
INIT_WORK ( & req - > f . work , nvmet_file_buffered_io_work ) ;
queue_work ( buffered_io_wq , & req - > f . work ) ;
}
2018-11-14 01:12:19 -05:00
static void nvmet_file_execute_rw ( struct nvmet_req * req )
{
2019-03-27 17:07:22 +08:00
ssize_t nr_bvec = req - > sg_cnt ;
2018-11-14 01:12:19 -05:00
2020-05-19 17:05:59 +03:00
if ( ! nvmet_check_transfer_len ( req , nvmet_rw_data_len ( req ) ) )
2019-10-23 10:35:44 -06:00
return ;
2018-11-14 01:12:19 -05:00
if ( ! req - > sg_cnt | | ! nr_bvec ) {
nvmet_req_complete ( req , 0 ) ;
return ;
}
if ( nr_bvec > NVMET_MAX_INLINE_BIOVEC )
req - > f . bvec = kmalloc_array ( nr_bvec , sizeof ( struct bio_vec ) ,
GFP_KERNEL ) ;
else
req - > f . bvec = req - > inline_bvec ;
if ( unlikely ( ! req - > f . bvec ) ) {
/* fallback under memory pressure */
req - > f . bvec = mempool_alloc ( req - > ns - > bvec_pool , GFP_KERNEL ) ;
req - > f . mpool_alloc = true ;
} else
req - > f . mpool_alloc = false ;
if ( req - > ns - > buffered_io ) {
if ( likely ( ! req - > f . mpool_alloc ) & &
nvmet_file_execute_io ( req , IOCB_NOWAIT ) )
return ;
nvmet_file_submit_buffered_io ( req ) ;
} else
nvmet_file_execute_io ( req , 0 ) ;
}
2018-08-07 23:01:07 -07:00
u16 nvmet_file_flush ( struct nvmet_req * req )
{
2018-12-12 15:11:43 -08:00
return errno_to_nvme_status ( req , vfs_fsync ( req - > ns - > file , 1 ) ) ;
2018-08-07 23:01:07 -07:00
}
2018-05-23 00:34:39 -04:00
static void nvmet_file_flush_work ( struct work_struct * w )
{
struct nvmet_req * req = container_of ( w , struct nvmet_req , f . work ) ;
2018-08-07 23:01:07 -07:00
nvmet_req_complete ( req , nvmet_file_flush ( req ) ) ;
2018-05-23 00:34:39 -04:00
}
static void nvmet_file_execute_flush ( struct nvmet_req * req )
{
2020-05-19 17:05:59 +03:00
if ( ! nvmet_check_transfer_len ( req , 0 ) )
2019-10-23 10:35:44 -06:00
return ;
2018-05-23 00:34:39 -04:00
INIT_WORK ( & req - > f . work , nvmet_file_flush_work ) ;
schedule_work ( & req - > f . work ) ;
}
static void nvmet_file_execute_discard ( struct nvmet_req * req )
{
int mode = FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE ;
struct nvme_dsm_range range ;
2018-07-11 12:43:16 +03:00
loff_t offset , len ;
2018-12-12 15:11:43 -08:00
u16 status = 0 ;
int ret ;
2018-07-11 12:43:16 +03:00
int i ;
2018-05-23 00:34:39 -04:00
for ( i = 0 ; i < = le32_to_cpu ( req - > cmd - > dsm . nr ) ; i + + ) {
2018-12-12 15:11:43 -08:00
status = nvmet_copy_from_sgl ( req , i * sizeof ( range ) , & range ,
2018-07-11 12:43:16 +03:00
sizeof ( range ) ) ;
2018-12-12 15:11:43 -08:00
if ( status )
2018-05-23 00:34:39 -04:00
break ;
2018-07-11 16:13:04 +03:00
2018-05-23 00:34:39 -04:00
offset = le64_to_cpu ( range . slba ) < < req - > ns - > blksize_shift ;
2018-10-08 14:28:52 -07:00
len = le32_to_cpu ( range . nlb ) ;
len < < = req - > ns - > blksize_shift ;
2018-07-11 16:13:04 +03:00
if ( offset + len > req - > ns - > size ) {
2018-12-12 15:11:43 -08:00
req - > error_slba = le64_to_cpu ( range . slba ) ;
status = errno_to_nvme_status ( req , - ENOSPC ) ;
2018-07-11 16:13:04 +03:00
break ;
}
2018-12-12 15:11:43 -08:00
ret = vfs_fallocate ( req - > ns - > file , mode , offset , len ) ;
2019-03-13 18:55:09 +01:00
if ( ret & & ret ! = - EOPNOTSUPP ) {
2018-12-12 15:11:43 -08:00
req - > error_slba = le64_to_cpu ( range . slba ) ;
status = errno_to_nvme_status ( req , ret ) ;
2018-05-23 00:34:39 -04:00
break ;
2018-07-11 12:43:16 +03:00
}
2018-05-23 00:34:39 -04:00
}
2018-12-12 15:11:43 -08:00
nvmet_req_complete ( req , status ) ;
2018-05-23 00:34:39 -04:00
}
static void nvmet_file_dsm_work ( struct work_struct * w )
{
struct nvmet_req * req = container_of ( w , struct nvmet_req , f . work ) ;
switch ( le32_to_cpu ( req - > cmd - > dsm . attributes ) ) {
case NVME_DSMGMT_AD :
nvmet_file_execute_discard ( req ) ;
return ;
case NVME_DSMGMT_IDR :
case NVME_DSMGMT_IDW :
default :
/* Not supported yet */
nvmet_req_complete ( req , 0 ) ;
return ;
}
}
static void nvmet_file_execute_dsm ( struct nvmet_req * req )
{
2020-01-26 23:23:28 -08:00
if ( ! nvmet_check_data_len_lte ( req , nvmet_dsm_len ( req ) ) )
2019-10-23 10:35:44 -06:00
return ;
2018-05-23 00:34:39 -04:00
INIT_WORK ( & req - > f . work , nvmet_file_dsm_work ) ;
schedule_work ( & req - > f . work ) ;
}
static void nvmet_file_write_zeroes_work ( struct work_struct * w )
{
struct nvmet_req * req = container_of ( w , struct nvmet_req , f . work ) ;
struct nvme_write_zeroes_cmd * write_zeroes = & req - > cmd - > write_zeroes ;
int mode = FALLOC_FL_ZERO_RANGE | FALLOC_FL_KEEP_SIZE ;
loff_t offset ;
loff_t len ;
int ret ;
offset = le64_to_cpu ( write_zeroes - > slba ) < < req - > ns - > blksize_shift ;
len = ( ( ( sector_t ) le16_to_cpu ( write_zeroes - > length ) + 1 ) < <
req - > ns - > blksize_shift ) ;
2018-07-11 16:13:04 +03:00
if ( unlikely ( offset + len > req - > ns - > size ) ) {
2018-12-12 15:11:43 -08:00
nvmet_req_complete ( req , errno_to_nvme_status ( req , - ENOSPC ) ) ;
2018-07-11 16:13:04 +03:00
return ;
}
2018-05-23 00:34:39 -04:00
ret = vfs_fallocate ( req - > ns - > file , mode , offset , len ) ;
2018-12-12 15:11:43 -08:00
nvmet_req_complete ( req , ret < 0 ? errno_to_nvme_status ( req , ret ) : 0 ) ;
2018-05-23 00:34:39 -04:00
}
static void nvmet_file_execute_write_zeroes ( struct nvmet_req * req )
{
2020-05-19 17:05:59 +03:00
if ( ! nvmet_check_transfer_len ( req , 0 ) )
2019-10-23 10:35:44 -06:00
return ;
2018-05-23 00:34:39 -04:00
INIT_WORK ( & req - > f . work , nvmet_file_write_zeroes_work ) ;
schedule_work ( & req - > f . work ) ;
}
u16 nvmet_file_parse_io_cmd ( struct nvmet_req * req )
{
struct nvme_command * cmd = req - > cmd ;
switch ( cmd - > common . opcode ) {
case nvme_cmd_read :
case nvme_cmd_write :
2018-11-14 01:12:19 -05:00
req - > execute = nvmet_file_execute_rw ;
2018-05-23 00:34:39 -04:00
return 0 ;
case nvme_cmd_flush :
req - > execute = nvmet_file_execute_flush ;
return 0 ;
case nvme_cmd_dsm :
req - > execute = nvmet_file_execute_dsm ;
return 0 ;
case nvme_cmd_write_zeroes :
req - > execute = nvmet_file_execute_write_zeroes ;
return 0 ;
default :
pr_err ( " unhandled cmd for file ns %d on qid %d \n " ,
cmd - > common . opcode , req - > sq - > qid ) ;
2018-12-12 15:11:43 -08:00
req - > error_loc = offsetof ( struct nvme_common_command , opcode ) ;
2018-05-23 00:34:39 -04:00
return NVME_SC_INVALID_OPCODE | NVME_SC_DNR ;
}
}