2016-06-13 16:45:26 +02:00
/*
* NVMe over Fabrics common host code .
* Copyright ( c ) 2015 - 2016 HGST , a Western Digital Company .
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms and conditions of the GNU General Public License ,
* version 2 , as published by the Free Software Foundation .
*
* This program is distributed in the hope it will be useful , but WITHOUT
* ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License for
* more details .
*/
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
# include <linux/init.h>
# include <linux/miscdevice.h>
# include <linux/module.h>
# include <linux/mutex.h>
# include <linux/parser.h>
# include <linux/seq_file.h>
# include "nvme.h"
# include "fabrics.h"
static LIST_HEAD ( nvmf_transports ) ;
static DEFINE_MUTEX ( nvmf_transports_mutex ) ;
static LIST_HEAD ( nvmf_hosts ) ;
static DEFINE_MUTEX ( nvmf_hosts_mutex ) ;
static struct nvmf_host * nvmf_default_host ;
static struct nvmf_host * __nvmf_host_find ( const char * hostnqn )
{
struct nvmf_host * host ;
list_for_each_entry ( host , & nvmf_hosts , list ) {
if ( ! strcmp ( host - > nqn , hostnqn ) )
return host ;
}
return NULL ;
}
static struct nvmf_host * nvmf_host_add ( const char * hostnqn )
{
struct nvmf_host * host ;
mutex_lock ( & nvmf_hosts_mutex ) ;
host = __nvmf_host_find ( hostnqn ) ;
if ( host )
goto out_unlock ;
host = kmalloc ( sizeof ( * host ) , GFP_KERNEL ) ;
if ( ! host )
goto out_unlock ;
kref_init ( & host - > ref ) ;
memcpy ( host - > nqn , hostnqn , NVMF_NQN_SIZE ) ;
uuid_le_gen ( & host - > id ) ;
list_add_tail ( & host - > list , & nvmf_hosts ) ;
out_unlock :
mutex_unlock ( & nvmf_hosts_mutex ) ;
return host ;
}
static struct nvmf_host * nvmf_host_default ( void )
{
struct nvmf_host * host ;
host = kmalloc ( sizeof ( * host ) , GFP_KERNEL ) ;
if ( ! host )
return NULL ;
kref_init ( & host - > ref ) ;
uuid_le_gen ( & host - > id ) ;
snprintf ( host - > nqn , NVMF_NQN_SIZE ,
" nqn.2014-08.org.nvmexpress:NVMf:uuid:%pUl " , & host - > id ) ;
mutex_lock ( & nvmf_hosts_mutex ) ;
list_add_tail ( & host - > list , & nvmf_hosts ) ;
mutex_unlock ( & nvmf_hosts_mutex ) ;
return host ;
}
static void nvmf_host_destroy ( struct kref * ref )
{
struct nvmf_host * host = container_of ( ref , struct nvmf_host , ref ) ;
nvme-fabrics: add-remove ctrl repeat fix
Repeatedly adding then removing the same NVMe-over-Fabrics controller
over and over again (shown below) can cause a kernel crash (also shown
below). This patch fixes that.
[nvmf]# ./setup_nvme_connections.sh
traddr=192.168.1.100,transport=rdma,trsvcid=4420,nqn=darkside
-nqn,hostnqn=evil-wins-nqn,nr_io_queues=16 > /dev/nvme-fabrics
traddr=192.168.1.100,transport=rdma,trsvcid=4420,nqn=lightside
-nqn,hostnqn=good-wins-nqn > /dev/nvme-fabrics
[nvmf]# ./remove_nvme_connections.sh 2
echo 1 > /sys/class/nvme/nvme0/delete_controller
echo 1 > /sys/class/nvme/nvme1/delete_controller
[nvmf]# ./setup_nvme_connections.sh
traddr=192.168.1.100,transport=rdma,trsvcid=4420,nqn=darkside
-nqn,hostnqn=evil-wins-nqn,nr_io_queues=16 > /dev/nvme-fabrics
Killed
[nvmf]# dmesg
[ 313.416908] nvme nvme0: creating 16 I/O queues.
[ 313.523908] nvme nvme0: new ctrl: NQN "darkside-nqn", addr
192.168.1.100:4420
[ 313.524857] BUG: unable to handle kernel NULL pointer dereference at
0000000000000010
[ 313.525262] IP: [<ffffffff8136c60e>] strcmp+0xe/0x30
[ 313.525490] PGD 0
[ 313.525726] Oops: 0000 [#1] SMP
[ 313.525900] Modules linked in: nvme_rdma nvme_fabrics nvme_core
ib_ipoib rdma_ucm ib_ucm ib_uverbs ib_umad rdma_cm ib_cm iw_cm mlx4_en
mlx4_ib ib_core mlx4_core
[ 313.527085] CPU: 15 PID: 5856 Comm: setup_nvme_conn Not tainted
4.7.0-rc2+ #2
[ 313.527259] Hardware name: Supermicro X9DRT-F/IBQF/IBFF/X9DRT
-F/IBQF/IBFF, BIOS 1.0a 10/09/2012
[ 313.527551] task: ffff88027646cd40 ti: ffff88025b980000 task.ti:
ffff88025b980000
[ 313.527879] RIP: 0010:[<ffffffff8136c60e>] [<ffffffff8136c60e>]
strcmp+0xe/0x30
[ 313.528232] RSP: 0018:ffff88025b983db0 EFLAGS: 00010206
[ 313.528403] RAX: 0000000000000000 RBX: ffff880471879880 RCX:
fffffffffffffff1
[ 313.528594] RDX: 0000000000000000 RSI: ffff880474afa860 RDI:
0000000000000011
[ 313.528778] RBP: ffff88025b983db0 R08: ffff880474afa860 R09:
ffff880471879058
[ 313.528956] R10: 000000000000002c R11: ffff88047f415000 R12:
ffff880471879800
[ 313.529129] R13: ffff880471879000 R14: ffff880474afa860 R15:
fffffffffffffff8
[ 313.529303] FS: 00007f778f510700(0000) GS:ffff88047fbc0000(0000)
knlGS:0000000000000000
[ 313.529629] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 313.529817] CR2: 0000000000000010 CR3: 0000000274174000 CR4:
00000000000406e0
[ 313.529989] Stack:
[ 313.530154] ffff88025b983e48 ffffffffa0171c74 0000000000000001
0000000000000059
[ 313.530621] ffff880476f32400 ffff88047e8add80 0000010074b33aa0
ffff880471879059
[ 313.531162] ffff88047187904b ffff880471879058 0000000000000000
ffff88047736e000
[ 313.531629] Call Trace:
[ 313.531797] [<ffffffffa0171c74>] nvmf_dev_write+0x674/0x840
[nvme_fabrics]
[ 313.531974] [<ffffffff81180b53>] __vfs_write+0x23/0x120
[ 313.532146] [<ffffffff8119daff>] ? __fd_install+0x1f/0xc0
[ 313.532316] [<ffffffff8119d97a>] ? __alloc_fd+0x3a/0x170
[ 313.532487] [<ffffffff811811f3>] vfs_write+0xb3/0x1b0
[ 313.532658] [<ffffffff8117e321>] ? filp_close+0x51/0x70
[ 313.532845] [<ffffffff811824e1>] SyS_write+0x41/0xa0
[ 313.533016] [<ffffffff8183055b>]
entry_SYSCALL_64_fastpath+0x13/0x8f
[ 313.533188] Code: 80 3a 00 75 f7 48 83 c6 01 0f b6 4e ff 48 83 c2 01
84 c9 88 4a ff 75 ed 5d c3 0f 1f 00 55 48 89 e5 eb 04 84 c0 74 18 48 83
c7 01 <0f> b6 47 ff 48 83 c6 01 3a 46 ff 74 eb 19 c0 83 c8 01 5d c3 31
[ 313.536563] RIP [<ffffffff8136c60e>] strcmp+0xe/0x30
[ 313.536815] RSP <ffff88025b983db0>
[ 313.536981] CR2: 0000000000000010
[ 313.537151] ---[ end trace 3d952e590e7bc2d5 ]---
Reported-and-tested-by: Jay Freyensee <james.p.freyensee@intel.com>
Signed-off-by: Ming Lin <mlin@kernel.org>
Signed-off-by: Jay Freyensee <james.p.freyensee@intel.com>
Signed-off-by: Jens Axboe <axboe@fb.com>
2016-07-01 12:13:32 -07:00
mutex_lock ( & nvmf_hosts_mutex ) ;
list_del ( & host - > list ) ;
mutex_unlock ( & nvmf_hosts_mutex ) ;
2016-06-13 16:45:26 +02:00
kfree ( host ) ;
}
static void nvmf_host_put ( struct nvmf_host * host )
{
if ( host )
kref_put ( & host - > ref , nvmf_host_destroy ) ;
}
/**
* nvmf_get_address ( ) - Get address / port
* @ ctrl : Host NVMe controller instance which we got the address
* @ buf : OUTPUT parameter that will contain the address / port
* @ size : buffer size
*/
int nvmf_get_address ( struct nvme_ctrl * ctrl , char * buf , int size )
{
return snprintf ( buf , size , " traddr=%s,trsvcid=%s \n " ,
ctrl - > opts - > traddr , ctrl - > opts - > trsvcid ) ;
}
EXPORT_SYMBOL_GPL ( nvmf_get_address ) ;
/**
* nvmf_get_subsysnqn ( ) - Get subsystem NQN
* @ ctrl : Host NVMe controller instance which we got the NQN
*/
const char * nvmf_get_subsysnqn ( struct nvme_ctrl * ctrl )
{
return ctrl - > opts - > subsysnqn ;
}
EXPORT_SYMBOL_GPL ( nvmf_get_subsysnqn ) ;
/**
* nvmf_reg_read32 ( ) - NVMe Fabrics " Property Get " API function .
* @ ctrl : Host NVMe controller instance maintaining the admin
* queue used to submit the property read command to
* the allocated NVMe controller resource on the target system .
* @ off : Starting offset value of the targeted property
* register ( see the fabrics section of the NVMe standard ) .
* @ val : OUTPUT parameter that will contain the value of
* the property after a successful read .
*
* Used by the host system to retrieve a 32 - bit capsule property value
* from an NVMe controller on the target system .
*
* ( " Capsule property " is an " PCIe register concept " applied to the
* NVMe fabrics space . )
*
* Return :
* 0 : successful read
* > 0 : NVMe error status code
* < 0 : Linux errno error code
*/
int nvmf_reg_read32 ( struct nvme_ctrl * ctrl , u32 off , u32 * val )
{
struct nvme_command cmd ;
struct nvme_completion cqe ;
int ret ;
memset ( & cmd , 0 , sizeof ( cmd ) ) ;
cmd . prop_get . opcode = nvme_fabrics_command ;
cmd . prop_get . fctype = nvme_fabrics_type_property_get ;
cmd . prop_get . offset = cpu_to_le32 ( off ) ;
ret = __nvme_submit_sync_cmd ( ctrl - > admin_q , & cmd , & cqe , NULL , 0 , 0 ,
NVME_QID_ANY , 0 , 0 ) ;
if ( ret > = 0 )
* val = le64_to_cpu ( cqe . result64 ) ;
if ( unlikely ( ret ! = 0 ) )
dev_err ( ctrl - > device ,
" Property Get error: %d, offset %#x \n " ,
ret > 0 ? ret & ~ NVME_SC_DNR : ret , off ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( nvmf_reg_read32 ) ;
/**
* nvmf_reg_read64 ( ) - NVMe Fabrics " Property Get " API function .
* @ ctrl : Host NVMe controller instance maintaining the admin
* queue used to submit the property read command to
* the allocated controller resource on the target system .
* @ off : Starting offset value of the targeted property
* register ( see the fabrics section of the NVMe standard ) .
* @ val : OUTPUT parameter that will contain the value of
* the property after a successful read .
*
* Used by the host system to retrieve a 64 - bit capsule property value
* from an NVMe controller on the target system .
*
* ( " Capsule property " is an " PCIe register concept " applied to the
* NVMe fabrics space . )
*
* Return :
* 0 : successful read
* > 0 : NVMe error status code
* < 0 : Linux errno error code
*/
int nvmf_reg_read64 ( struct nvme_ctrl * ctrl , u32 off , u64 * val )
{
struct nvme_command cmd ;
struct nvme_completion cqe ;
int ret ;
memset ( & cmd , 0 , sizeof ( cmd ) ) ;
cmd . prop_get . opcode = nvme_fabrics_command ;
cmd . prop_get . fctype = nvme_fabrics_type_property_get ;
cmd . prop_get . attrib = 1 ;
cmd . prop_get . offset = cpu_to_le32 ( off ) ;
ret = __nvme_submit_sync_cmd ( ctrl - > admin_q , & cmd , & cqe , NULL , 0 , 0 ,
NVME_QID_ANY , 0 , 0 ) ;
if ( ret > = 0 )
* val = le64_to_cpu ( cqe . result64 ) ;
if ( unlikely ( ret ! = 0 ) )
dev_err ( ctrl - > device ,
" Property Get error: %d, offset %#x \n " ,
ret > 0 ? ret & ~ NVME_SC_DNR : ret , off ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( nvmf_reg_read64 ) ;
/**
* nvmf_reg_write32 ( ) - NVMe Fabrics " Property Write " API function .
* @ ctrl : Host NVMe controller instance maintaining the admin
* queue used to submit the property read command to
* the allocated NVMe controller resource on the target system .
* @ off : Starting offset value of the targeted property
* register ( see the fabrics section of the NVMe standard ) .
* @ val : Input parameter that contains the value to be
* written to the property .
*
* Used by the NVMe host system to write a 32 - bit capsule property value
* to an NVMe controller on the target system .
*
* ( " Capsule property " is an " PCIe register concept " applied to the
* NVMe fabrics space . )
*
* Return :
* 0 : successful write
* > 0 : NVMe error status code
* < 0 : Linux errno error code
*/
int nvmf_reg_write32 ( struct nvme_ctrl * ctrl , u32 off , u32 val )
{
struct nvme_command cmd ;
int ret ;
memset ( & cmd , 0 , sizeof ( cmd ) ) ;
cmd . prop_set . opcode = nvme_fabrics_command ;
cmd . prop_set . fctype = nvme_fabrics_type_property_set ;
cmd . prop_set . attrib = 0 ;
cmd . prop_set . offset = cpu_to_le32 ( off ) ;
cmd . prop_set . value = cpu_to_le64 ( val ) ;
ret = __nvme_submit_sync_cmd ( ctrl - > admin_q , & cmd , NULL , NULL , 0 , 0 ,
NVME_QID_ANY , 0 , 0 ) ;
if ( unlikely ( ret ) )
dev_err ( ctrl - > device ,
" Property Set error: %d, offset %#x \n " ,
ret > 0 ? ret & ~ NVME_SC_DNR : ret , off ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( nvmf_reg_write32 ) ;
/**
* nvmf_log_connect_error ( ) - Error - parsing - diagnostic print
* out function for connect ( ) errors .
*
* @ ctrl : the specific / dev / nvmeX device that had the error .
*
* @ errval : Error code to be decoded in a more human - friendly
* printout .
*
* @ offset : For use with the NVMe error code NVME_SC_CONNECT_INVALID_PARAM .
*
* @ cmd : This is the SQE portion of a submission capsule .
*
* @ data : This is the " Data " portion of a submission capsule .
*/
static void nvmf_log_connect_error ( struct nvme_ctrl * ctrl ,
int errval , int offset , struct nvme_command * cmd ,
struct nvmf_connect_data * data )
{
int err_sctype = errval & ( ~ NVME_SC_DNR ) ;
switch ( err_sctype ) {
case ( NVME_SC_CONNECT_INVALID_PARAM ) :
if ( offset > > 16 ) {
char * inv_data = " Connect Invalid Data Parameter " ;
switch ( offset & 0xffff ) {
case ( offsetof ( struct nvmf_connect_data , cntlid ) ) :
dev_err ( ctrl - > device ,
" %s, cntlid: %d \n " ,
inv_data , data - > cntlid ) ;
break ;
case ( offsetof ( struct nvmf_connect_data , hostnqn ) ) :
dev_err ( ctrl - > device ,
" %s, hostnqn \" %s \" \n " ,
inv_data , data - > hostnqn ) ;
break ;
case ( offsetof ( struct nvmf_connect_data , subsysnqn ) ) :
dev_err ( ctrl - > device ,
" %s, subsysnqn \" %s \" \n " ,
inv_data , data - > subsysnqn ) ;
break ;
default :
dev_err ( ctrl - > device ,
" %s, starting byte offset: %d \n " ,
inv_data , offset & 0xffff ) ;
break ;
}
} else {
char * inv_sqe = " Connect Invalid SQE Parameter " ;
switch ( offset ) {
case ( offsetof ( struct nvmf_connect_command , qid ) ) :
dev_err ( ctrl - > device ,
" %s, qid %d \n " ,
inv_sqe , cmd - > connect . qid ) ;
break ;
default :
dev_err ( ctrl - > device ,
" %s, starting byte offset: %d \n " ,
inv_sqe , offset ) ;
}
}
break ;
default :
dev_err ( ctrl - > device ,
" Connect command failed, error wo/DNR bit: %d \n " ,
err_sctype ) ;
break ;
} /* switch (err_sctype) */
}
/**
* nvmf_connect_admin_queue ( ) - NVMe Fabrics Admin Queue " Connect "
* API function .
* @ ctrl : Host nvme controller instance used to request
* a new NVMe controller allocation on the target
* system and establish an NVMe Admin connection to
* that controller .
*
* This function enables an NVMe host device to request a new allocation of
* an NVMe controller resource on a target system as well establish a
* fabrics - protocol connection of the NVMe Admin queue between the
* host system device and the allocated NVMe controller on the
* target system via a NVMe Fabrics " Connect " command .
*
* Return :
* 0 : success
* > 0 : NVMe error status code
* < 0 : Linux errno error code
*
*/
int nvmf_connect_admin_queue ( struct nvme_ctrl * ctrl )
{
struct nvme_command cmd ;
struct nvme_completion cqe ;
struct nvmf_connect_data * data ;
int ret ;
memset ( & cmd , 0 , sizeof ( cmd ) ) ;
cmd . connect . opcode = nvme_fabrics_command ;
cmd . connect . fctype = nvme_fabrics_type_connect ;
cmd . connect . qid = 0 ;
cmd . connect . sqsize = cpu_to_le16 ( ctrl - > sqsize ) ;
2016-06-13 16:45:28 +02:00
/*
* Set keep - alive timeout in seconds granularity ( ms * 1000 )
* and add a grace period for controller kato enforcement
*/
cmd . connect . kato = ctrl - > opts - > discovery_nqn ? 0 :
cpu_to_le32 ( ( ctrl - > kato + NVME_KATO_GRACE ) * 1000 ) ;
2016-06-13 16:45:26 +02:00
data = kzalloc ( sizeof ( * data ) , GFP_KERNEL ) ;
if ( ! data )
return - ENOMEM ;
memcpy ( & data - > hostid , & ctrl - > opts - > host - > id , sizeof ( uuid_le ) ) ;
data - > cntlid = cpu_to_le16 ( 0xffff ) ;
strncpy ( data - > subsysnqn , ctrl - > opts - > subsysnqn , NVMF_NQN_SIZE ) ;
strncpy ( data - > hostnqn , ctrl - > opts - > host - > nqn , NVMF_NQN_SIZE ) ;
ret = __nvme_submit_sync_cmd ( ctrl - > admin_q , & cmd , & cqe ,
data , sizeof ( * data ) , 0 , NVME_QID_ANY , 1 ,
BLK_MQ_REQ_RESERVED | BLK_MQ_REQ_NOWAIT ) ;
if ( ret ) {
nvmf_log_connect_error ( ctrl , ret , le32_to_cpu ( cqe . result ) ,
& cmd , data ) ;
goto out_free_data ;
}
ctrl - > cntlid = le16_to_cpu ( cqe . result16 ) ;
out_free_data :
kfree ( data ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( nvmf_connect_admin_queue ) ;
/**
* nvmf_connect_io_queue ( ) - NVMe Fabrics I / O Queue " Connect "
* API function .
* @ ctrl : Host nvme controller instance used to establish an
* NVMe I / O queue connection to the already allocated NVMe
* controller on the target system .
* @ qid : NVMe I / O queue number for the new I / O connection between
* host and target ( note qid = = 0 is illegal as this is
* the Admin queue , per NVMe standard ) .
*
* This function issues a fabrics - protocol connection
* of a NVMe I / O queue ( via NVMe Fabrics " Connect " command )
* between the host system device and the allocated NVMe controller
* on the target system .
*
* Return :
* 0 : success
* > 0 : NVMe error status code
* < 0 : Linux errno error code
*/
int nvmf_connect_io_queue ( struct nvme_ctrl * ctrl , u16 qid )
{
struct nvme_command cmd ;
struct nvmf_connect_data * data ;
struct nvme_completion cqe ;
int ret ;
memset ( & cmd , 0 , sizeof ( cmd ) ) ;
cmd . connect . opcode = nvme_fabrics_command ;
cmd . connect . fctype = nvme_fabrics_type_connect ;
cmd . connect . qid = cpu_to_le16 ( qid ) ;
cmd . connect . sqsize = cpu_to_le16 ( ctrl - > sqsize ) ;
data = kzalloc ( sizeof ( * data ) , GFP_KERNEL ) ;
if ( ! data )
return - ENOMEM ;
memcpy ( & data - > hostid , & ctrl - > opts - > host - > id , sizeof ( uuid_le ) ) ;
data - > cntlid = cpu_to_le16 ( ctrl - > cntlid ) ;
strncpy ( data - > subsysnqn , ctrl - > opts - > subsysnqn , NVMF_NQN_SIZE ) ;
strncpy ( data - > hostnqn , ctrl - > opts - > host - > nqn , NVMF_NQN_SIZE ) ;
ret = __nvme_submit_sync_cmd ( ctrl - > connect_q , & cmd , & cqe ,
data , sizeof ( * data ) , 0 , qid , 1 ,
BLK_MQ_REQ_RESERVED | BLK_MQ_REQ_NOWAIT ) ;
if ( ret ) {
nvmf_log_connect_error ( ctrl , ret , le32_to_cpu ( cqe . result ) ,
& cmd , data ) ;
}
kfree ( data ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( nvmf_connect_io_queue ) ;
/**
* nvmf_register_transport ( ) - NVMe Fabrics Library registration function .
* @ ops : Transport ops instance to be registered to the
* common fabrics library .
*
* API function that registers the type of specific transport fabric
* being implemented to the common NVMe fabrics library . Part of
* the overall init sequence of starting up a fabrics driver .
*/
void nvmf_register_transport ( struct nvmf_transport_ops * ops )
{
mutex_lock ( & nvmf_transports_mutex ) ;
list_add_tail ( & ops - > entry , & nvmf_transports ) ;
mutex_unlock ( & nvmf_transports_mutex ) ;
}
EXPORT_SYMBOL_GPL ( nvmf_register_transport ) ;
/**
* nvmf_unregister_transport ( ) - NVMe Fabrics Library unregistration function .
* @ ops : Transport ops instance to be unregistered from the
* common fabrics library .
*
* Fabrics API function that unregisters the type of specific transport
* fabric being implemented from the common NVMe fabrics library .
* Part of the overall exit sequence of unloading the implemented driver .
*/
void nvmf_unregister_transport ( struct nvmf_transport_ops * ops )
{
mutex_lock ( & nvmf_transports_mutex ) ;
list_del ( & ops - > entry ) ;
mutex_unlock ( & nvmf_transports_mutex ) ;
}
EXPORT_SYMBOL_GPL ( nvmf_unregister_transport ) ;
static struct nvmf_transport_ops * nvmf_lookup_transport (
struct nvmf_ctrl_options * opts )
{
struct nvmf_transport_ops * ops ;
lockdep_assert_held ( & nvmf_transports_mutex ) ;
list_for_each_entry ( ops , & nvmf_transports , entry ) {
if ( strcmp ( ops - > name , opts - > transport ) = = 0 )
return ops ;
}
return NULL ;
}
static const match_table_t opt_tokens = {
{ NVMF_OPT_TRANSPORT , " transport=%s " } ,
{ NVMF_OPT_TRADDR , " traddr=%s " } ,
{ NVMF_OPT_TRSVCID , " trsvcid=%s " } ,
{ NVMF_OPT_NQN , " nqn=%s " } ,
{ NVMF_OPT_QUEUE_SIZE , " queue_size=%d " } ,
{ NVMF_OPT_NR_IO_QUEUES , " nr_io_queues=%d " } ,
{ NVMF_OPT_RECONNECT_DELAY , " reconnect_delay=%d " } ,
2016-06-13 16:45:28 +02:00
{ NVMF_OPT_KATO , " keep_alive_tmo=%d " } ,
2016-06-13 16:45:26 +02:00
{ NVMF_OPT_HOSTNQN , " hostnqn=%s " } ,
{ NVMF_OPT_ERR , NULL }
} ;
static int nvmf_parse_options ( struct nvmf_ctrl_options * opts ,
const char * buf )
{
substring_t args [ MAX_OPT_ARGS ] ;
char * options , * o , * p ;
int token , ret = 0 ;
size_t nqnlen = 0 ;
/* Set defaults */
opts - > queue_size = NVMF_DEF_QUEUE_SIZE ;
opts - > nr_io_queues = num_online_cpus ( ) ;
opts - > reconnect_delay = NVMF_DEF_RECONNECT_DELAY ;
options = o = kstrdup ( buf , GFP_KERNEL ) ;
if ( ! options )
return - ENOMEM ;
while ( ( p = strsep ( & o , " , \n " ) ) ! = NULL ) {
if ( ! * p )
continue ;
token = match_token ( p , opt_tokens , args ) ;
opts - > mask | = token ;
switch ( token ) {
case NVMF_OPT_TRANSPORT :
p = match_strdup ( args ) ;
if ( ! p ) {
ret = - ENOMEM ;
goto out ;
}
opts - > transport = p ;
break ;
case NVMF_OPT_NQN :
p = match_strdup ( args ) ;
if ( ! p ) {
ret = - ENOMEM ;
goto out ;
}
opts - > subsysnqn = p ;
nqnlen = strlen ( opts - > subsysnqn ) ;
if ( nqnlen > = NVMF_NQN_SIZE ) {
pr_err ( " %s needs to be < %d bytes \n " ,
opts - > subsysnqn , NVMF_NQN_SIZE ) ;
ret = - EINVAL ;
goto out ;
}
opts - > discovery_nqn =
! ( strcmp ( opts - > subsysnqn ,
NVME_DISC_SUBSYS_NAME ) ) ;
if ( opts - > discovery_nqn )
opts - > nr_io_queues = 0 ;
break ;
case NVMF_OPT_TRADDR :
p = match_strdup ( args ) ;
if ( ! p ) {
ret = - ENOMEM ;
goto out ;
}
opts - > traddr = p ;
break ;
case NVMF_OPT_TRSVCID :
p = match_strdup ( args ) ;
if ( ! p ) {
ret = - ENOMEM ;
goto out ;
}
opts - > trsvcid = p ;
break ;
case NVMF_OPT_QUEUE_SIZE :
if ( match_int ( args , & token ) ) {
ret = - EINVAL ;
goto out ;
}
if ( token < NVMF_MIN_QUEUE_SIZE | |
token > NVMF_MAX_QUEUE_SIZE ) {
pr_err ( " Invalid queue_size %d \n " , token ) ;
ret = - EINVAL ;
goto out ;
}
opts - > queue_size = token ;
break ;
case NVMF_OPT_NR_IO_QUEUES :
if ( match_int ( args , & token ) ) {
ret = - EINVAL ;
goto out ;
}
if ( token < = 0 ) {
pr_err ( " Invalid number of IOQs %d \n " , token ) ;
ret = - EINVAL ;
goto out ;
}
opts - > nr_io_queues = min_t ( unsigned int ,
num_online_cpus ( ) , token ) ;
break ;
2016-06-13 16:45:28 +02:00
case NVMF_OPT_KATO :
if ( match_int ( args , & token ) ) {
ret = - EINVAL ;
goto out ;
}
if ( opts - > discovery_nqn ) {
pr_err ( " Discovery controllers cannot accept keep_alive_tmo != 0 \n " ) ;
ret = - EINVAL ;
goto out ;
}
if ( token < 0 ) {
pr_err ( " Invalid keep_alive_tmo %d \n " , token ) ;
ret = - EINVAL ;
goto out ;
} else if ( token = = 0 ) {
/* Allowed for debug */
pr_warn ( " keep_alive_tmo 0 won't execute keep alives!!! \n " ) ;
}
opts - > kato = token ;
break ;
2016-06-13 16:45:26 +02:00
case NVMF_OPT_HOSTNQN :
if ( opts - > host ) {
pr_err ( " hostnqn already user-assigned: %s \n " ,
opts - > host - > nqn ) ;
ret = - EADDRINUSE ;
goto out ;
}
p = match_strdup ( args ) ;
if ( ! p ) {
ret = - ENOMEM ;
goto out ;
}
nqnlen = strlen ( p ) ;
if ( nqnlen > = NVMF_NQN_SIZE ) {
pr_err ( " %s needs to be < %d bytes \n " ,
p , NVMF_NQN_SIZE ) ;
ret = - EINVAL ;
goto out ;
}
opts - > host = nvmf_host_add ( p ) ;
if ( ! opts - > host ) {
ret = - ENOMEM ;
goto out ;
}
break ;
case NVMF_OPT_RECONNECT_DELAY :
if ( match_int ( args , & token ) ) {
ret = - EINVAL ;
goto out ;
}
if ( token < = 0 ) {
pr_err ( " Invalid reconnect_delay %d \n " , token ) ;
ret = - EINVAL ;
goto out ;
}
opts - > reconnect_delay = token ;
break ;
default :
pr_warn ( " unknown parameter or missing value '%s' in ctrl creation request \n " ,
p ) ;
ret = - EINVAL ;
goto out ;
}
}
if ( ! opts - > host ) {
kref_get ( & nvmf_default_host - > ref ) ;
opts - > host = nvmf_default_host ;
}
out :
2016-06-13 16:45:28 +02:00
if ( ! opts - > discovery_nqn & & ! opts - > kato )
opts - > kato = NVME_DEFAULT_KATO ;
2016-06-13 16:45:26 +02:00
kfree ( options ) ;
return ret ;
}
static int nvmf_check_required_opts ( struct nvmf_ctrl_options * opts ,
unsigned int required_opts )
{
if ( ( opts - > mask & required_opts ) ! = required_opts ) {
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( opt_tokens ) ; i + + ) {
if ( ( opt_tokens [ i ] . token & required_opts ) & &
! ( opt_tokens [ i ] . token & opts - > mask ) ) {
pr_warn ( " missing parameter '%s' \n " ,
opt_tokens [ i ] . pattern ) ;
}
}
return - EINVAL ;
}
return 0 ;
}
static int nvmf_check_allowed_opts ( struct nvmf_ctrl_options * opts ,
unsigned int allowed_opts )
{
if ( opts - > mask & ~ allowed_opts ) {
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( opt_tokens ) ; i + + ) {
if ( opt_tokens [ i ] . token & ~ allowed_opts ) {
pr_warn ( " invalid parameter '%s' \n " ,
opt_tokens [ i ] . pattern ) ;
}
}
return - EINVAL ;
}
return 0 ;
}
void nvmf_free_options ( struct nvmf_ctrl_options * opts )
{
nvmf_host_put ( opts - > host ) ;
kfree ( opts - > transport ) ;
kfree ( opts - > traddr ) ;
kfree ( opts - > trsvcid ) ;
kfree ( opts - > subsysnqn ) ;
kfree ( opts ) ;
}
EXPORT_SYMBOL_GPL ( nvmf_free_options ) ;
# define NVMF_REQUIRED_OPTS (NVMF_OPT_TRANSPORT | NVMF_OPT_NQN)
# define NVMF_ALLOWED_OPTS (NVMF_OPT_QUEUE_SIZE | NVMF_OPT_NR_IO_QUEUES | \
2016-06-13 16:45:28 +02:00
NVMF_OPT_KATO | NVMF_OPT_HOSTNQN )
2016-06-13 16:45:26 +02:00
static struct nvme_ctrl *
nvmf_create_ctrl ( struct device * dev , const char * buf , size_t count )
{
struct nvmf_ctrl_options * opts ;
struct nvmf_transport_ops * ops ;
struct nvme_ctrl * ctrl ;
int ret ;
opts = kzalloc ( sizeof ( * opts ) , GFP_KERNEL ) ;
if ( ! opts )
return ERR_PTR ( - ENOMEM ) ;
ret = nvmf_parse_options ( opts , buf ) ;
if ( ret )
goto out_free_opts ;
/*
* Check the generic options first as we need a valid transport for
* the lookup below . Then clear the generic flags so that transport
* drivers don ' t have to care about them .
*/
ret = nvmf_check_required_opts ( opts , NVMF_REQUIRED_OPTS ) ;
if ( ret )
goto out_free_opts ;
opts - > mask & = ~ NVMF_REQUIRED_OPTS ;
mutex_lock ( & nvmf_transports_mutex ) ;
ops = nvmf_lookup_transport ( opts ) ;
if ( ! ops ) {
pr_info ( " no handler found for transport %s. \n " ,
opts - > transport ) ;
ret = - EINVAL ;
goto out_unlock ;
}
ret = nvmf_check_required_opts ( opts , ops - > required_opts ) ;
if ( ret )
goto out_unlock ;
ret = nvmf_check_allowed_opts ( opts , NVMF_ALLOWED_OPTS |
ops - > allowed_opts | ops - > required_opts ) ;
if ( ret )
goto out_unlock ;
ctrl = ops - > create_ctrl ( dev , opts ) ;
if ( IS_ERR ( ctrl ) ) {
ret = PTR_ERR ( ctrl ) ;
goto out_unlock ;
}
mutex_unlock ( & nvmf_transports_mutex ) ;
return ctrl ;
out_unlock :
mutex_unlock ( & nvmf_transports_mutex ) ;
out_free_opts :
nvmf_host_put ( opts - > host ) ;
kfree ( opts ) ;
return ERR_PTR ( ret ) ;
}
static struct class * nvmf_class ;
static struct device * nvmf_device ;
static DEFINE_MUTEX ( nvmf_dev_mutex ) ;
static ssize_t nvmf_dev_write ( struct file * file , const char __user * ubuf ,
size_t count , loff_t * pos )
{
struct seq_file * seq_file = file - > private_data ;
struct nvme_ctrl * ctrl ;
const char * buf ;
int ret = 0 ;
if ( count > PAGE_SIZE )
return - ENOMEM ;
buf = memdup_user_nul ( ubuf , count ) ;
if ( IS_ERR ( buf ) )
return PTR_ERR ( buf ) ;
mutex_lock ( & nvmf_dev_mutex ) ;
if ( seq_file - > private ) {
ret = - EINVAL ;
goto out_unlock ;
}
ctrl = nvmf_create_ctrl ( nvmf_device , buf , count ) ;
if ( IS_ERR ( ctrl ) ) {
ret = PTR_ERR ( ctrl ) ;
goto out_unlock ;
}
seq_file - > private = ctrl ;
out_unlock :
mutex_unlock ( & nvmf_dev_mutex ) ;
kfree ( buf ) ;
return ret ? ret : count ;
}
static int nvmf_dev_show ( struct seq_file * seq_file , void * private )
{
struct nvme_ctrl * ctrl ;
int ret = 0 ;
mutex_lock ( & nvmf_dev_mutex ) ;
ctrl = seq_file - > private ;
if ( ! ctrl ) {
ret = - EINVAL ;
goto out_unlock ;
}
seq_printf ( seq_file , " instance=%d,cntlid=%d \n " ,
ctrl - > instance , ctrl - > cntlid ) ;
out_unlock :
mutex_unlock ( & nvmf_dev_mutex ) ;
return ret ;
}
static int nvmf_dev_open ( struct inode * inode , struct file * file )
{
/*
* The miscdevice code initializes file - > private_data , but doesn ' t
* make use of it later .
*/
file - > private_data = NULL ;
return single_open ( file , nvmf_dev_show , NULL ) ;
}
static int nvmf_dev_release ( struct inode * inode , struct file * file )
{
struct seq_file * seq_file = file - > private_data ;
struct nvme_ctrl * ctrl = seq_file - > private ;
if ( ctrl )
nvme_put_ctrl ( ctrl ) ;
return single_release ( inode , file ) ;
}
static const struct file_operations nvmf_dev_fops = {
. owner = THIS_MODULE ,
. write = nvmf_dev_write ,
. read = seq_read ,
. open = nvmf_dev_open ,
. release = nvmf_dev_release ,
} ;
static struct miscdevice nvmf_misc = {
. minor = MISC_DYNAMIC_MINOR ,
. name = " nvme-fabrics " ,
. fops = & nvmf_dev_fops ,
} ;
static int __init nvmf_init ( void )
{
int ret ;
nvmf_default_host = nvmf_host_default ( ) ;
if ( ! nvmf_default_host )
return - ENOMEM ;
nvmf_class = class_create ( THIS_MODULE , " nvme-fabrics " ) ;
if ( IS_ERR ( nvmf_class ) ) {
pr_err ( " couldn't register class nvme-fabrics \n " ) ;
ret = PTR_ERR ( nvmf_class ) ;
goto out_free_host ;
}
nvmf_device =
device_create ( nvmf_class , NULL , MKDEV ( 0 , 0 ) , NULL , " ctl " ) ;
if ( IS_ERR ( nvmf_device ) ) {
pr_err ( " couldn't create nvme-fabris device! \n " ) ;
ret = PTR_ERR ( nvmf_device ) ;
goto out_destroy_class ;
}
ret = misc_register ( & nvmf_misc ) ;
if ( ret ) {
pr_err ( " couldn't register misc device: %d \n " , ret ) ;
goto out_destroy_device ;
}
return 0 ;
out_destroy_device :
device_destroy ( nvmf_class , MKDEV ( 0 , 0 ) ) ;
out_destroy_class :
class_destroy ( nvmf_class ) ;
out_free_host :
nvmf_host_put ( nvmf_default_host ) ;
return ret ;
}
static void __exit nvmf_exit ( void )
{
misc_deregister ( & nvmf_misc ) ;
device_destroy ( nvmf_class , MKDEV ( 0 , 0 ) ) ;
class_destroy ( nvmf_class ) ;
nvmf_host_put ( nvmf_default_host ) ;
BUILD_BUG_ON ( sizeof ( struct nvmf_connect_command ) ! = 64 ) ;
BUILD_BUG_ON ( sizeof ( struct nvmf_property_get_command ) ! = 64 ) ;
BUILD_BUG_ON ( sizeof ( struct nvmf_property_set_command ) ! = 64 ) ;
BUILD_BUG_ON ( sizeof ( struct nvmf_connect_data ) ! = 1024 ) ;
}
MODULE_LICENSE ( " GPL v2 " ) ;
module_init ( nvmf_init ) ;
module_exit ( nvmf_exit ) ;