2016-12-02 11:28:42 +03:00
/*
* Copyright ( c ) 2016 Avago Technologies . All rights reserved .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful .
* ALL EXPRESS OR IMPLIED CONDITIONS , REPRESENTATIONS AND WARRANTIES ,
* INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY , FITNESS FOR A
* PARTICULAR PURPOSE , OR NON - INFRINGEMENT , ARE DISCLAIMED , EXCEPT TO
* THE EXTENT THAT SUCH DISCLAIMERS ARE HELD TO BE LEGALLY INVALID .
* See the GNU General Public License for more details , a copy of which
* can be found in the file COPYING included with this package
*
*/
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
# include <linux/module.h>
# include <linux/parser.h>
# include <uapi/scsi/fc/fc_fs.h>
# include <uapi/scsi/fc/fc_els.h>
2017-04-23 18:30:08 +03:00
# include <linux/delay.h>
2016-12-02 11:28:42 +03:00
# include "nvme.h"
# include "fabrics.h"
# include <linux/nvme-fc-driver.h>
# include <linux/nvme-fc.h>
/* *************************** Data Structures/Defines ****************** */
/*
* We handle AEN commands ourselves and don ' t even let the
* block layer know about them .
*/
# define NVME_FC_NR_AEN_COMMANDS 1
# define NVME_FC_AQ_BLKMQ_DEPTH \
2017-06-18 16:15:59 +03:00
( NVME_AQ_DEPTH - NVME_FC_NR_AEN_COMMANDS )
2016-12-02 11:28:42 +03:00
# define AEN_CMDID_BASE (NVME_FC_AQ_BLKMQ_DEPTH + 1)
enum nvme_fc_queue_flags {
NVME_FC_Q_CONNECTED = ( 1 < < 0 ) ,
} ;
# define NVMEFC_QUEUE_DELAY 3 /* ms units */
struct nvme_fc_queue {
struct nvme_fc_ctrl * ctrl ;
struct device * dev ;
struct blk_mq_hw_ctx * hctx ;
void * lldd_handle ;
int queue_size ;
size_t cmnd_capsule_len ;
u32 qnum ;
u32 rqcnt ;
u32 seqno ;
u64 connection_id ;
atomic_t csn ;
unsigned long flags ;
} __aligned ( sizeof ( u64 ) ) ; /* alignment for other things alloc'd with */
2017-04-11 21:35:09 +03:00
enum nvme_fcop_flags {
FCOP_FLAGS_TERMIO = ( 1 < < 0 ) ,
FCOP_FLAGS_RELEASED = ( 1 < < 1 ) ,
FCOP_FLAGS_COMPLETE = ( 1 < < 2 ) ,
2017-04-23 18:30:07 +03:00
FCOP_FLAGS_AEN = ( 1 < < 3 ) ,
2017-04-11 21:35:09 +03:00
} ;
2016-12-02 11:28:42 +03:00
struct nvmefc_ls_req_op {
struct nvmefc_ls_req ls_req ;
2017-04-11 21:35:08 +03:00
struct nvme_fc_rport * rport ;
2016-12-02 11:28:42 +03:00
struct nvme_fc_queue * queue ;
struct request * rq ;
2017-04-11 21:35:09 +03:00
u32 flags ;
2016-12-02 11:28:42 +03:00
int ls_error ;
struct completion ls_done ;
2017-04-11 21:35:08 +03:00
struct list_head lsreq_list ; /* rport->ls_req_list */
2016-12-02 11:28:42 +03:00
bool req_queued ;
} ;
enum nvme_fcpop_state {
FCPOP_STATE_UNINIT = 0 ,
FCPOP_STATE_IDLE = 1 ,
FCPOP_STATE_ACTIVE = 2 ,
FCPOP_STATE_ABORTED = 3 ,
2017-04-23 18:30:07 +03:00
FCPOP_STATE_COMPLETE = 4 ,
2016-12-02 11:28:42 +03:00
} ;
struct nvme_fc_fcp_op {
struct nvme_request nreq ; /*
* nvme / host / core . c
* requires this to be
* the 1 st element in the
* private structure
* associated with the
* request .
*/
struct nvmefc_fcp_req fcp_req ;
struct nvme_fc_ctrl * ctrl ;
struct nvme_fc_queue * queue ;
struct request * rq ;
atomic_t state ;
2017-04-23 18:30:07 +03:00
u32 flags ;
2016-12-02 11:28:42 +03:00
u32 rqno ;
u32 nents ;
struct nvme_fc_cmd_iu cmd_iu ;
struct nvme_fc_ersp_iu rsp_iu ;
} ;
struct nvme_fc_lport {
struct nvme_fc_local_port localport ;
struct ida endp_cnt ;
struct list_head port_list ; /* nvme_fc_port_list */
struct list_head endp_list ;
struct device * dev ; /* physical device for dma */
struct nvme_fc_port_template * ops ;
struct kref ref ;
} __aligned ( sizeof ( u64 ) ) ; /* alignment for other things alloc'd with */
struct nvme_fc_rport {
struct nvme_fc_remote_port remoteport ;
struct list_head endp_list ; /* for lport->endp_list */
struct list_head ctrl_list ;
2017-04-11 21:35:08 +03:00
struct list_head ls_req_list ;
struct device * dev ; /* physical device for dma */
struct nvme_fc_lport * lport ;
2016-12-02 11:28:42 +03:00
spinlock_t lock ;
struct kref ref ;
} __aligned ( sizeof ( u64 ) ) ; /* alignment for other things alloc'd with */
2017-04-23 18:30:08 +03:00
enum nvme_fcctrl_flags {
FCCTRL_TERMIO = ( 1 < < 0 ) ,
2016-12-02 11:28:42 +03:00
} ;
struct nvme_fc_ctrl {
spinlock_t lock ;
struct nvme_fc_queue * queues ;
struct device * dev ;
struct nvme_fc_lport * lport ;
struct nvme_fc_rport * rport ;
u32 cnum ;
u64 association_id ;
struct list_head ctrl_list ; /* rport->ctrl_list */
struct blk_mq_tag_set admin_tag_set ;
struct blk_mq_tag_set tag_set ;
struct work_struct delete_work ;
2017-04-23 18:30:08 +03:00
struct delayed_work connect_work ;
2016-12-02 11:28:42 +03:00
struct kref ref ;
2017-04-23 18:30:08 +03:00
u32 flags ;
u32 iocnt ;
2017-05-23 01:28:42 +03:00
wait_queue_head_t ioabort_wait ;
2016-12-02 11:28:42 +03:00
struct nvme_fc_fcp_op aen_ops [ NVME_FC_NR_AEN_COMMANDS ] ;
struct nvme_ctrl ctrl ;
} ;
static inline struct nvme_fc_ctrl *
to_fc_ctrl ( struct nvme_ctrl * ctrl )
{
return container_of ( ctrl , struct nvme_fc_ctrl , ctrl ) ;
}
static inline struct nvme_fc_lport *
localport_to_lport ( struct nvme_fc_local_port * portptr )
{
return container_of ( portptr , struct nvme_fc_lport , localport ) ;
}
static inline struct nvme_fc_rport *
remoteport_to_rport ( struct nvme_fc_remote_port * portptr )
{
return container_of ( portptr , struct nvme_fc_rport , remoteport ) ;
}
static inline struct nvmefc_ls_req_op *
ls_req_to_lsop ( struct nvmefc_ls_req * lsreq )
{
return container_of ( lsreq , struct nvmefc_ls_req_op , ls_req ) ;
}
static inline struct nvme_fc_fcp_op *
fcp_req_to_fcp_op ( struct nvmefc_fcp_req * fcpreq )
{
return container_of ( fcpreq , struct nvme_fc_fcp_op , fcp_req ) ;
}
/* *************************** Globals **************************** */
static DEFINE_SPINLOCK ( nvme_fc_lock ) ;
static LIST_HEAD ( nvme_fc_lport_list ) ;
static DEFINE_IDA ( nvme_fc_local_port_cnt ) ;
static DEFINE_IDA ( nvme_fc_ctrl_cnt ) ;
/* *********************** FC-NVME Port Management ************************ */
static int __nvme_fc_del_ctrl ( struct nvme_fc_ctrl * ) ;
static void __nvme_fc_delete_hw_queue ( struct nvme_fc_ctrl * ,
struct nvme_fc_queue * , unsigned int ) ;
2017-07-31 23:20:30 +03:00
static void
nvme_fc_free_lport ( struct kref * ref )
{
struct nvme_fc_lport * lport =
container_of ( ref , struct nvme_fc_lport , ref ) ;
unsigned long flags ;
WARN_ON ( lport - > localport . port_state ! = FC_OBJSTATE_DELETED ) ;
WARN_ON ( ! list_empty ( & lport - > endp_list ) ) ;
/* remove from transport list */
spin_lock_irqsave ( & nvme_fc_lock , flags ) ;
list_del ( & lport - > port_list ) ;
spin_unlock_irqrestore ( & nvme_fc_lock , flags ) ;
/* let the LLDD know we've finished tearing it down */
lport - > ops - > localport_delete ( & lport - > localport ) ;
ida_simple_remove ( & nvme_fc_local_port_cnt , lport - > localport . port_num ) ;
ida_destroy ( & lport - > endp_cnt ) ;
put_device ( lport - > dev ) ;
kfree ( lport ) ;
}
static void
nvme_fc_lport_put ( struct nvme_fc_lport * lport )
{
kref_put ( & lport - > ref , nvme_fc_free_lport ) ;
}
static int
nvme_fc_lport_get ( struct nvme_fc_lport * lport )
{
return kref_get_unless_zero ( & lport - > ref ) ;
}
static struct nvme_fc_lport *
nvme_fc_attach_to_unreg_lport ( struct nvme_fc_port_info * pinfo )
{
struct nvme_fc_lport * lport ;
unsigned long flags ;
spin_lock_irqsave ( & nvme_fc_lock , flags ) ;
list_for_each_entry ( lport , & nvme_fc_lport_list , port_list ) {
if ( lport - > localport . node_name ! = pinfo - > node_name | |
lport - > localport . port_name ! = pinfo - > port_name )
continue ;
if ( lport - > localport . port_state ! = FC_OBJSTATE_DELETED ) {
lport = ERR_PTR ( - EEXIST ) ;
goto out_done ;
}
if ( ! nvme_fc_lport_get ( lport ) ) {
/*
* fails if ref cnt already 0. If so ,
* act as if lport already deleted
*/
lport = NULL ;
goto out_done ;
}
/* resume the lport */
lport - > localport . port_role = pinfo - > port_role ;
lport - > localport . port_id = pinfo - > port_id ;
lport - > localport . port_state = FC_OBJSTATE_ONLINE ;
spin_unlock_irqrestore ( & nvme_fc_lock , flags ) ;
return lport ;
}
lport = NULL ;
out_done :
spin_unlock_irqrestore ( & nvme_fc_lock , flags ) ;
return lport ;
}
2016-12-02 11:28:42 +03:00
/**
* nvme_fc_register_localport - transport entry point called by an
* LLDD to register the existence of a NVME
* host FC port .
* @ pinfo : pointer to information about the port to be registered
* @ template : LLDD entrypoints and operational parameters for the port
* @ dev : physical hardware device node port corresponds to . Will be
* used for DMA mappings
* @ lport_p : pointer to a local port pointer . Upon success , the routine
* will allocate a nvme_fc_local_port structure and place its
* address in the local port pointer . Upon failure , local port
* pointer will be set to 0.
*
* Returns :
* a completion status . Must be 0 upon success ; a negative errno
* ( ex : - ENXIO ) upon failure .
*/
int
nvme_fc_register_localport ( struct nvme_fc_port_info * pinfo ,
struct nvme_fc_port_template * template ,
struct device * dev ,
struct nvme_fc_local_port * * portptr )
{
struct nvme_fc_lport * newrec ;
unsigned long flags ;
int ret , idx ;
if ( ! template - > localport_delete | | ! template - > remoteport_delete | |
! template - > ls_req | | ! template - > fcp_io | |
! template - > ls_abort | | ! template - > fcp_abort | |
! template - > max_hw_queues | | ! template - > max_sgl_segments | |
! template - > max_dif_sgl_segments | | ! template - > dma_boundary ) {
ret = - EINVAL ;
goto out_reghost_failed ;
}
2017-07-31 23:20:30 +03:00
/*
* look to see if there is already a localport that had been
* deregistered and in the process of waiting for all the
* references to fully be removed . If the references haven ' t
* expired , we can simply re - enable the localport . Remoteports
* and controller reconnections should resume naturally .
*/
newrec = nvme_fc_attach_to_unreg_lport ( pinfo ) ;
/* found an lport, but something about its state is bad */
if ( IS_ERR ( newrec ) ) {
ret = PTR_ERR ( newrec ) ;
goto out_reghost_failed ;
/* found existing lport, which was resumed */
} else if ( newrec ) {
* portptr = & newrec - > localport ;
return 0 ;
}
/* nothing found - allocate a new localport struct */
2016-12-02 11:28:42 +03:00
newrec = kmalloc ( ( sizeof ( * newrec ) + template - > local_priv_sz ) ,
GFP_KERNEL ) ;
if ( ! newrec ) {
ret = - ENOMEM ;
goto out_reghost_failed ;
}
idx = ida_simple_get ( & nvme_fc_local_port_cnt , 0 , 0 , GFP_KERNEL ) ;
if ( idx < 0 ) {
ret = - ENOSPC ;
goto out_fail_kfree ;
}
if ( ! get_device ( dev ) & & dev ) {
ret = - ENODEV ;
goto out_ida_put ;
}
INIT_LIST_HEAD ( & newrec - > port_list ) ;
INIT_LIST_HEAD ( & newrec - > endp_list ) ;
kref_init ( & newrec - > ref ) ;
newrec - > ops = template ;
newrec - > dev = dev ;
ida_init ( & newrec - > endp_cnt ) ;
newrec - > localport . private = & newrec [ 1 ] ;
newrec - > localport . node_name = pinfo - > node_name ;
newrec - > localport . port_name = pinfo - > port_name ;
newrec - > localport . port_role = pinfo - > port_role ;
newrec - > localport . port_id = pinfo - > port_id ;
newrec - > localport . port_state = FC_OBJSTATE_ONLINE ;
newrec - > localport . port_num = idx ;
spin_lock_irqsave ( & nvme_fc_lock , flags ) ;
list_add_tail ( & newrec - > port_list , & nvme_fc_lport_list ) ;
spin_unlock_irqrestore ( & nvme_fc_lock , flags ) ;
if ( dev )
dma_set_seg_boundary ( dev , template - > dma_boundary ) ;
* portptr = & newrec - > localport ;
return 0 ;
out_ida_put :
ida_simple_remove ( & nvme_fc_local_port_cnt , idx ) ;
out_fail_kfree :
kfree ( newrec ) ;
out_reghost_failed :
* portptr = NULL ;
return ret ;
}
EXPORT_SYMBOL_GPL ( nvme_fc_register_localport ) ;
/**
* nvme_fc_unregister_localport - transport entry point called by an
* LLDD to deregister / remove a previously
* registered a NVME host FC port .
* @ localport : pointer to the ( registered ) local port that is to be
* deregistered .
*
* Returns :
* a completion status . Must be 0 upon success ; a negative errno
* ( ex : - ENXIO ) upon failure .
*/
int
nvme_fc_unregister_localport ( struct nvme_fc_local_port * portptr )
{
struct nvme_fc_lport * lport = localport_to_lport ( portptr ) ;
unsigned long flags ;
if ( ! portptr )
return - EINVAL ;
spin_lock_irqsave ( & nvme_fc_lock , flags ) ;
if ( portptr - > port_state ! = FC_OBJSTATE_ONLINE ) {
spin_unlock_irqrestore ( & nvme_fc_lock , flags ) ;
return - EINVAL ;
}
portptr - > port_state = FC_OBJSTATE_DELETED ;
spin_unlock_irqrestore ( & nvme_fc_lock , flags ) ;
nvme_fc_lport_put ( lport ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( nvme_fc_unregister_localport ) ;
/**
* nvme_fc_register_remoteport - transport entry point called by an
* LLDD to register the existence of a NVME
* subsystem FC port on its fabric .
* @ localport : pointer to the ( registered ) local port that the remote
* subsystem port is connected to .
* @ pinfo : pointer to information about the port to be registered
* @ rport_p : pointer to a remote port pointer . Upon success , the routine
* will allocate a nvme_fc_remote_port structure and place its
* address in the remote port pointer . Upon failure , remote port
* pointer will be set to 0.
*
* Returns :
* a completion status . Must be 0 upon success ; a negative errno
* ( ex : - ENXIO ) upon failure .
*/
int
nvme_fc_register_remoteport ( struct nvme_fc_local_port * localport ,
struct nvme_fc_port_info * pinfo ,
struct nvme_fc_remote_port * * portptr )
{
struct nvme_fc_lport * lport = localport_to_lport ( localport ) ;
struct nvme_fc_rport * newrec ;
unsigned long flags ;
int ret , idx ;
newrec = kmalloc ( ( sizeof ( * newrec ) + lport - > ops - > remote_priv_sz ) ,
GFP_KERNEL ) ;
if ( ! newrec ) {
ret = - ENOMEM ;
goto out_reghost_failed ;
}
if ( ! nvme_fc_lport_get ( lport ) ) {
ret = - ESHUTDOWN ;
goto out_kfree_rport ;
}
idx = ida_simple_get ( & lport - > endp_cnt , 0 , 0 , GFP_KERNEL ) ;
if ( idx < 0 ) {
ret = - ENOSPC ;
goto out_lport_put ;
}
INIT_LIST_HEAD ( & newrec - > endp_list ) ;
INIT_LIST_HEAD ( & newrec - > ctrl_list ) ;
2017-04-11 21:35:08 +03:00
INIT_LIST_HEAD ( & newrec - > ls_req_list ) ;
2016-12-02 11:28:42 +03:00
kref_init ( & newrec - > ref ) ;
spin_lock_init ( & newrec - > lock ) ;
newrec - > remoteport . localport = & lport - > localport ;
2017-04-11 21:35:08 +03:00
newrec - > dev = lport - > dev ;
newrec - > lport = lport ;
2016-12-02 11:28:42 +03:00
newrec - > remoteport . private = & newrec [ 1 ] ;
newrec - > remoteport . port_role = pinfo - > port_role ;
newrec - > remoteport . node_name = pinfo - > node_name ;
newrec - > remoteport . port_name = pinfo - > port_name ;
newrec - > remoteport . port_id = pinfo - > port_id ;
newrec - > remoteport . port_state = FC_OBJSTATE_ONLINE ;
newrec - > remoteport . port_num = idx ;
spin_lock_irqsave ( & nvme_fc_lock , flags ) ;
list_add_tail ( & newrec - > endp_list , & lport - > endp_list ) ;
spin_unlock_irqrestore ( & nvme_fc_lock , flags ) ;
* portptr = & newrec - > remoteport ;
return 0 ;
out_lport_put :
nvme_fc_lport_put ( lport ) ;
out_kfree_rport :
kfree ( newrec ) ;
out_reghost_failed :
* portptr = NULL ;
return ret ;
}
EXPORT_SYMBOL_GPL ( nvme_fc_register_remoteport ) ;
static void
nvme_fc_free_rport ( struct kref * ref )
{
struct nvme_fc_rport * rport =
container_of ( ref , struct nvme_fc_rport , ref ) ;
struct nvme_fc_lport * lport =
localport_to_lport ( rport - > remoteport . localport ) ;
unsigned long flags ;
WARN_ON ( rport - > remoteport . port_state ! = FC_OBJSTATE_DELETED ) ;
WARN_ON ( ! list_empty ( & rport - > ctrl_list ) ) ;
/* remove from lport list */
spin_lock_irqsave ( & nvme_fc_lock , flags ) ;
list_del ( & rport - > endp_list ) ;
spin_unlock_irqrestore ( & nvme_fc_lock , flags ) ;
/* let the LLDD know we've finished tearing it down */
lport - > ops - > remoteport_delete ( & rport - > remoteport ) ;
ida_simple_remove ( & lport - > endp_cnt , rport - > remoteport . port_num ) ;
kfree ( rport ) ;
nvme_fc_lport_put ( lport ) ;
}
static void
nvme_fc_rport_put ( struct nvme_fc_rport * rport )
{
kref_put ( & rport - > ref , nvme_fc_free_rport ) ;
}
static int
nvme_fc_rport_get ( struct nvme_fc_rport * rport )
{
return kref_get_unless_zero ( & rport - > ref ) ;
}
2017-04-11 21:35:09 +03:00
static int
nvme_fc_abort_lsops ( struct nvme_fc_rport * rport )
{
struct nvmefc_ls_req_op * lsop ;
unsigned long flags ;
restart :
spin_lock_irqsave ( & rport - > lock , flags ) ;
list_for_each_entry ( lsop , & rport - > ls_req_list , lsreq_list ) {
if ( ! ( lsop - > flags & FCOP_FLAGS_TERMIO ) ) {
lsop - > flags | = FCOP_FLAGS_TERMIO ;
spin_unlock_irqrestore ( & rport - > lock , flags ) ;
rport - > lport - > ops - > ls_abort ( & rport - > lport - > localport ,
& rport - > remoteport ,
& lsop - > ls_req ) ;
goto restart ;
}
}
spin_unlock_irqrestore ( & rport - > lock , flags ) ;
return 0 ;
}
2016-12-02 11:28:42 +03:00
/**
* nvme_fc_unregister_remoteport - transport entry point called by an
* LLDD to deregister / remove a previously
* registered a NVME subsystem FC port .
* @ remoteport : pointer to the ( registered ) remote port that is to be
* deregistered .
*
* Returns :
* a completion status . Must be 0 upon success ; a negative errno
* ( ex : - ENXIO ) upon failure .
*/
int
nvme_fc_unregister_remoteport ( struct nvme_fc_remote_port * portptr )
{
struct nvme_fc_rport * rport = remoteport_to_rport ( portptr ) ;
struct nvme_fc_ctrl * ctrl ;
unsigned long flags ;
if ( ! portptr )
return - EINVAL ;
spin_lock_irqsave ( & rport - > lock , flags ) ;
if ( portptr - > port_state ! = FC_OBJSTATE_ONLINE ) {
spin_unlock_irqrestore ( & rport - > lock , flags ) ;
return - EINVAL ;
}
portptr - > port_state = FC_OBJSTATE_DELETED ;
/* tear down all associations to the remote port */
list_for_each_entry ( ctrl , & rport - > ctrl_list , ctrl_list )
__nvme_fc_del_ctrl ( ctrl ) ;
spin_unlock_irqrestore ( & rport - > lock , flags ) ;
2017-04-11 21:35:09 +03:00
nvme_fc_abort_lsops ( rport ) ;
2016-12-02 11:28:42 +03:00
nvme_fc_rport_put ( rport ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( nvme_fc_unregister_remoteport ) ;
/* *********************** FC-NVME DMA Handling **************************** */
/*
* The fcloop device passes in a NULL device pointer . Real LLD ' s will
* pass in a valid device pointer . If NULL is passed to the dma mapping
* routines , depending on the platform , it may or may not succeed , and
* may crash .
*
* As such :
* Wrapper all the dma routines and check the dev pointer .
*
* If simple mappings ( return just a dma address , we ' ll noop them ,
* returning a dma address of 0.
*
* On more complex mappings ( dma_map_sg ) , a pseudo routine fills
* in the scatter list , setting all dma addresses to 0.
*/
static inline dma_addr_t
fc_dma_map_single ( struct device * dev , void * ptr , size_t size ,
enum dma_data_direction dir )
{
return dev ? dma_map_single ( dev , ptr , size , dir ) : ( dma_addr_t ) 0L ;
}
static inline int
fc_dma_mapping_error ( struct device * dev , dma_addr_t dma_addr )
{
return dev ? dma_mapping_error ( dev , dma_addr ) : 0 ;
}
static inline void
fc_dma_unmap_single ( struct device * dev , dma_addr_t addr , size_t size ,
enum dma_data_direction dir )
{
if ( dev )
dma_unmap_single ( dev , addr , size , dir ) ;
}
static inline void
fc_dma_sync_single_for_cpu ( struct device * dev , dma_addr_t addr , size_t size ,
enum dma_data_direction dir )
{
if ( dev )
dma_sync_single_for_cpu ( dev , addr , size , dir ) ;
}
static inline void
fc_dma_sync_single_for_device ( struct device * dev , dma_addr_t addr , size_t size ,
enum dma_data_direction dir )
{
if ( dev )
dma_sync_single_for_device ( dev , addr , size , dir ) ;
}
/* pseudo dma_map_sg call */
static int
fc_map_sg ( struct scatterlist * sg , int nents )
{
struct scatterlist * s ;
int i ;
WARN_ON ( nents = = 0 | | sg [ 0 ] . length = = 0 ) ;
for_each_sg ( sg , s , nents , i ) {
s - > dma_address = 0L ;
# ifdef CONFIG_NEED_SG_DMA_LENGTH
s - > dma_length = s - > length ;
# endif
}
return nents ;
}
static inline int
fc_dma_map_sg ( struct device * dev , struct scatterlist * sg , int nents ,
enum dma_data_direction dir )
{
return dev ? dma_map_sg ( dev , sg , nents , dir ) : fc_map_sg ( sg , nents ) ;
}
static inline void
fc_dma_unmap_sg ( struct device * dev , struct scatterlist * sg , int nents ,
enum dma_data_direction dir )
{
if ( dev )
dma_unmap_sg ( dev , sg , nents , dir ) ;
}
/* *********************** FC-NVME LS Handling **************************** */
static void nvme_fc_ctrl_put ( struct nvme_fc_ctrl * ) ;
static int nvme_fc_ctrl_get ( struct nvme_fc_ctrl * ) ;
static void
2017-04-11 21:35:08 +03:00
__nvme_fc_finish_ls_req ( struct nvmefc_ls_req_op * lsop )
2016-12-02 11:28:42 +03:00
{
2017-04-11 21:35:08 +03:00
struct nvme_fc_rport * rport = lsop - > rport ;
2016-12-02 11:28:42 +03:00
struct nvmefc_ls_req * lsreq = & lsop - > ls_req ;
unsigned long flags ;
2017-04-11 21:35:08 +03:00
spin_lock_irqsave ( & rport - > lock , flags ) ;
2016-12-02 11:28:42 +03:00
if ( ! lsop - > req_queued ) {
2017-04-11 21:35:08 +03:00
spin_unlock_irqrestore ( & rport - > lock , flags ) ;
2016-12-02 11:28:42 +03:00
return ;
}
list_del ( & lsop - > lsreq_list ) ;
lsop - > req_queued = false ;
2017-04-11 21:35:08 +03:00
spin_unlock_irqrestore ( & rport - > lock , flags ) ;
2016-12-02 11:28:42 +03:00
2017-04-11 21:35:08 +03:00
fc_dma_unmap_single ( rport - > dev , lsreq - > rqstdma ,
2016-12-02 11:28:42 +03:00
( lsreq - > rqstlen + lsreq - > rsplen ) ,
DMA_BIDIRECTIONAL ) ;
2017-04-11 21:35:08 +03:00
nvme_fc_rport_put ( rport ) ;
2016-12-02 11:28:42 +03:00
}
static int
2017-04-11 21:35:08 +03:00
__nvme_fc_send_ls_req ( struct nvme_fc_rport * rport ,
2016-12-02 11:28:42 +03:00
struct nvmefc_ls_req_op * lsop ,
void ( * done ) ( struct nvmefc_ls_req * req , int status ) )
{
struct nvmefc_ls_req * lsreq = & lsop - > ls_req ;
unsigned long flags ;
2017-04-11 21:35:08 +03:00
int ret = 0 ;
2016-12-02 11:28:42 +03:00
2017-04-11 21:35:08 +03:00
if ( rport - > remoteport . port_state ! = FC_OBJSTATE_ONLINE )
return - ECONNREFUSED ;
if ( ! nvme_fc_rport_get ( rport ) )
2016-12-02 11:28:42 +03:00
return - ESHUTDOWN ;
lsreq - > done = done ;
2017-04-11 21:35:08 +03:00
lsop - > rport = rport ;
2016-12-02 11:28:42 +03:00
lsop - > req_queued = false ;
INIT_LIST_HEAD ( & lsop - > lsreq_list ) ;
init_completion ( & lsop - > ls_done ) ;
2017-04-11 21:35:08 +03:00
lsreq - > rqstdma = fc_dma_map_single ( rport - > dev , lsreq - > rqstaddr ,
2016-12-02 11:28:42 +03:00
lsreq - > rqstlen + lsreq - > rsplen ,
DMA_BIDIRECTIONAL ) ;
2017-04-11 21:35:08 +03:00
if ( fc_dma_mapping_error ( rport - > dev , lsreq - > rqstdma ) ) {
ret = - EFAULT ;
goto out_putrport ;
2016-12-02 11:28:42 +03:00
}
lsreq - > rspdma = lsreq - > rqstdma + lsreq - > rqstlen ;
2017-04-11 21:35:08 +03:00
spin_lock_irqsave ( & rport - > lock , flags ) ;
2016-12-02 11:28:42 +03:00
2017-04-11 21:35:08 +03:00
list_add_tail ( & lsop - > lsreq_list , & rport - > ls_req_list ) ;
2016-12-02 11:28:42 +03:00
lsop - > req_queued = true ;
2017-04-11 21:35:08 +03:00
spin_unlock_irqrestore ( & rport - > lock , flags ) ;
2016-12-02 11:28:42 +03:00
2017-04-11 21:35:08 +03:00
ret = rport - > lport - > ops - > ls_req ( & rport - > lport - > localport ,
& rport - > remoteport , lsreq ) ;
2016-12-02 11:28:42 +03:00
if ( ret )
2017-04-11 21:35:08 +03:00
goto out_unlink ;
return 0 ;
out_unlink :
lsop - > ls_error = ret ;
spin_lock_irqsave ( & rport - > lock , flags ) ;
lsop - > req_queued = false ;
list_del ( & lsop - > lsreq_list ) ;
spin_unlock_irqrestore ( & rport - > lock , flags ) ;
fc_dma_unmap_single ( rport - > dev , lsreq - > rqstdma ,
( lsreq - > rqstlen + lsreq - > rsplen ) ,
DMA_BIDIRECTIONAL ) ;
out_putrport :
nvme_fc_rport_put ( rport ) ;
2016-12-02 11:28:42 +03:00
return ret ;
}
static void
nvme_fc_send_ls_req_done ( struct nvmefc_ls_req * lsreq , int status )
{
struct nvmefc_ls_req_op * lsop = ls_req_to_lsop ( lsreq ) ;
lsop - > ls_error = status ;
complete ( & lsop - > ls_done ) ;
}
static int
2017-04-11 21:35:08 +03:00
nvme_fc_send_ls_req ( struct nvme_fc_rport * rport , struct nvmefc_ls_req_op * lsop )
2016-12-02 11:28:42 +03:00
{
struct nvmefc_ls_req * lsreq = & lsop - > ls_req ;
struct fcnvme_ls_rjt * rjt = lsreq - > rspaddr ;
int ret ;
2017-04-11 21:35:08 +03:00
ret = __nvme_fc_send_ls_req ( rport , lsop , nvme_fc_send_ls_req_done ) ;
2016-12-02 11:28:42 +03:00
2017-04-11 21:35:08 +03:00
if ( ! ret ) {
2016-12-02 11:28:42 +03:00
/*
* No timeout / not interruptible as we need the struct
* to exist until the lldd calls us back . Thus mandate
* wait until driver calls back . lldd responsible for
* the timeout action
*/
wait_for_completion ( & lsop - > ls_done ) ;
2017-04-11 21:35:08 +03:00
__nvme_fc_finish_ls_req ( lsop ) ;
2016-12-02 11:28:42 +03:00
2017-04-11 21:35:08 +03:00
ret = lsop - > ls_error ;
2016-12-02 11:28:42 +03:00
}
2017-04-11 21:35:08 +03:00
if ( ret )
return ret ;
2016-12-02 11:28:42 +03:00
/* ACC or RJT payload ? */
if ( rjt - > w0 . ls_cmd = = FCNVME_LS_RJT )
return - ENXIO ;
return 0 ;
}
2017-04-11 21:35:08 +03:00
static int
nvme_fc_send_ls_req_async ( struct nvme_fc_rport * rport ,
2016-12-02 11:28:42 +03:00
struct nvmefc_ls_req_op * lsop ,
void ( * done ) ( struct nvmefc_ls_req * req , int status ) )
{
/* don't wait for completion */
2017-04-11 21:35:08 +03:00
return __nvme_fc_send_ls_req ( rport , lsop , done ) ;
2016-12-02 11:28:42 +03:00
}
/* Validation Error indexes into the string table below */
enum {
VERR_NO_ERROR = 0 ,
VERR_LSACC = 1 ,
VERR_LSDESC_RQST = 2 ,
VERR_LSDESC_RQST_LEN = 3 ,
VERR_ASSOC_ID = 4 ,
VERR_ASSOC_ID_LEN = 5 ,
VERR_CONN_ID = 6 ,
VERR_CONN_ID_LEN = 7 ,
VERR_CR_ASSOC = 8 ,
VERR_CR_ASSOC_ACC_LEN = 9 ,
VERR_CR_CONN = 10 ,
VERR_CR_CONN_ACC_LEN = 11 ,
VERR_DISCONN = 12 ,
VERR_DISCONN_ACC_LEN = 13 ,
} ;
static char * validation_errors [ ] = {
" OK " ,
" Not LS_ACC " ,
" Not LSDESC_RQST " ,
" Bad LSDESC_RQST Length " ,
" Not Association ID " ,
" Bad Association ID Length " ,
" Not Connection ID " ,
" Bad Connection ID Length " ,
" Not CR_ASSOC Rqst " ,
" Bad CR_ASSOC ACC Length " ,
" Not CR_CONN Rqst " ,
" Bad CR_CONN ACC Length " ,
" Not Disconnect Rqst " ,
" Bad Disconnect ACC Length " ,
} ;
static int
nvme_fc_connect_admin_queue ( struct nvme_fc_ctrl * ctrl ,
struct nvme_fc_queue * queue , u16 qsize , u16 ersp_ratio )
{
struct nvmefc_ls_req_op * lsop ;
struct nvmefc_ls_req * lsreq ;
struct fcnvme_ls_cr_assoc_rqst * assoc_rqst ;
struct fcnvme_ls_cr_assoc_acc * assoc_acc ;
int ret , fcret = 0 ;
lsop = kzalloc ( ( sizeof ( * lsop ) +
ctrl - > lport - > ops - > lsrqst_priv_sz +
sizeof ( * assoc_rqst ) + sizeof ( * assoc_acc ) ) , GFP_KERNEL ) ;
if ( ! lsop ) {
ret = - ENOMEM ;
goto out_no_memory ;
}
lsreq = & lsop - > ls_req ;
lsreq - > private = ( void * ) & lsop [ 1 ] ;
assoc_rqst = ( struct fcnvme_ls_cr_assoc_rqst * )
( lsreq - > private + ctrl - > lport - > ops - > lsrqst_priv_sz ) ;
assoc_acc = ( struct fcnvme_ls_cr_assoc_acc * ) & assoc_rqst [ 1 ] ;
assoc_rqst - > w0 . ls_cmd = FCNVME_LS_CREATE_ASSOCIATION ;
assoc_rqst - > desc_list_len =
cpu_to_be32 ( sizeof ( struct fcnvme_lsdesc_cr_assoc_cmd ) ) ;
assoc_rqst - > assoc_cmd . desc_tag =
cpu_to_be32 ( FCNVME_LSDESC_CREATE_ASSOC_CMD ) ;
assoc_rqst - > assoc_cmd . desc_len =
fcnvme_lsdesc_len (
sizeof ( struct fcnvme_lsdesc_cr_assoc_cmd ) ) ;
assoc_rqst - > assoc_cmd . ersp_ratio = cpu_to_be16 ( ersp_ratio ) ;
assoc_rqst - > assoc_cmd . sqsize = cpu_to_be16 ( qsize ) ;
/* Linux supports only Dynamic controllers */
assoc_rqst - > assoc_cmd . cntlid = cpu_to_be16 ( 0xffff ) ;
2017-05-17 10:54:27 +03:00
uuid_copy ( & assoc_rqst - > assoc_cmd . hostid , & ctrl - > ctrl . opts - > host - > id ) ;
2016-12-02 11:28:42 +03:00
strncpy ( assoc_rqst - > assoc_cmd . hostnqn , ctrl - > ctrl . opts - > host - > nqn ,
min ( FCNVME_ASSOC_HOSTNQN_LEN , NVMF_NQN_SIZE ) ) ;
strncpy ( assoc_rqst - > assoc_cmd . subnqn , ctrl - > ctrl . opts - > subsysnqn ,
min ( FCNVME_ASSOC_SUBNQN_LEN , NVMF_NQN_SIZE ) ) ;
lsop - > queue = queue ;
lsreq - > rqstaddr = assoc_rqst ;
lsreq - > rqstlen = sizeof ( * assoc_rqst ) ;
lsreq - > rspaddr = assoc_acc ;
lsreq - > rsplen = sizeof ( * assoc_acc ) ;
lsreq - > timeout = NVME_FC_CONNECT_TIMEOUT_SEC ;
2017-04-11 21:35:08 +03:00
ret = nvme_fc_send_ls_req ( ctrl - > rport , lsop ) ;
2016-12-02 11:28:42 +03:00
if ( ret )
goto out_free_buffer ;
/* process connect LS completion */
/* validate the ACC response */
if ( assoc_acc - > hdr . w0 . ls_cmd ! = FCNVME_LS_ACC )
fcret = VERR_LSACC ;
2017-03-24 06:41:25 +03:00
else if ( assoc_acc - > hdr . desc_list_len ! =
2016-12-02 11:28:42 +03:00
fcnvme_lsdesc_len (
sizeof ( struct fcnvme_ls_cr_assoc_acc ) ) )
fcret = VERR_CR_ASSOC_ACC_LEN ;
2017-03-24 06:41:25 +03:00
else if ( assoc_acc - > hdr . rqst . desc_tag ! =
cpu_to_be32 ( FCNVME_LSDESC_RQST ) )
2016-12-02 11:28:42 +03:00
fcret = VERR_LSDESC_RQST ;
else if ( assoc_acc - > hdr . rqst . desc_len ! =
fcnvme_lsdesc_len ( sizeof ( struct fcnvme_lsdesc_rqst ) ) )
fcret = VERR_LSDESC_RQST_LEN ;
else if ( assoc_acc - > hdr . rqst . w0 . ls_cmd ! = FCNVME_LS_CREATE_ASSOCIATION )
fcret = VERR_CR_ASSOC ;
else if ( assoc_acc - > associd . desc_tag ! =
cpu_to_be32 ( FCNVME_LSDESC_ASSOC_ID ) )
fcret = VERR_ASSOC_ID ;
else if ( assoc_acc - > associd . desc_len ! =
fcnvme_lsdesc_len (
sizeof ( struct fcnvme_lsdesc_assoc_id ) ) )
fcret = VERR_ASSOC_ID_LEN ;
else if ( assoc_acc - > connectid . desc_tag ! =
cpu_to_be32 ( FCNVME_LSDESC_CONN_ID ) )
fcret = VERR_CONN_ID ;
else if ( assoc_acc - > connectid . desc_len ! =
fcnvme_lsdesc_len ( sizeof ( struct fcnvme_lsdesc_conn_id ) ) )
fcret = VERR_CONN_ID_LEN ;
if ( fcret ) {
ret = - EBADF ;
dev_err ( ctrl - > dev ,
" q %d connect failed: %s \n " ,
queue - > qnum , validation_errors [ fcret ] ) ;
} else {
ctrl - > association_id =
be64_to_cpu ( assoc_acc - > associd . association_id ) ;
queue - > connection_id =
be64_to_cpu ( assoc_acc - > connectid . connection_id ) ;
set_bit ( NVME_FC_Q_CONNECTED , & queue - > flags ) ;
}
out_free_buffer :
kfree ( lsop ) ;
out_no_memory :
if ( ret )
dev_err ( ctrl - > dev ,
" queue %d connect admin queue failed (%d). \n " ,
queue - > qnum , ret ) ;
return ret ;
}
static int
nvme_fc_connect_queue ( struct nvme_fc_ctrl * ctrl , struct nvme_fc_queue * queue ,
u16 qsize , u16 ersp_ratio )
{
struct nvmefc_ls_req_op * lsop ;
struct nvmefc_ls_req * lsreq ;
struct fcnvme_ls_cr_conn_rqst * conn_rqst ;
struct fcnvme_ls_cr_conn_acc * conn_acc ;
int ret , fcret = 0 ;
lsop = kzalloc ( ( sizeof ( * lsop ) +
ctrl - > lport - > ops - > lsrqst_priv_sz +
sizeof ( * conn_rqst ) + sizeof ( * conn_acc ) ) , GFP_KERNEL ) ;
if ( ! lsop ) {
ret = - ENOMEM ;
goto out_no_memory ;
}
lsreq = & lsop - > ls_req ;
lsreq - > private = ( void * ) & lsop [ 1 ] ;
conn_rqst = ( struct fcnvme_ls_cr_conn_rqst * )
( lsreq - > private + ctrl - > lport - > ops - > lsrqst_priv_sz ) ;
conn_acc = ( struct fcnvme_ls_cr_conn_acc * ) & conn_rqst [ 1 ] ;
conn_rqst - > w0 . ls_cmd = FCNVME_LS_CREATE_CONNECTION ;
conn_rqst - > desc_list_len = cpu_to_be32 (
sizeof ( struct fcnvme_lsdesc_assoc_id ) +
sizeof ( struct fcnvme_lsdesc_cr_conn_cmd ) ) ;
conn_rqst - > associd . desc_tag = cpu_to_be32 ( FCNVME_LSDESC_ASSOC_ID ) ;
conn_rqst - > associd . desc_len =
fcnvme_lsdesc_len (
sizeof ( struct fcnvme_lsdesc_assoc_id ) ) ;
conn_rqst - > associd . association_id = cpu_to_be64 ( ctrl - > association_id ) ;
conn_rqst - > connect_cmd . desc_tag =
cpu_to_be32 ( FCNVME_LSDESC_CREATE_CONN_CMD ) ;
conn_rqst - > connect_cmd . desc_len =
fcnvme_lsdesc_len (
sizeof ( struct fcnvme_lsdesc_cr_conn_cmd ) ) ;
conn_rqst - > connect_cmd . ersp_ratio = cpu_to_be16 ( ersp_ratio ) ;
conn_rqst - > connect_cmd . qid = cpu_to_be16 ( queue - > qnum ) ;
conn_rqst - > connect_cmd . sqsize = cpu_to_be16 ( qsize ) ;
lsop - > queue = queue ;
lsreq - > rqstaddr = conn_rqst ;
lsreq - > rqstlen = sizeof ( * conn_rqst ) ;
lsreq - > rspaddr = conn_acc ;
lsreq - > rsplen = sizeof ( * conn_acc ) ;
lsreq - > timeout = NVME_FC_CONNECT_TIMEOUT_SEC ;
2017-04-11 21:35:08 +03:00
ret = nvme_fc_send_ls_req ( ctrl - > rport , lsop ) ;
2016-12-02 11:28:42 +03:00
if ( ret )
goto out_free_buffer ;
/* process connect LS completion */
/* validate the ACC response */
if ( conn_acc - > hdr . w0 . ls_cmd ! = FCNVME_LS_ACC )
fcret = VERR_LSACC ;
2017-03-24 06:41:25 +03:00
else if ( conn_acc - > hdr . desc_list_len ! =
2016-12-02 11:28:42 +03:00
fcnvme_lsdesc_len ( sizeof ( struct fcnvme_ls_cr_conn_acc ) ) )
fcret = VERR_CR_CONN_ACC_LEN ;
2017-03-24 06:41:25 +03:00
else if ( conn_acc - > hdr . rqst . desc_tag ! = cpu_to_be32 ( FCNVME_LSDESC_RQST ) )
2016-12-02 11:28:42 +03:00
fcret = VERR_LSDESC_RQST ;
else if ( conn_acc - > hdr . rqst . desc_len ! =
fcnvme_lsdesc_len ( sizeof ( struct fcnvme_lsdesc_rqst ) ) )
fcret = VERR_LSDESC_RQST_LEN ;
else if ( conn_acc - > hdr . rqst . w0 . ls_cmd ! = FCNVME_LS_CREATE_CONNECTION )
fcret = VERR_CR_CONN ;
else if ( conn_acc - > connectid . desc_tag ! =
cpu_to_be32 ( FCNVME_LSDESC_CONN_ID ) )
fcret = VERR_CONN_ID ;
else if ( conn_acc - > connectid . desc_len ! =
fcnvme_lsdesc_len ( sizeof ( struct fcnvme_lsdesc_conn_id ) ) )
fcret = VERR_CONN_ID_LEN ;
if ( fcret ) {
ret = - EBADF ;
dev_err ( ctrl - > dev ,
" q %d connect failed: %s \n " ,
queue - > qnum , validation_errors [ fcret ] ) ;
} else {
queue - > connection_id =
be64_to_cpu ( conn_acc - > connectid . connection_id ) ;
set_bit ( NVME_FC_Q_CONNECTED , & queue - > flags ) ;
}
out_free_buffer :
kfree ( lsop ) ;
out_no_memory :
if ( ret )
dev_err ( ctrl - > dev ,
" queue %d connect command failed (%d). \n " ,
queue - > qnum , ret ) ;
return ret ;
}
static void
nvme_fc_disconnect_assoc_done ( struct nvmefc_ls_req * lsreq , int status )
{
struct nvmefc_ls_req_op * lsop = ls_req_to_lsop ( lsreq ) ;
2017-04-11 21:35:08 +03:00
__nvme_fc_finish_ls_req ( lsop ) ;
2016-12-02 11:28:42 +03:00
/* fc-nvme iniator doesn't care about success or failure of cmd */
kfree ( lsop ) ;
}
/*
* This routine sends a FC - NVME LS to disconnect ( aka terminate )
* the FC - NVME Association . Terminating the association also
* terminates the FC - NVME connections ( per queue , both admin and io
* queues ) that are part of the association . E . g . things are torn
* down , and the related FC - NVME Association ID and Connection IDs
* become invalid .
*
* The behavior of the fc - nvme initiator is such that it ' s
* understanding of the association and connections will implicitly
* be torn down . The action is implicit as it may be due to a loss of
* connectivity with the fc - nvme target , so you may never get a
* response even if you tried . As such , the action of this routine
* is to asynchronously send the LS , ignore any results of the LS , and
* continue on with terminating the association . If the fc - nvme target
* is present and receives the LS , it too can tear down .
*/
static void
nvme_fc_xmt_disconnect_assoc ( struct nvme_fc_ctrl * ctrl )
{
struct fcnvme_ls_disconnect_rqst * discon_rqst ;
struct fcnvme_ls_disconnect_acc * discon_acc ;
struct nvmefc_ls_req_op * lsop ;
struct nvmefc_ls_req * lsreq ;
2017-04-11 21:35:08 +03:00
int ret ;
2016-12-02 11:28:42 +03:00
lsop = kzalloc ( ( sizeof ( * lsop ) +
ctrl - > lport - > ops - > lsrqst_priv_sz +
sizeof ( * discon_rqst ) + sizeof ( * discon_acc ) ) ,
GFP_KERNEL ) ;
if ( ! lsop )
/* couldn't sent it... too bad */
return ;
lsreq = & lsop - > ls_req ;
lsreq - > private = ( void * ) & lsop [ 1 ] ;
discon_rqst = ( struct fcnvme_ls_disconnect_rqst * )
( lsreq - > private + ctrl - > lport - > ops - > lsrqst_priv_sz ) ;
discon_acc = ( struct fcnvme_ls_disconnect_acc * ) & discon_rqst [ 1 ] ;
discon_rqst - > w0 . ls_cmd = FCNVME_LS_DISCONNECT ;
discon_rqst - > desc_list_len = cpu_to_be32 (
sizeof ( struct fcnvme_lsdesc_assoc_id ) +
sizeof ( struct fcnvme_lsdesc_disconn_cmd ) ) ;
discon_rqst - > associd . desc_tag = cpu_to_be32 ( FCNVME_LSDESC_ASSOC_ID ) ;
discon_rqst - > associd . desc_len =
fcnvme_lsdesc_len (
sizeof ( struct fcnvme_lsdesc_assoc_id ) ) ;
discon_rqst - > associd . association_id = cpu_to_be64 ( ctrl - > association_id ) ;
discon_rqst - > discon_cmd . desc_tag = cpu_to_be32 (
FCNVME_LSDESC_DISCONN_CMD ) ;
discon_rqst - > discon_cmd . desc_len =
fcnvme_lsdesc_len (
sizeof ( struct fcnvme_lsdesc_disconn_cmd ) ) ;
discon_rqst - > discon_cmd . scope = FCNVME_DISCONN_ASSOCIATION ;
discon_rqst - > discon_cmd . id = cpu_to_be64 ( ctrl - > association_id ) ;
lsreq - > rqstaddr = discon_rqst ;
lsreq - > rqstlen = sizeof ( * discon_rqst ) ;
lsreq - > rspaddr = discon_acc ;
lsreq - > rsplen = sizeof ( * discon_acc ) ;
lsreq - > timeout = NVME_FC_CONNECT_TIMEOUT_SEC ;
2017-04-11 21:35:08 +03:00
ret = nvme_fc_send_ls_req_async ( ctrl - > rport , lsop ,
nvme_fc_disconnect_assoc_done ) ;
if ( ret )
kfree ( lsop ) ;
2016-12-02 11:28:42 +03:00
/* only meaningful part to terminating the association */
ctrl - > association_id = 0 ;
}
/* *********************** NVME Ctrl Routines **************************** */
2017-04-23 18:30:07 +03:00
static void __nvme_fc_final_op_cleanup ( struct request * rq ) ;
2017-06-02 08:54:21 +03:00
static void nvme_fc_error_recovery ( struct nvme_fc_ctrl * ctrl , char * errmsg ) ;
2016-12-02 11:28:42 +03:00
static int
nvme_fc_reinit_request ( void * data , struct request * rq )
{
struct nvme_fc_fcp_op * op = blk_mq_rq_to_pdu ( rq ) ;
struct nvme_fc_cmd_iu * cmdiu = & op - > cmd_iu ;
memset ( cmdiu , 0 , sizeof ( * cmdiu ) ) ;
cmdiu - > scsi_id = NVME_CMD_SCSI_ID ;
cmdiu - > fc_id = NVME_CMD_FC_ID ;
cmdiu - > iu_len = cpu_to_be16 ( sizeof ( * cmdiu ) / sizeof ( u32 ) ) ;
memset ( & op - > rsp_iu , 0 , sizeof ( op - > rsp_iu ) ) ;
return 0 ;
}
static void
__nvme_fc_exit_request ( struct nvme_fc_ctrl * ctrl ,
struct nvme_fc_fcp_op * op )
{
fc_dma_unmap_single ( ctrl - > lport - > dev , op - > fcp_req . rspdma ,
sizeof ( op - > rsp_iu ) , DMA_FROM_DEVICE ) ;
fc_dma_unmap_single ( ctrl - > lport - > dev , op - > fcp_req . cmddma ,
sizeof ( op - > cmd_iu ) , DMA_TO_DEVICE ) ;
atomic_set ( & op - > state , FCPOP_STATE_UNINIT ) ;
}
static void
2017-05-01 19:19:08 +03:00
nvme_fc_exit_request ( struct blk_mq_tag_set * set , struct request * rq ,
unsigned int hctx_idx )
2016-12-02 11:28:42 +03:00
{
struct nvme_fc_fcp_op * op = blk_mq_rq_to_pdu ( rq ) ;
2017-05-01 19:19:08 +03:00
return __nvme_fc_exit_request ( set - > driver_data , op ) ;
2016-12-02 11:28:42 +03:00
}
2017-04-23 18:30:07 +03:00
static int
__nvme_fc_abort_op ( struct nvme_fc_ctrl * ctrl , struct nvme_fc_fcp_op * op )
{
int state ;
state = atomic_xchg ( & op - > state , FCPOP_STATE_ABORTED ) ;
if ( state ! = FCPOP_STATE_ACTIVE ) {
atomic_set ( & op - > state , state ) ;
return - ECANCELED ;
}
ctrl - > lport - > ops - > fcp_abort ( & ctrl - > lport - > localport ,
& ctrl - > rport - > remoteport ,
op - > queue - > lldd_handle ,
& op - > fcp_req ) ;
return 0 ;
}
2016-12-02 11:28:42 +03:00
static void
2017-04-23 18:30:07 +03:00
nvme_fc_abort_aen_ops ( struct nvme_fc_ctrl * ctrl )
2016-12-02 11:28:42 +03:00
{
struct nvme_fc_fcp_op * aen_op = ctrl - > aen_ops ;
2017-04-23 18:30:07 +03:00
unsigned long flags ;
int i , ret ;
2016-12-02 11:28:42 +03:00
for ( i = 0 ; i < NVME_FC_NR_AEN_COMMANDS ; i + + , aen_op + + ) {
2017-04-23 18:30:07 +03:00
if ( atomic_read ( & aen_op - > state ) ! = FCPOP_STATE_ACTIVE )
2016-12-02 11:28:42 +03:00
continue ;
2017-04-23 18:30:07 +03:00
spin_lock_irqsave ( & ctrl - > lock , flags ) ;
2017-04-23 18:30:08 +03:00
if ( ctrl - > flags & FCCTRL_TERMIO ) {
ctrl - > iocnt + + ;
aen_op - > flags | = FCOP_FLAGS_TERMIO ;
}
2017-04-23 18:30:07 +03:00
spin_unlock_irqrestore ( & ctrl - > lock , flags ) ;
ret = __nvme_fc_abort_op ( ctrl , aen_op ) ;
if ( ret ) {
/*
* if __nvme_fc_abort_op failed the io wasn ' t
* active . Thus this call path is running in
* parallel to the io complete . Treat as non - error .
*/
/* back out the flags/counters */
spin_lock_irqsave ( & ctrl - > lock , flags ) ;
2017-04-23 18:30:08 +03:00
if ( ctrl - > flags & FCCTRL_TERMIO )
ctrl - > iocnt - - ;
2017-04-23 18:30:07 +03:00
aen_op - > flags & = ~ FCOP_FLAGS_TERMIO ;
spin_unlock_irqrestore ( & ctrl - > lock , flags ) ;
return ;
}
2016-12-02 11:28:42 +03:00
}
}
2017-04-23 18:30:07 +03:00
static inline int
__nvme_fc_fcpop_chk_teardowns ( struct nvme_fc_ctrl * ctrl ,
struct nvme_fc_fcp_op * op )
{
unsigned long flags ;
bool complete_rq = false ;
spin_lock_irqsave ( & ctrl - > lock , flags ) ;
2017-04-23 18:30:08 +03:00
if ( unlikely ( op - > flags & FCOP_FLAGS_TERMIO ) ) {
2017-05-23 01:28:42 +03:00
if ( ctrl - > flags & FCCTRL_TERMIO ) {
if ( ! - - ctrl - > iocnt )
wake_up ( & ctrl - > ioabort_wait ) ;
}
2017-04-23 18:30:08 +03:00
}
2017-04-23 18:30:07 +03:00
if ( op - > flags & FCOP_FLAGS_RELEASED )
complete_rq = true ;
else
op - > flags | = FCOP_FLAGS_COMPLETE ;
spin_unlock_irqrestore ( & ctrl - > lock , flags ) ;
return complete_rq ;
}
2017-04-21 11:44:06 +03:00
static void
2016-12-02 11:28:42 +03:00
nvme_fc_fcpio_done ( struct nvmefc_fcp_req * req )
{
struct nvme_fc_fcp_op * op = fcp_req_to_fcp_op ( req ) ;
struct request * rq = op - > rq ;
struct nvmefc_fcp_req * freq = & op - > fcp_req ;
struct nvme_fc_ctrl * ctrl = op - > ctrl ;
struct nvme_fc_queue * queue = op - > queue ;
struct nvme_completion * cqe = & op - > rsp_iu . cqe ;
2017-04-23 18:30:06 +03:00
struct nvme_command * sqe = & op - > cmd_iu . sqe ;
2017-04-20 17:02:56 +03:00
__le16 status = cpu_to_le16 ( NVME_SC_SUCCESS < < 1 ) ;
2017-04-20 17:02:57 +03:00
union nvme_result result ;
2017-06-02 08:54:21 +03:00
bool complete_rq , terminate_assoc = true ;
2016-12-02 11:28:42 +03:00
/*
* WARNING :
* The current linux implementation of a nvme controller
* allocates a single tag set for all io queues and sizes
* the io queues to fully hold all possible tags . Thus , the
* implementation does not reference or care about the sqhd
* value as it never needs to use the sqhd / sqtail pointers
* for submission pacing .
*
* This affects the FC - NVME implementation in two ways :
* 1 ) As the value doesn ' t matter , we don ' t need to waste
* cycles extracting it from ERSPs and stamping it in the
* cases where the transport fabricates CQEs on successful
* completions .
* 2 ) The FC - NVME implementation requires that delivery of
* ERSP completions are to go back to the nvme layer in order
* relative to the rsn , such that the sqhd value will always
* be " in order " for the nvme layer . As the nvme layer in
* linux doesn ' t care about sqhd , there ' s no need to return
* them in order .
*
* Additionally :
* As the core nvme layer in linux currently does not look at
* every field in the cqe - in cases where the FC transport must
* fabricate a CQE , the following fields will not be set as they
* are not referenced :
* cqe . sqid , cqe . sqhd , cqe . command_id
2017-06-02 08:54:21 +03:00
*
* Failure or error of an individual i / o , in a transport
* detected fashion unrelated to the nvme completion status ,
* potentially cause the initiator and target sides to get out
* of sync on SQ head / tail ( aka outstanding io count allowed ) .
* Per FC - NVME spec , failure of an individual command requires
* the connection to be terminated , which in turn requires the
* association to be terminated .
2016-12-02 11:28:42 +03:00
*/
fc_dma_sync_single_for_cpu ( ctrl - > lport - > dev , op - > fcp_req . rspdma ,
sizeof ( op - > rsp_iu ) , DMA_FROM_DEVICE ) ;
if ( atomic_read ( & op - > state ) = = FCPOP_STATE_ABORTED )
2017-04-20 17:02:56 +03:00
status = cpu_to_le16 ( ( NVME_SC_ABORT_REQ | NVME_SC_DNR ) < < 1 ) ;
2017-03-24 06:41:27 +03:00
else if ( freq - > status )
2017-09-08 02:27:26 +03:00
status = cpu_to_le16 ( NVME_SC_INTERNAL < < 1 ) ;
2016-12-02 11:28:42 +03:00
/*
* For the linux implementation , if we have an unsuccesful
* status , they blk - mq layer can typically be called with the
* non - zero status and the content of the cqe isn ' t important .
*/
if ( status )
goto done ;
/*
* command completed successfully relative to the wire
* protocol . However , validate anything received and
* extract the status and result from the cqe ( create it
* where necessary ) .
*/
switch ( freq - > rcv_rsplen ) {
case 0 :
case NVME_FC_SIZEOF_ZEROS_RSP :
/*
* No response payload or 12 bytes of payload ( which
* should all be zeros ) are considered successful and
* no payload in the CQE by the transport .
*/
if ( freq - > transferred_length ! =
be32_to_cpu ( op - > cmd_iu . data_len ) ) {
2017-09-08 02:27:26 +03:00
status = cpu_to_le16 ( NVME_SC_INTERNAL < < 1 ) ;
2016-12-02 11:28:42 +03:00
goto done ;
}
2017-04-20 17:02:57 +03:00
result . u64 = 0 ;
2016-12-02 11:28:42 +03:00
break ;
case sizeof ( struct nvme_fc_ersp_iu ) :
/*
* The ERSP IU contains a full completion with CQE .
* Validate ERSP IU and look at cqe .
*/
if ( unlikely ( be16_to_cpu ( op - > rsp_iu . iu_len ) ! =
( freq - > rcv_rsplen / 4 ) | |
be32_to_cpu ( op - > rsp_iu . xfrd_len ) ! =
freq - > transferred_length | |
2017-03-24 06:41:23 +03:00
op - > rsp_iu . status_code | |
2017-04-23 18:30:06 +03:00
sqe - > common . command_id ! = cqe - > command_id ) ) {
2017-09-08 02:27:26 +03:00
status = cpu_to_le16 ( NVME_SC_INTERNAL < < 1 ) ;
2016-12-02 11:28:42 +03:00
goto done ;
}
2017-04-20 17:02:57 +03:00
result = cqe - > result ;
2017-04-20 17:02:56 +03:00
status = cqe - > status ;
2016-12-02 11:28:42 +03:00
break ;
default :
2017-09-08 02:27:26 +03:00
status = cpu_to_le16 ( NVME_SC_INTERNAL < < 1 ) ;
2016-12-02 11:28:42 +03:00
goto done ;
}
2017-06-02 08:54:21 +03:00
terminate_assoc = false ;
2016-12-02 11:28:42 +03:00
done :
2017-04-23 18:30:07 +03:00
if ( op - > flags & FCOP_FLAGS_AEN ) {
2017-04-20 17:02:57 +03:00
nvme_complete_async_event ( & queue - > ctrl - > ctrl , status , & result ) ;
2017-04-23 18:30:07 +03:00
complete_rq = __nvme_fc_fcpop_chk_teardowns ( ctrl , op ) ;
atomic_set ( & op - > state , FCPOP_STATE_IDLE ) ;
op - > flags = FCOP_FLAGS_AEN ; /* clear other flags */
2016-12-02 11:28:42 +03:00
nvme_fc_ctrl_put ( ctrl ) ;
2017-06-02 08:54:21 +03:00
goto check_error ;
2016-12-02 11:28:42 +03:00
}
2017-04-23 18:30:07 +03:00
complete_rq = __nvme_fc_fcpop_chk_teardowns ( ctrl , op ) ;
if ( ! complete_rq ) {
if ( unlikely ( op - > flags & FCOP_FLAGS_TERMIO ) ) {
2017-05-16 03:10:24 +03:00
status = cpu_to_le16 ( NVME_SC_ABORT_REQ < < 1 ) ;
2017-04-23 18:30:07 +03:00
if ( blk_queue_dying ( rq - > q ) )
2017-05-16 03:10:24 +03:00
status | = cpu_to_le16 ( NVME_SC_DNR < < 1 ) ;
2017-04-23 18:30:07 +03:00
}
nvme_end_request ( rq , status , result ) ;
} else
__nvme_fc_final_op_cleanup ( rq ) ;
2017-06-02 08:54:21 +03:00
check_error :
if ( terminate_assoc )
nvme_fc_error_recovery ( ctrl , " transport detected io error " ) ;
2016-12-02 11:28:42 +03:00
}
static int
__nvme_fc_init_request ( struct nvme_fc_ctrl * ctrl ,
struct nvme_fc_queue * queue , struct nvme_fc_fcp_op * op ,
struct request * rq , u32 rqno )
{
struct nvme_fc_cmd_iu * cmdiu = & op - > cmd_iu ;
int ret = 0 ;
memset ( op , 0 , sizeof ( * op ) ) ;
op - > fcp_req . cmdaddr = & op - > cmd_iu ;
op - > fcp_req . cmdlen = sizeof ( op - > cmd_iu ) ;
op - > fcp_req . rspaddr = & op - > rsp_iu ;
op - > fcp_req . rsplen = sizeof ( op - > rsp_iu ) ;
op - > fcp_req . done = nvme_fc_fcpio_done ;
op - > fcp_req . first_sgl = ( struct scatterlist * ) & op [ 1 ] ;
op - > fcp_req . private = & op - > fcp_req . first_sgl [ SG_CHUNK_SIZE ] ;
op - > ctrl = ctrl ;
op - > queue = queue ;
op - > rq = rq ;
op - > rqno = rqno ;
cmdiu - > scsi_id = NVME_CMD_SCSI_ID ;
cmdiu - > fc_id = NVME_CMD_FC_ID ;
cmdiu - > iu_len = cpu_to_be16 ( sizeof ( * cmdiu ) / sizeof ( u32 ) ) ;
op - > fcp_req . cmddma = fc_dma_map_single ( ctrl - > lport - > dev ,
& op - > cmd_iu , sizeof ( op - > cmd_iu ) , DMA_TO_DEVICE ) ;
if ( fc_dma_mapping_error ( ctrl - > lport - > dev , op - > fcp_req . cmddma ) ) {
dev_err ( ctrl - > dev ,
" FCP Op failed - cmdiu dma mapping failed. \n " ) ;
ret = EFAULT ;
goto out_on_error ;
}
op - > fcp_req . rspdma = fc_dma_map_single ( ctrl - > lport - > dev ,
& op - > rsp_iu , sizeof ( op - > rsp_iu ) ,
DMA_FROM_DEVICE ) ;
if ( fc_dma_mapping_error ( ctrl - > lport - > dev , op - > fcp_req . rspdma ) ) {
dev_err ( ctrl - > dev ,
" FCP Op failed - rspiu dma mapping failed. \n " ) ;
ret = EFAULT ;
}
atomic_set ( & op - > state , FCPOP_STATE_IDLE ) ;
out_on_error :
return ret ;
}
static int
2017-05-01 19:19:08 +03:00
nvme_fc_init_request ( struct blk_mq_tag_set * set , struct request * rq ,
unsigned int hctx_idx , unsigned int numa_node )
2016-12-02 11:28:42 +03:00
{
2017-05-01 19:19:08 +03:00
struct nvme_fc_ctrl * ctrl = set - > driver_data ;
2016-12-02 11:28:42 +03:00
struct nvme_fc_fcp_op * op = blk_mq_rq_to_pdu ( rq ) ;
2017-06-13 10:15:20 +03:00
int queue_idx = ( set = = & ctrl - > tag_set ) ? hctx_idx + 1 : 0 ;
struct nvme_fc_queue * queue = & ctrl - > queues [ queue_idx ] ;
2016-12-02 11:28:42 +03:00
return __nvme_fc_init_request ( ctrl , queue , op , rq , queue - > rqcnt + + ) ;
}
static int
nvme_fc_init_aen_ops ( struct nvme_fc_ctrl * ctrl )
{
struct nvme_fc_fcp_op * aen_op ;
struct nvme_fc_cmd_iu * cmdiu ;
struct nvme_command * sqe ;
2017-04-23 18:30:08 +03:00
void * private ;
2016-12-02 11:28:42 +03:00
int i , ret ;
aen_op = ctrl - > aen_ops ;
for ( i = 0 ; i < NVME_FC_NR_AEN_COMMANDS ; i + + , aen_op + + ) {
2017-04-23 18:30:08 +03:00
private = kzalloc ( ctrl - > lport - > ops - > fcprqst_priv_sz ,
GFP_KERNEL ) ;
if ( ! private )
return - ENOMEM ;
2016-12-02 11:28:42 +03:00
cmdiu = & aen_op - > cmd_iu ;
sqe = & cmdiu - > sqe ;
ret = __nvme_fc_init_request ( ctrl , & ctrl - > queues [ 0 ] ,
aen_op , ( struct request * ) NULL ,
( AEN_CMDID_BASE + i ) ) ;
2017-04-23 18:30:08 +03:00
if ( ret ) {
kfree ( private ) ;
2016-12-02 11:28:42 +03:00
return ret ;
2017-04-23 18:30:08 +03:00
}
2016-12-02 11:28:42 +03:00
2017-04-23 18:30:07 +03:00
aen_op - > flags = FCOP_FLAGS_AEN ;
2017-04-23 18:30:08 +03:00
aen_op - > fcp_req . first_sgl = NULL ; /* no sg list */
aen_op - > fcp_req . private = private ;
2017-04-23 18:30:07 +03:00
2016-12-02 11:28:42 +03:00
memset ( sqe , 0 , sizeof ( * sqe ) ) ;
sqe - > common . opcode = nvme_admin_async_event ;
2017-04-23 18:30:07 +03:00
/* Note: core layer may overwrite the sqe.command_id value */
2016-12-02 11:28:42 +03:00
sqe - > common . command_id = AEN_CMDID_BASE + i ;
}
return 0 ;
}
2017-04-23 18:30:08 +03:00
static void
nvme_fc_term_aen_ops ( struct nvme_fc_ctrl * ctrl )
{
struct nvme_fc_fcp_op * aen_op ;
int i ;
aen_op = ctrl - > aen_ops ;
for ( i = 0 ; i < NVME_FC_NR_AEN_COMMANDS ; i + + , aen_op + + ) {
if ( ! aen_op - > fcp_req . private )
continue ;
__nvme_fc_exit_request ( ctrl , aen_op ) ;
kfree ( aen_op - > fcp_req . private ) ;
aen_op - > fcp_req . private = NULL ;
}
}
2016-12-02 11:28:42 +03:00
static inline void
__nvme_fc_init_hctx ( struct blk_mq_hw_ctx * hctx , struct nvme_fc_ctrl * ctrl ,
unsigned int qidx )
{
struct nvme_fc_queue * queue = & ctrl - > queues [ qidx ] ;
hctx - > driver_data = queue ;
queue - > hctx = hctx ;
}
static int
nvme_fc_init_hctx ( struct blk_mq_hw_ctx * hctx , void * data ,
unsigned int hctx_idx )
{
struct nvme_fc_ctrl * ctrl = data ;
__nvme_fc_init_hctx ( hctx , ctrl , hctx_idx + 1 ) ;
return 0 ;
}
static int
nvme_fc_init_admin_hctx ( struct blk_mq_hw_ctx * hctx , void * data ,
unsigned int hctx_idx )
{
struct nvme_fc_ctrl * ctrl = data ;
__nvme_fc_init_hctx ( hctx , ctrl , hctx_idx ) ;
return 0 ;
}
static void
nvme_fc_init_queue ( struct nvme_fc_ctrl * ctrl , int idx , size_t queue_size )
{
struct nvme_fc_queue * queue ;
queue = & ctrl - > queues [ idx ] ;
memset ( queue , 0 , sizeof ( * queue ) ) ;
queue - > ctrl = ctrl ;
queue - > qnum = idx ;
atomic_set ( & queue - > csn , 1 ) ;
queue - > dev = ctrl - > dev ;
if ( idx > 0 )
queue - > cmnd_capsule_len = ctrl - > ctrl . ioccsz * 16 ;
else
queue - > cmnd_capsule_len = sizeof ( struct nvme_command ) ;
queue - > queue_size = queue_size ;
/*
* Considered whether we should allocate buffers for all SQEs
* and CQEs and dma map them - mapping their respective entries
* into the request structures ( kernel vm addr and dma address )
* thus the driver could use the buffers / mappings directly .
* It only makes sense if the LLDD would use them for its
* messaging api . It ' s very unlikely most adapter api ' s would use
* a native NVME sqe / cqe . More reasonable if FC - NVME IU payload
* structures were used instead .
*/
}
/*
* This routine terminates a queue at the transport level .
* The transport has already ensured that all outstanding ios on
* the queue have been terminated .
* The transport will send a Disconnect LS request to terminate
* the queue ' s connection . Termination of the admin queue will also
* terminate the association at the target .
*/
static void
nvme_fc_free_queue ( struct nvme_fc_queue * queue )
{
if ( ! test_and_clear_bit ( NVME_FC_Q_CONNECTED , & queue - > flags ) )
return ;
/*
* Current implementation never disconnects a single queue .
* It always terminates a whole association . So there is never
* a disconnect ( queue ) LS sent to the target .
*/
queue - > connection_id = 0 ;
clear_bit ( NVME_FC_Q_CONNECTED , & queue - > flags ) ;
}
static void
__nvme_fc_delete_hw_queue ( struct nvme_fc_ctrl * ctrl ,
struct nvme_fc_queue * queue , unsigned int qidx )
{
if ( ctrl - > lport - > ops - > delete_queue )
ctrl - > lport - > ops - > delete_queue ( & ctrl - > lport - > localport , qidx ,
queue - > lldd_handle ) ;
queue - > lldd_handle = NULL ;
}
static void
nvme_fc_free_io_queues ( struct nvme_fc_ctrl * ctrl )
{
int i ;
2017-04-24 10:58:29 +03:00
for ( i = 1 ; i < ctrl - > ctrl . queue_count ; i + + )
2016-12-02 11:28:42 +03:00
nvme_fc_free_queue ( & ctrl - > queues [ i ] ) ;
}
static int
__nvme_fc_create_hw_queue ( struct nvme_fc_ctrl * ctrl ,
struct nvme_fc_queue * queue , unsigned int qidx , u16 qsize )
{
int ret = 0 ;
queue - > lldd_handle = NULL ;
if ( ctrl - > lport - > ops - > create_queue )
ret = ctrl - > lport - > ops - > create_queue ( & ctrl - > lport - > localport ,
qidx , qsize , & queue - > lldd_handle ) ;
return ret ;
}
static void
nvme_fc_delete_hw_io_queues ( struct nvme_fc_ctrl * ctrl )
{
2017-04-24 10:58:29 +03:00
struct nvme_fc_queue * queue = & ctrl - > queues [ ctrl - > ctrl . queue_count - 1 ] ;
2016-12-02 11:28:42 +03:00
int i ;
2017-04-24 10:58:29 +03:00
for ( i = ctrl - > ctrl . queue_count - 1 ; i > = 1 ; i - - , queue - - )
2016-12-02 11:28:42 +03:00
__nvme_fc_delete_hw_queue ( ctrl , queue , i ) ;
}
static int
nvme_fc_create_hw_io_queues ( struct nvme_fc_ctrl * ctrl , u16 qsize )
{
struct nvme_fc_queue * queue = & ctrl - > queues [ 1 ] ;
2016-12-15 16:20:48 +03:00
int i , ret ;
2016-12-02 11:28:42 +03:00
2017-04-24 10:58:29 +03:00
for ( i = 1 ; i < ctrl - > ctrl . queue_count ; i + + , queue + + ) {
2016-12-02 11:28:42 +03:00
ret = __nvme_fc_create_hw_queue ( ctrl , queue , i , qsize ) ;
2016-12-15 16:20:48 +03:00
if ( ret )
goto delete_queues ;
2016-12-02 11:28:42 +03:00
}
return 0 ;
2016-12-15 16:20:48 +03:00
delete_queues :
for ( ; i > = 0 ; i - - )
__nvme_fc_delete_hw_queue ( ctrl , & ctrl - > queues [ i ] , i ) ;
return ret ;
2016-12-02 11:28:42 +03:00
}
static int
nvme_fc_connect_io_queues ( struct nvme_fc_ctrl * ctrl , u16 qsize )
{
int i , ret = 0 ;
2017-04-24 10:58:29 +03:00
for ( i = 1 ; i < ctrl - > ctrl . queue_count ; i + + ) {
2016-12-02 11:28:42 +03:00
ret = nvme_fc_connect_queue ( ctrl , & ctrl - > queues [ i ] , qsize ,
( qsize / 5 ) ) ;
if ( ret )
break ;
ret = nvmf_connect_io_queue ( & ctrl - > ctrl , i ) ;
if ( ret )
break ;
}
return ret ;
}
static void
nvme_fc_init_io_queues ( struct nvme_fc_ctrl * ctrl )
{
int i ;
2017-04-24 10:58:29 +03:00
for ( i = 1 ; i < ctrl - > ctrl . queue_count ; i + + )
2016-12-02 11:28:42 +03:00
nvme_fc_init_queue ( ctrl , i , ctrl - > ctrl . sqsize ) ;
}
static void
nvme_fc_ctrl_free ( struct kref * ref )
{
struct nvme_fc_ctrl * ctrl =
container_of ( ref , struct nvme_fc_ctrl , ref ) ;
unsigned long flags ;
2017-04-23 18:30:08 +03:00
if ( ctrl - > ctrl . tagset ) {
blk_cleanup_queue ( ctrl - > ctrl . connect_q ) ;
blk_mq_free_tag_set ( & ctrl - > tag_set ) ;
2016-12-02 11:28:42 +03:00
}
2017-04-23 18:30:08 +03:00
/* remove from rport list */
spin_lock_irqsave ( & ctrl - > rport - > lock , flags ) ;
list_del ( & ctrl - > ctrl_list ) ;
spin_unlock_irqrestore ( & ctrl - > rport - > lock , flags ) ;
2017-07-02 15:39:34 +03:00
blk_mq_unquiesce_queue ( ctrl - > ctrl . admin_q ) ;
2017-04-23 18:30:08 +03:00
blk_cleanup_queue ( ctrl - > ctrl . admin_q ) ;
blk_mq_free_tag_set ( & ctrl - > admin_tag_set ) ;
kfree ( ctrl - > queues ) ;
2016-12-02 11:28:42 +03:00
put_device ( ctrl - > dev ) ;
nvme_fc_rport_put ( ctrl - > rport ) ;
ida_simple_remove ( & nvme_fc_ctrl_cnt , ctrl - > cnum ) ;
2017-04-24 20:24:16 +03:00
if ( ctrl - > ctrl . opts )
nvmf_free_options ( ctrl - > ctrl . opts ) ;
2016-12-02 11:28:42 +03:00
kfree ( ctrl ) ;
}
static void
nvme_fc_ctrl_put ( struct nvme_fc_ctrl * ctrl )
{
kref_put ( & ctrl - > ref , nvme_fc_ctrl_free ) ;
}
static int
nvme_fc_ctrl_get ( struct nvme_fc_ctrl * ctrl )
{
return kref_get_unless_zero ( & ctrl - > ref ) ;
}
/*
* All accesses from nvme core layer done - can now free the
* controller . Called after last nvme_put_ctrl ( ) call
*/
static void
2017-04-23 18:30:08 +03:00
nvme_fc_nvme_ctrl_freed ( struct nvme_ctrl * nctrl )
2016-12-02 11:28:42 +03:00
{
struct nvme_fc_ctrl * ctrl = to_fc_ctrl ( nctrl ) ;
WARN_ON ( nctrl ! = & ctrl - > ctrl ) ;
2017-04-23 18:30:08 +03:00
nvme_fc_ctrl_put ( ctrl ) ;
}
2016-12-02 11:28:42 +03:00
2017-04-23 18:30:08 +03:00
static void
nvme_fc_error_recovery ( struct nvme_fc_ctrl * ctrl , char * errmsg )
{
2017-06-22 03:43:21 +03:00
/* only proceed if in LIVE state - e.g. on first error */
if ( ctrl - > ctrl . state ! = NVME_CTRL_LIVE )
return ;
2017-04-23 18:30:08 +03:00
dev_warn ( ctrl - > ctrl . device ,
" NVME-FC{%d}: transport association error detected: %s \n " ,
ctrl - > cnum , errmsg ) ;
2017-05-16 03:10:19 +03:00
dev_warn ( ctrl - > ctrl . device ,
2017-04-23 18:30:08 +03:00
" NVME-FC{%d}: resetting controller \n " , ctrl - > cnum ) ;
2016-12-02 11:28:42 +03:00
2017-04-23 18:30:08 +03:00
if ( ! nvme_change_ctrl_state ( & ctrl - > ctrl , NVME_CTRL_RECONNECTING ) ) {
dev_err ( ctrl - > ctrl . device ,
" NVME-FC{%d}: error_recovery: Couldn't change state "
" to RECONNECTING \n " , ctrl - > cnum ) ;
return ;
2016-12-02 11:28:42 +03:00
}
2017-06-15 16:41:08 +03:00
nvme_reset_ctrl ( & ctrl - > ctrl ) ;
2016-12-02 11:28:42 +03:00
}
2017-04-21 11:44:06 +03:00
static enum blk_eh_timer_return
2016-12-02 11:28:42 +03:00
nvme_fc_timeout ( struct request * rq , bool reserved )
{
struct nvme_fc_fcp_op * op = blk_mq_rq_to_pdu ( rq ) ;
struct nvme_fc_ctrl * ctrl = op - > ctrl ;
int ret ;
if ( reserved )
return BLK_EH_RESET_TIMER ;
ret = __nvme_fc_abort_op ( ctrl , op ) ;
if ( ret )
/* io wasn't active to abort consider it done */
return BLK_EH_HANDLED ;
/*
2017-04-23 18:30:08 +03:00
* we can ' t individually ABTS an io without affecting the queue ,
* thus killing the queue , adn thus the association .
* So resolve by performing a controller reset , which will stop
* the host / io stack , terminate the association on the link ,
* and recreate an association on the link .
2016-12-02 11:28:42 +03:00
*/
2017-04-23 18:30:08 +03:00
nvme_fc_error_recovery ( ctrl , " io timeout error " ) ;
2016-12-02 11:28:42 +03:00
return BLK_EH_HANDLED ;
}
static int
nvme_fc_map_data ( struct nvme_fc_ctrl * ctrl , struct request * rq ,
struct nvme_fc_fcp_op * op )
{
struct nvmefc_fcp_req * freq = & op - > fcp_req ;
enum dma_data_direction dir ;
int ret ;
freq - > sg_cnt = 0 ;
2017-01-13 14:29:12 +03:00
if ( ! blk_rq_payload_bytes ( rq ) )
2016-12-02 11:28:42 +03:00
return 0 ;
freq - > sg_table . sgl = freq - > first_sgl ;
2017-01-19 18:55:57 +03:00
ret = sg_alloc_table_chained ( & freq - > sg_table ,
blk_rq_nr_phys_segments ( rq ) , freq - > sg_table . sgl ) ;
2016-12-02 11:28:42 +03:00
if ( ret )
return - ENOMEM ;
op - > nents = blk_rq_map_sg ( rq - > q , rq , freq - > sg_table . sgl ) ;
2017-01-19 18:55:57 +03:00
WARN_ON ( op - > nents > blk_rq_nr_phys_segments ( rq ) ) ;
2016-12-02 11:28:42 +03:00
dir = ( rq_data_dir ( rq ) = = WRITE ) ? DMA_TO_DEVICE : DMA_FROM_DEVICE ;
freq - > sg_cnt = fc_dma_map_sg ( ctrl - > lport - > dev , freq - > sg_table . sgl ,
op - > nents , dir ) ;
if ( unlikely ( freq - > sg_cnt < = 0 ) ) {
sg_free_table_chained ( & freq - > sg_table , true ) ;
freq - > sg_cnt = 0 ;
return - EFAULT ;
}
/*
* TODO : blk_integrity_rq ( rq ) for DIF
*/
return 0 ;
}
static void
nvme_fc_unmap_data ( struct nvme_fc_ctrl * ctrl , struct request * rq ,
struct nvme_fc_fcp_op * op )
{
struct nvmefc_fcp_req * freq = & op - > fcp_req ;
if ( ! freq - > sg_cnt )
return ;
fc_dma_unmap_sg ( ctrl - > lport - > dev , freq - > sg_table . sgl , op - > nents ,
( ( rq_data_dir ( rq ) = = WRITE ) ?
DMA_TO_DEVICE : DMA_FROM_DEVICE ) ) ;
nvme_cleanup_cmd ( rq ) ;
sg_free_table_chained ( & freq - > sg_table , true ) ;
freq - > sg_cnt = 0 ;
}
/*
* In FC , the queue is a logical thing . At transport connect , the target
* creates its " queue " and returns a handle that is to be given to the
* target whenever it posts something to the corresponding SQ . When an
* SQE is sent on a SQ , FC effectively considers the SQE , or rather the
* command contained within the SQE , an io , and assigns a FC exchange
* to it . The SQE and the associated SQ handle are sent in the initial
* CMD IU sents on the exchange . All transfers relative to the io occur
* as part of the exchange . The CQE is the last thing for the io ,
* which is transferred ( explicitly or implicitly ) with the RSP IU
* sent on the exchange . After the CQE is received , the FC exchange is
* terminaed and the Exchange may be used on a different io .
*
* The transport to LLDD api has the transport making a request for a
* new fcp io request to the LLDD . The LLDD then allocates a FC exchange
* resource and transfers the command . The LLDD will then process all
* steps to complete the io . Upon completion , the transport done routine
* is called .
*
* So - while the operation is outstanding to the LLDD , there is a link
* level FC exchange resource that is also outstanding . This must be
* considered in all cleanup operations .
*/
2017-06-03 10:38:05 +03:00
static blk_status_t
2016-12-02 11:28:42 +03:00
nvme_fc_start_fcp_op ( struct nvme_fc_ctrl * ctrl , struct nvme_fc_queue * queue ,
struct nvme_fc_fcp_op * op , u32 data_len ,
enum nvmefc_fcp_datadir io_dir )
{
struct nvme_fc_cmd_iu * cmdiu = & op - > cmd_iu ;
struct nvme_command * sqe = & cmdiu - > sqe ;
u32 csn ;
int ret ;
2017-04-23 18:30:08 +03:00
/*
* before attempting to send the io , check to see if we believe
* the target device is present
*/
if ( ctrl - > rport - > remoteport . port_state ! = FC_OBJSTATE_ONLINE )
2017-07-19 00:29:34 +03:00
goto busy ;
2017-04-23 18:30:08 +03:00
2016-12-02 11:28:42 +03:00
if ( ! nvme_fc_ctrl_get ( ctrl ) )
2017-06-03 10:38:05 +03:00
return BLK_STS_IOERR ;
2016-12-02 11:28:42 +03:00
/* format the FC-NVME CMD IU and fcp_req */
cmdiu - > connection_id = cpu_to_be64 ( queue - > connection_id ) ;
csn = atomic_inc_return ( & queue - > csn ) ;
cmdiu - > csn = cpu_to_be32 ( csn ) ;
cmdiu - > data_len = cpu_to_be32 ( data_len ) ;
switch ( io_dir ) {
case NVMEFC_FCP_WRITE :
cmdiu - > flags = FCNVME_CMD_FLAGS_WRITE ;
break ;
case NVMEFC_FCP_READ :
cmdiu - > flags = FCNVME_CMD_FLAGS_READ ;
break ;
case NVMEFC_FCP_NODATA :
cmdiu - > flags = 0 ;
break ;
}
op - > fcp_req . payload_length = data_len ;
op - > fcp_req . io_dir = io_dir ;
op - > fcp_req . transferred_length = 0 ;
op - > fcp_req . rcv_rsplen = 0 ;
2017-03-24 06:41:27 +03:00
op - > fcp_req . status = NVME_SC_SUCCESS ;
2016-12-02 11:28:42 +03:00
op - > fcp_req . sqid = cpu_to_le16 ( queue - > qnum ) ;
/*
* validate per fabric rules , set fields mandated by fabric spec
* as well as those by FC - NVME spec .
*/
WARN_ON_ONCE ( sqe - > common . metadata ) ;
WARN_ON_ONCE ( sqe - > common . dptr . prp1 ) ;
WARN_ON_ONCE ( sqe - > common . dptr . prp2 ) ;
sqe - > common . flags | = NVME_CMD_SGL_METABUF ;
/*
* format SQE DPTR field per FC - NVME rules
* type = data block descr ; subtype = offset ;
* offset is currently 0.
*/
sqe - > rw . dptr . sgl . type = NVME_SGL_FMT_OFFSET ;
sqe - > rw . dptr . sgl . length = cpu_to_le32 ( data_len ) ;
sqe - > rw . dptr . sgl . addr = 0 ;
2017-04-23 18:30:07 +03:00
if ( ! ( op - > flags & FCOP_FLAGS_AEN ) ) {
2016-12-02 11:28:42 +03:00
ret = nvme_fc_map_data ( ctrl , op - > rq , op ) ;
if ( ret < 0 ) {
nvme_cleanup_cmd ( op - > rq ) ;
nvme_fc_ctrl_put ( ctrl ) ;
2017-06-03 10:38:05 +03:00
if ( ret = = - ENOMEM | | ret = = - EAGAIN )
return BLK_STS_RESOURCE ;
return BLK_STS_IOERR ;
2016-12-02 11:28:42 +03:00
}
}
fc_dma_sync_single_for_device ( ctrl - > lport - > dev , op - > fcp_req . cmddma ,
sizeof ( op - > cmd_iu ) , DMA_TO_DEVICE ) ;
atomic_set ( & op - > state , FCPOP_STATE_ACTIVE ) ;
2017-04-23 18:30:07 +03:00
if ( ! ( op - > flags & FCOP_FLAGS_AEN ) )
2016-12-02 11:28:42 +03:00
blk_mq_start_request ( op - > rq ) ;
ret = ctrl - > lport - > ops - > fcp_io ( & ctrl - > lport - > localport ,
& ctrl - > rport - > remoteport ,
queue - > lldd_handle , & op - > fcp_req ) ;
if ( ret ) {
2017-07-19 00:29:34 +03:00
if ( ! ( op - > flags & FCOP_FLAGS_AEN ) )
2016-12-02 11:28:42 +03:00
nvme_fc_unmap_data ( ctrl , op - > rq , op ) ;
nvme_fc_ctrl_put ( ctrl ) ;
2017-07-19 00:29:34 +03:00
if ( ctrl - > rport - > remoteport . port_state = = FC_OBJSTATE_ONLINE & &
ret ! = - EBUSY )
2017-06-03 10:38:05 +03:00
return BLK_STS_IOERR ;
2016-12-02 11:28:42 +03:00
2017-07-19 00:29:34 +03:00
goto busy ;
2016-12-02 11:28:42 +03:00
}
2017-06-03 10:38:05 +03:00
return BLK_STS_OK ;
2017-07-19 00:29:34 +03:00
busy :
if ( ! ( op - > flags & FCOP_FLAGS_AEN ) & & queue - > hctx )
blk_mq_delay_run_hw_queue ( queue - > hctx , NVMEFC_QUEUE_DELAY ) ;
return BLK_STS_RESOURCE ;
2016-12-02 11:28:42 +03:00
}
2017-06-03 10:38:05 +03:00
static blk_status_t
2016-12-02 11:28:42 +03:00
nvme_fc_queue_rq ( struct blk_mq_hw_ctx * hctx ,
const struct blk_mq_queue_data * bd )
{
struct nvme_ns * ns = hctx - > queue - > queuedata ;
struct nvme_fc_queue * queue = hctx - > driver_data ;
struct nvme_fc_ctrl * ctrl = queue - > ctrl ;
struct request * rq = bd - > rq ;
struct nvme_fc_fcp_op * op = blk_mq_rq_to_pdu ( rq ) ;
struct nvme_fc_cmd_iu * cmdiu = & op - > cmd_iu ;
struct nvme_command * sqe = & cmdiu - > sqe ;
enum nvmefc_fcp_datadir io_dir ;
u32 data_len ;
2017-06-03 10:38:05 +03:00
blk_status_t ret ;
2016-12-02 11:28:42 +03:00
ret = nvme_setup_cmd ( ns , rq , sqe ) ;
if ( ret )
return ret ;
2017-01-13 14:29:12 +03:00
data_len = blk_rq_payload_bytes ( rq ) ;
2016-12-02 11:28:42 +03:00
if ( data_len )
io_dir = ( ( rq_data_dir ( rq ) = = WRITE ) ?
NVMEFC_FCP_WRITE : NVMEFC_FCP_READ ) ;
else
io_dir = NVMEFC_FCP_NODATA ;
return nvme_fc_start_fcp_op ( ctrl , queue , op , data_len , io_dir ) ;
}
static struct blk_mq_tags *
nvme_fc_tagset ( struct nvme_fc_queue * queue )
{
if ( queue - > qnum = = 0 )
return queue - > ctrl - > admin_tag_set . tags [ queue - > qnum ] ;
return queue - > ctrl - > tag_set . tags [ queue - > qnum - 1 ] ;
}
static int
nvme_fc_poll ( struct blk_mq_hw_ctx * hctx , unsigned int tag )
{
struct nvme_fc_queue * queue = hctx - > driver_data ;
struct nvme_fc_ctrl * ctrl = queue - > ctrl ;
struct request * req ;
struct nvme_fc_fcp_op * op ;
req = blk_mq_tag_to_rq ( nvme_fc_tagset ( queue ) , tag ) ;
2017-04-23 18:30:08 +03:00
if ( ! req )
2016-12-02 11:28:42 +03:00
return 0 ;
op = blk_mq_rq_to_pdu ( req ) ;
if ( ( atomic_read ( & op - > state ) = = FCPOP_STATE_ACTIVE ) & &
( ctrl - > lport - > ops - > poll_queue ) )
ctrl - > lport - > ops - > poll_queue ( & ctrl - > lport - > localport ,
queue - > lldd_handle ) ;
return ( ( atomic_read ( & op - > state ) ! = FCPOP_STATE_ACTIVE ) ) ;
}
static void
nvme_fc_submit_async_event ( struct nvme_ctrl * arg , int aer_idx )
{
struct nvme_fc_ctrl * ctrl = to_fc_ctrl ( arg ) ;
struct nvme_fc_fcp_op * aen_op ;
2017-04-23 18:30:08 +03:00
unsigned long flags ;
bool terminating = false ;
2017-06-03 10:38:05 +03:00
blk_status_t ret ;
2016-12-02 11:28:42 +03:00
if ( aer_idx > NVME_FC_NR_AEN_COMMANDS )
return ;
2017-04-23 18:30:08 +03:00
spin_lock_irqsave ( & ctrl - > lock , flags ) ;
if ( ctrl - > flags & FCCTRL_TERMIO )
terminating = true ;
spin_unlock_irqrestore ( & ctrl - > lock , flags ) ;
if ( terminating )
return ;
2016-12-02 11:28:42 +03:00
aen_op = & ctrl - > aen_ops [ aer_idx ] ;
ret = nvme_fc_start_fcp_op ( ctrl , aen_op - > queue , aen_op , 0 ,
NVMEFC_FCP_NODATA ) ;
if ( ret )
dev_err ( ctrl - > ctrl . device ,
" failed async event work [%d] \n " , aer_idx ) ;
}
static void
2017-04-23 18:30:07 +03:00
__nvme_fc_final_op_cleanup ( struct request * rq )
2016-12-02 11:28:42 +03:00
{
struct nvme_fc_fcp_op * op = blk_mq_rq_to_pdu ( rq ) ;
struct nvme_fc_ctrl * ctrl = op - > ctrl ;
2017-04-23 18:30:07 +03:00
atomic_set ( & op - > state , FCPOP_STATE_IDLE ) ;
op - > flags & = ~ ( FCOP_FLAGS_TERMIO | FCOP_FLAGS_RELEASED |
FCOP_FLAGS_COMPLETE ) ;
2016-12-02 11:28:42 +03:00
nvme_fc_unmap_data ( ctrl , rq , op ) ;
2017-03-30 14:41:32 +03:00
nvme_complete_rq ( rq ) ;
2016-12-02 11:28:42 +03:00
nvme_fc_ctrl_put ( ctrl ) ;
}
2017-04-23 18:30:07 +03:00
static void
nvme_fc_complete_rq ( struct request * rq )
{
struct nvme_fc_fcp_op * op = blk_mq_rq_to_pdu ( rq ) ;
struct nvme_fc_ctrl * ctrl = op - > ctrl ;
unsigned long flags ;
bool completed = false ;
/*
* the core layer , on controller resets after calling
* nvme_shutdown_ctrl ( ) , calls complete_rq without our
* calling blk_mq_complete_request ( ) , thus there may still
* be live i / o outstanding with the LLDD . Means transport has
* to track complete calls vs fcpio_done calls to know what
* path to take on completes and dones .
*/
spin_lock_irqsave ( & ctrl - > lock , flags ) ;
if ( op - > flags & FCOP_FLAGS_COMPLETE )
completed = true ;
else
op - > flags | = FCOP_FLAGS_RELEASED ;
spin_unlock_irqrestore ( & ctrl - > lock , flags ) ;
if ( completed )
__nvme_fc_final_op_cleanup ( rq ) ;
}
2016-12-02 11:28:42 +03:00
/*
* This routine is used by the transport when it needs to find active
* io on a queue that is to be terminated . The transport uses
* blk_mq_tagset_busy_itr ( ) to find the busy requests , which then invoke
* this routine to kill them on a 1 by 1 basis .
*
* As FC allocates FC exchange for each io , the transport must contact
* the LLDD to terminate the exchange , thus releasing the FC exchange .
* After terminating the exchange the LLDD will call the transport ' s
* normal io done path for the request , but it will have an aborted
* status . The done path will return the io request back to the block
* layer with an error status .
*/
static void
nvme_fc_terminate_exchange ( struct request * req , void * data , bool reserved )
{
struct nvme_ctrl * nctrl = data ;
struct nvme_fc_ctrl * ctrl = to_fc_ctrl ( nctrl ) ;
struct nvme_fc_fcp_op * op = blk_mq_rq_to_pdu ( req ) ;
2017-04-23 18:30:07 +03:00
unsigned long flags ;
int status ;
2016-12-02 11:28:42 +03:00
if ( ! blk_mq_request_started ( req ) )
return ;
2017-04-23 18:30:07 +03:00
spin_lock_irqsave ( & ctrl - > lock , flags ) ;
2017-04-23 18:30:08 +03:00
if ( ctrl - > flags & FCCTRL_TERMIO ) {
ctrl - > iocnt + + ;
op - > flags | = FCOP_FLAGS_TERMIO ;
}
2017-04-23 18:30:07 +03:00
spin_unlock_irqrestore ( & ctrl - > lock , flags ) ;
2016-12-02 11:28:42 +03:00
status = __nvme_fc_abort_op ( ctrl , op ) ;
2017-04-23 18:30:07 +03:00
if ( status ) {
/*
* if __nvme_fc_abort_op failed the io wasn ' t
* active . Thus this call path is running in
* parallel to the io complete . Treat as non - error .
*/
/* back out the flags/counters */
spin_lock_irqsave ( & ctrl - > lock , flags ) ;
2017-04-23 18:30:08 +03:00
if ( ctrl - > flags & FCCTRL_TERMIO )
ctrl - > iocnt - - ;
2017-04-23 18:30:07 +03:00
op - > flags & = ~ FCOP_FLAGS_TERMIO ;
spin_unlock_irqrestore ( & ctrl - > lock , flags ) ;
2016-12-02 11:28:42 +03:00
return ;
2017-04-23 18:30:07 +03:00
}
2016-12-02 11:28:42 +03:00
}
2017-04-23 18:30:07 +03:00
2017-04-23 18:30:08 +03:00
static const struct blk_mq_ops nvme_fc_mq_ops = {
. queue_rq = nvme_fc_queue_rq ,
. complete = nvme_fc_complete_rq ,
. init_request = nvme_fc_init_request ,
. exit_request = nvme_fc_exit_request ,
. init_hctx = nvme_fc_init_hctx ,
. poll = nvme_fc_poll ,
. timeout = nvme_fc_timeout ,
} ;
2016-12-02 11:28:42 +03:00
2017-04-23 18:30:08 +03:00
static int
nvme_fc_create_io_queues ( struct nvme_fc_ctrl * ctrl )
2016-12-02 11:28:42 +03:00
{
2017-04-23 18:30:08 +03:00
struct nvmf_ctrl_options * opts = ctrl - > ctrl . opts ;
2017-06-29 11:16:49 +03:00
unsigned int nr_io_queues ;
2017-04-23 18:30:08 +03:00
int ret ;
2016-12-02 11:28:42 +03:00
2017-06-29 11:16:49 +03:00
nr_io_queues = min ( min ( opts - > nr_io_queues , num_online_cpus ( ) ) ,
ctrl - > lport - > ops - > max_hw_queues ) ;
ret = nvme_set_queue_count ( & ctrl - > ctrl , & nr_io_queues ) ;
2017-04-23 18:30:08 +03:00
if ( ret ) {
dev_info ( ctrl - > ctrl . device ,
" set_queue_count failed: %d \n " , ret ) ;
return ret ;
}
2016-12-02 11:28:42 +03:00
2017-06-29 11:16:49 +03:00
ctrl - > ctrl . queue_count = nr_io_queues + 1 ;
if ( ! nr_io_queues )
2017-04-23 18:30:08 +03:00
return 0 ;
2016-12-02 11:28:42 +03:00
2017-04-23 18:30:08 +03:00
nvme_fc_init_io_queues ( ctrl ) ;
2016-12-02 11:28:42 +03:00
2017-04-23 18:30:08 +03:00
memset ( & ctrl - > tag_set , 0 , sizeof ( ctrl - > tag_set ) ) ;
ctrl - > tag_set . ops = & nvme_fc_mq_ops ;
ctrl - > tag_set . queue_depth = ctrl - > ctrl . opts - > queue_size ;
ctrl - > tag_set . reserved_tags = 1 ; /* fabric connect */
ctrl - > tag_set . numa_node = NUMA_NO_NODE ;
ctrl - > tag_set . flags = BLK_MQ_F_SHOULD_MERGE ;
ctrl - > tag_set . cmd_size = sizeof ( struct nvme_fc_fcp_op ) +
( SG_CHUNK_SIZE *
sizeof ( struct scatterlist ) ) +
ctrl - > lport - > ops - > fcprqst_priv_sz ;
ctrl - > tag_set . driver_data = ctrl ;
2017-04-24 10:58:29 +03:00
ctrl - > tag_set . nr_hw_queues = ctrl - > ctrl . queue_count - 1 ;
2017-04-23 18:30:08 +03:00
ctrl - > tag_set . timeout = NVME_IO_TIMEOUT ;
2016-12-02 11:28:42 +03:00
2017-04-23 18:30:08 +03:00
ret = blk_mq_alloc_tag_set ( & ctrl - > tag_set ) ;
if ( ret )
return ret ;
2016-12-02 11:28:42 +03:00
2017-04-23 18:30:08 +03:00
ctrl - > ctrl . tagset = & ctrl - > tag_set ;
2016-12-02 11:28:42 +03:00
2017-04-23 18:30:08 +03:00
ctrl - > ctrl . connect_q = blk_mq_init_queue ( & ctrl - > tag_set ) ;
if ( IS_ERR ( ctrl - > ctrl . connect_q ) ) {
ret = PTR_ERR ( ctrl - > ctrl . connect_q ) ;
goto out_free_tag_set ;
}
2016-12-02 11:28:42 +03:00
2017-04-23 18:30:08 +03:00
ret = nvme_fc_create_hw_io_queues ( ctrl , ctrl - > ctrl . opts - > queue_size ) ;
2016-12-02 11:28:42 +03:00
if ( ret )
2017-04-23 18:30:08 +03:00
goto out_cleanup_blk_queue ;
2016-12-02 11:28:42 +03:00
2017-04-23 18:30:08 +03:00
ret = nvme_fc_connect_io_queues ( ctrl , ctrl - > ctrl . opts - > queue_size ) ;
if ( ret )
goto out_delete_hw_queues ;
2016-12-02 11:28:42 +03:00
return 0 ;
2017-04-23 18:30:08 +03:00
out_delete_hw_queues :
nvme_fc_delete_hw_io_queues ( ctrl ) ;
out_cleanup_blk_queue :
blk_cleanup_queue ( ctrl - > ctrl . connect_q ) ;
out_free_tag_set :
blk_mq_free_tag_set ( & ctrl - > tag_set ) ;
nvme_fc_free_io_queues ( ctrl ) ;
2016-12-02 11:28:42 +03:00
2017-04-23 18:30:08 +03:00
/* force put free routine to ignore io queues */
ctrl - > ctrl . tagset = NULL ;
return ret ;
}
2016-12-02 11:28:42 +03:00
static int
2017-04-23 18:30:08 +03:00
nvme_fc_reinit_io_queues ( struct nvme_fc_ctrl * ctrl )
2016-12-02 11:28:42 +03:00
{
struct nvmf_ctrl_options * opts = ctrl - > ctrl . opts ;
2017-06-29 11:16:49 +03:00
unsigned int nr_io_queues ;
2016-12-02 11:28:42 +03:00
int ret ;
2017-06-29 11:16:49 +03:00
nr_io_queues = min ( min ( opts - > nr_io_queues , num_online_cpus ( ) ) ,
ctrl - > lport - > ops - > max_hw_queues ) ;
ret = nvme_set_queue_count ( & ctrl - > ctrl , & nr_io_queues ) ;
2016-12-02 11:28:42 +03:00
if ( ret ) {
dev_info ( ctrl - > ctrl . device ,
" set_queue_count failed: %d \n " , ret ) ;
return ret ;
}
2017-06-29 11:16:49 +03:00
ctrl - > ctrl . queue_count = nr_io_queues + 1 ;
2017-04-23 18:30:08 +03:00
/* check for io queues existing */
2017-04-24 10:58:29 +03:00
if ( ctrl - > ctrl . queue_count = = 1 )
2016-12-02 11:28:42 +03:00
return 0 ;
nvme_fc_init_io_queues ( ctrl ) ;
2017-08-18 02:23:03 +03:00
ret = blk_mq_reinit_tagset ( & ctrl - > tag_set , nvme_fc_reinit_request ) ;
2016-12-02 11:28:42 +03:00
if ( ret )
2017-04-23 18:30:08 +03:00
goto out_free_io_queues ;
2016-12-02 11:28:42 +03:00
ret = nvme_fc_create_hw_io_queues ( ctrl , ctrl - > ctrl . opts - > queue_size ) ;
if ( ret )
2017-04-23 18:30:08 +03:00
goto out_free_io_queues ;
2016-12-02 11:28:42 +03:00
ret = nvme_fc_connect_io_queues ( ctrl , ctrl - > ctrl . opts - > queue_size ) ;
if ( ret )
goto out_delete_hw_queues ;
2017-06-29 11:20:10 +03:00
blk_mq_update_nr_hw_queues ( & ctrl - > tag_set , nr_io_queues ) ;
2016-12-02 11:28:42 +03:00
return 0 ;
out_delete_hw_queues :
nvme_fc_delete_hw_io_queues ( ctrl ) ;
2017-04-23 18:30:08 +03:00
out_free_io_queues :
2016-12-02 11:28:42 +03:00
nvme_fc_free_io_queues ( ctrl ) ;
2017-04-23 18:30:08 +03:00
return ret ;
}
2016-12-02 11:28:42 +03:00
2017-04-23 18:30:08 +03:00
/*
* This routine restarts the controller on the host side , and
* on the link side , recreates the controller association .
*/
static int
nvme_fc_create_association ( struct nvme_fc_ctrl * ctrl )
{
struct nvmf_ctrl_options * opts = ctrl - > ctrl . opts ;
u32 segs ;
int ret ;
bool changed ;
2017-05-04 13:33:15 +03:00
+ + ctrl - > ctrl . nr_reconnects ;
2017-04-23 18:30:08 +03:00
/*
* Create the admin queue
*/
nvme_fc_init_queue ( ctrl , 0 , NVME_FC_AQ_BLKMQ_DEPTH ) ;
ret = __nvme_fc_create_hw_queue ( ctrl , & ctrl - > queues [ 0 ] , 0 ,
NVME_FC_AQ_BLKMQ_DEPTH ) ;
if ( ret )
goto out_free_queue ;
ret = nvme_fc_connect_admin_queue ( ctrl , & ctrl - > queues [ 0 ] ,
NVME_FC_AQ_BLKMQ_DEPTH ,
( NVME_FC_AQ_BLKMQ_DEPTH / 4 ) ) ;
if ( ret )
goto out_delete_hw_queue ;
if ( ctrl - > ctrl . state ! = NVME_CTRL_NEW )
2017-07-02 15:39:34 +03:00
blk_mq_unquiesce_queue ( ctrl - > ctrl . admin_q ) ;
2017-04-23 18:30:08 +03:00
ret = nvmf_connect_admin_queue ( & ctrl - > ctrl ) ;
if ( ret )
goto out_disconnect_admin_queue ;
/*
* Check controller capabilities
*
* todo : - add code to check if ctrl attributes changed from
* prior connection values
*/
2017-06-27 22:16:38 +03:00
ret = nvmf_reg_read64 ( & ctrl - > ctrl , NVME_REG_CAP , & ctrl - > ctrl . cap ) ;
2017-04-23 18:30:08 +03:00
if ( ret ) {
dev_err ( ctrl - > ctrl . device ,
" prop_get NVME_REG_CAP failed \n " ) ;
goto out_disconnect_admin_queue ;
}
ctrl - > ctrl . sqsize =
2017-06-27 22:16:38 +03:00
min_t ( int , NVME_CAP_MQES ( ctrl - > ctrl . cap ) + 1 , ctrl - > ctrl . sqsize ) ;
2017-04-23 18:30:08 +03:00
2017-06-27 22:16:38 +03:00
ret = nvme_enable_ctrl ( & ctrl - > ctrl , ctrl - > ctrl . cap ) ;
2017-04-23 18:30:08 +03:00
if ( ret )
goto out_disconnect_admin_queue ;
segs = min_t ( u32 , NVME_FC_MAX_SEGMENTS ,
ctrl - > lport - > ops - > max_sgl_segments ) ;
ctrl - > ctrl . max_hw_sectors = ( segs - 1 ) < < ( PAGE_SHIFT - 9 ) ;
ret = nvme_init_identify ( & ctrl - > ctrl ) ;
if ( ret )
goto out_disconnect_admin_queue ;
/* sanity checks */
/* FC-NVME does not have other data in the capsule */
if ( ctrl - > ctrl . icdoff ) {
dev_err ( ctrl - > ctrl . device , " icdoff %d is not supported! \n " ,
ctrl - > ctrl . icdoff ) ;
goto out_disconnect_admin_queue ;
}
/* FC-NVME supports normal SGL Data Block Descriptors */
if ( opts - > queue_size > ctrl - > ctrl . maxcmd ) {
/* warn if maxcmd is lower than queue_size */
dev_warn ( ctrl - > ctrl . device ,
" queue_size %zu > ctrl maxcmd %u, reducing "
" to queue_size \n " ,
opts - > queue_size , ctrl - > ctrl . maxcmd ) ;
opts - > queue_size = ctrl - > ctrl . maxcmd ;
}
ret = nvme_fc_init_aen_ops ( ctrl ) ;
if ( ret )
goto out_term_aen_ops ;
/*
* Create the io queues
*/
2017-04-24 10:58:29 +03:00
if ( ctrl - > ctrl . queue_count > 1 ) {
2017-04-23 18:30:08 +03:00
if ( ctrl - > ctrl . state = = NVME_CTRL_NEW )
ret = nvme_fc_create_io_queues ( ctrl ) ;
else
ret = nvme_fc_reinit_io_queues ( ctrl ) ;
if ( ret )
goto out_term_aen_ops ;
}
changed = nvme_change_ctrl_state ( & ctrl - > ctrl , NVME_CTRL_LIVE ) ;
WARN_ON_ONCE ( ! changed ) ;
2017-05-04 13:33:15 +03:00
ctrl - > ctrl . nr_reconnects = 0 ;
2017-04-23 18:30:08 +03:00
2017-07-02 10:56:43 +03:00
nvme_start_ctrl ( & ctrl - > ctrl ) ;
2017-04-23 18:30:08 +03:00
return 0 ; /* Success */
out_term_aen_ops :
nvme_fc_term_aen_ops ( ctrl ) ;
out_disconnect_admin_queue :
/* send a Disconnect(association) LS to fc-nvme target */
nvme_fc_xmt_disconnect_assoc ( ctrl ) ;
out_delete_hw_queue :
__nvme_fc_delete_hw_queue ( ctrl , & ctrl - > queues [ 0 ] , 0 ) ;
out_free_queue :
nvme_fc_free_queue ( & ctrl - > queues [ 0 ] ) ;
2016-12-02 11:28:42 +03:00
return ret ;
}
2017-04-23 18:30:08 +03:00
/*
* This routine stops operation of the controller on the host side .
* On the host os stack side : Admin and IO queues are stopped ,
* outstanding ios on them terminated via FC ABTS .
* On the link side : the association is terminated .
*/
static void
nvme_fc_delete_association ( struct nvme_fc_ctrl * ctrl )
{
unsigned long flags ;
spin_lock_irqsave ( & ctrl - > lock , flags ) ;
ctrl - > flags | = FCCTRL_TERMIO ;
ctrl - > iocnt = 0 ;
spin_unlock_irqrestore ( & ctrl - > lock , flags ) ;
/*
* If io queues are present , stop them and terminate all outstanding
* ios on them . As FC allocates FC exchange for each io , the
* transport must contact the LLDD to terminate the exchange ,
* thus releasing the FC exchange . We use blk_mq_tagset_busy_itr ( )
* to tell us what io ' s are busy and invoke a transport routine
* to kill them with the LLDD . After terminating the exchange
* the LLDD will call the transport ' s normal io done path , but it
* will have an aborted status . The done path will return the
* io requests back to the block layer as part of normal completions
* ( but with error status ) .
*/
2017-04-24 10:58:29 +03:00
if ( ctrl - > ctrl . queue_count > 1 ) {
2017-04-23 18:30:08 +03:00
nvme_stop_queues ( & ctrl - > ctrl ) ;
blk_mq_tagset_busy_iter ( & ctrl - > tag_set ,
nvme_fc_terminate_exchange , & ctrl - > ctrl ) ;
}
/*
* Other transports , which don ' t have link - level contexts bound
* to sqe ' s , would try to gracefully shutdown the controller by
* writing the registers for shutdown and polling ( call
* nvme_shutdown_ctrl ( ) ) . Given a bunch of i / o was potentially
* just aborted and we will wait on those contexts , and given
* there was no indication of how live the controlelr is on the
* link , don ' t send more io to create more contexts for the
* shutdown . Let the controller fail via keepalive failure if
* its still present .
*/
/*
* clean up the admin queue . Same thing as above .
* use blk_mq_tagset_busy_itr ( ) and the transport routine to
* terminate the exchanges .
*/
2017-07-02 15:39:34 +03:00
blk_mq_quiesce_queue ( ctrl - > ctrl . admin_q ) ;
2017-04-23 18:30:08 +03:00
blk_mq_tagset_busy_iter ( & ctrl - > admin_tag_set ,
nvme_fc_terminate_exchange , & ctrl - > ctrl ) ;
/* kill the aens as they are a separate path */
nvme_fc_abort_aen_ops ( ctrl ) ;
/* wait for all io that had to be aborted */
spin_lock_irqsave ( & ctrl - > lock , flags ) ;
2017-05-23 01:28:42 +03:00
wait_event_lock_irq ( ctrl - > ioabort_wait , ctrl - > iocnt = = 0 , ctrl - > lock ) ;
2017-04-23 18:30:08 +03:00
ctrl - > flags & = ~ FCCTRL_TERMIO ;
spin_unlock_irqrestore ( & ctrl - > lock , flags ) ;
nvme_fc_term_aen_ops ( ctrl ) ;
/*
* send a Disconnect ( association ) LS to fc - nvme target
* Note : could have been sent at top of process , but
* cleaner on link traffic if after the aborts complete .
* Note : if association doesn ' t exist , association_id will be 0
*/
if ( ctrl - > association_id )
nvme_fc_xmt_disconnect_assoc ( ctrl ) ;
if ( ctrl - > ctrl . tagset ) {
nvme_fc_delete_hw_io_queues ( ctrl ) ;
nvme_fc_free_io_queues ( ctrl ) ;
}
__nvme_fc_delete_hw_queue ( ctrl , & ctrl - > queues [ 0 ] , 0 ) ;
nvme_fc_free_queue ( & ctrl - > queues [ 0 ] ) ;
}
static void
nvme_fc_delete_ctrl_work ( struct work_struct * work )
{
struct nvme_fc_ctrl * ctrl =
container_of ( work , struct nvme_fc_ctrl , delete_work ) ;
2017-06-15 16:41:08 +03:00
cancel_work_sync ( & ctrl - > ctrl . reset_work ) ;
2017-04-23 18:30:08 +03:00
cancel_delayed_work_sync ( & ctrl - > connect_work ) ;
2017-07-02 10:56:43 +03:00
nvme_stop_ctrl ( & ctrl - > ctrl ) ;
nvme_remove_namespaces ( & ctrl - > ctrl ) ;
2017-04-23 18:30:08 +03:00
/*
* kill the association on the link side . this will block
* waiting for io to terminate
*/
nvme_fc_delete_association ( ctrl ) ;
/*
* tear down the controller
2017-05-16 03:10:18 +03:00
* After the last reference on the nvme ctrl is removed ,
* the transport nvme_fc_nvme_ctrl_freed ( ) callback will be
* invoked . From there , the transport will tear down it ' s
* logical queues and association .
2017-04-23 18:30:08 +03:00
*/
nvme_uninit_ctrl ( & ctrl - > ctrl ) ;
nvme_put_ctrl ( & ctrl - > ctrl ) ;
}
2017-05-16 03:10:16 +03:00
static bool
__nvme_fc_schedule_delete_work ( struct nvme_fc_ctrl * ctrl )
2017-04-23 18:30:08 +03:00
{
if ( ! nvme_change_ctrl_state ( & ctrl - > ctrl , NVME_CTRL_DELETING ) )
2017-05-16 03:10:16 +03:00
return true ;
2017-04-23 18:30:08 +03:00
2017-06-07 21:31:55 +03:00
if ( ! queue_work ( nvme_wq , & ctrl - > delete_work ) )
2017-05-16 03:10:16 +03:00
return true ;
2017-04-23 18:30:08 +03:00
2017-05-16 03:10:16 +03:00
return false ;
}
static int
__nvme_fc_del_ctrl ( struct nvme_fc_ctrl * ctrl )
{
return __nvme_fc_schedule_delete_work ( ctrl ) ? - EBUSY : 0 ;
2017-04-23 18:30:08 +03:00
}
/*
* Request from nvme core layer to delete the controller
*/
static int
nvme_fc_del_nvme_ctrl ( struct nvme_ctrl * nctrl )
{
struct nvme_fc_ctrl * ctrl = to_fc_ctrl ( nctrl ) ;
int ret ;
if ( ! kref_get_unless_zero ( & ctrl - > ctrl . kref ) )
return - EBUSY ;
ret = __nvme_fc_del_ctrl ( ctrl ) ;
if ( ! ret )
2017-06-07 21:31:55 +03:00
flush_workqueue ( nvme_wq ) ;
2017-04-23 18:30:08 +03:00
nvme_put_ctrl ( & ctrl - > ctrl ) ;
return ret ;
}
2017-05-16 03:10:16 +03:00
static void
nvme_fc_reconnect_or_delete ( struct nvme_fc_ctrl * ctrl , int status )
{
/* If we are resetting/deleting then do nothing */
if ( ctrl - > ctrl . state ! = NVME_CTRL_RECONNECTING ) {
WARN_ON_ONCE ( ctrl - > ctrl . state = = NVME_CTRL_NEW | |
ctrl - > ctrl . state = = NVME_CTRL_LIVE ) ;
return ;
}
2017-05-16 03:10:19 +03:00
dev_info ( ctrl - > ctrl . device ,
2017-05-16 03:10:16 +03:00
" NVME-FC{%d}: reset: Reconnect attempt failed (%d) \n " ,
ctrl - > cnum , status ) ;
if ( nvmf_should_reconnect ( & ctrl - > ctrl ) ) {
dev_info ( ctrl - > ctrl . device ,
" NVME-FC{%d}: Reconnect attempt in %d seconds. \n " ,
ctrl - > cnum , ctrl - > ctrl . opts - > reconnect_delay ) ;
2017-06-07 21:31:55 +03:00
queue_delayed_work ( nvme_wq , & ctrl - > connect_work ,
2017-05-16 03:10:16 +03:00
ctrl - > ctrl . opts - > reconnect_delay * HZ ) ;
} else {
2017-05-16 03:10:19 +03:00
dev_warn ( ctrl - > ctrl . device ,
2017-05-16 03:10:16 +03:00
" NVME-FC{%d}: Max reconnect attempts (%d) "
" reached. Removing controller \n " ,
2017-05-04 13:33:15 +03:00
ctrl - > cnum , ctrl - > ctrl . nr_reconnects ) ;
2017-05-16 03:10:16 +03:00
WARN_ON ( __nvme_fc_schedule_delete_work ( ctrl ) ) ;
}
}
2017-04-23 18:30:08 +03:00
static void
nvme_fc_reset_ctrl_work ( struct work_struct * work )
{
struct nvme_fc_ctrl * ctrl =
2017-06-15 16:41:08 +03:00
container_of ( work , struct nvme_fc_ctrl , ctrl . reset_work ) ;
2017-04-23 18:30:08 +03:00
int ret ;
2017-07-02 10:56:43 +03:00
nvme_stop_ctrl ( & ctrl - > ctrl ) ;
2017-04-23 18:30:08 +03:00
/* will block will waiting for io to terminate */
nvme_fc_delete_association ( ctrl ) ;
ret = nvme_fc_create_association ( ctrl ) ;
2017-05-16 03:10:16 +03:00
if ( ret )
nvme_fc_reconnect_or_delete ( ctrl , ret ) ;
else
2017-04-23 18:30:08 +03:00
dev_info ( ctrl - > ctrl . device ,
" NVME-FC{%d}: controller reset complete \n " , ctrl - > cnum ) ;
}
static const struct nvme_ctrl_ops nvme_fc_ctrl_ops = {
. name = " fc " ,
. module = THIS_MODULE ,
2017-05-20 16:14:44 +03:00
. flags = NVME_F_FABRICS ,
2017-04-23 18:30:08 +03:00
. reg_read32 = nvmf_reg_read32 ,
. reg_read64 = nvmf_reg_read64 ,
. reg_write32 = nvmf_reg_write32 ,
. free_ctrl = nvme_fc_nvme_ctrl_freed ,
. submit_async_event = nvme_fc_submit_async_event ,
. delete_ctrl = nvme_fc_del_nvme_ctrl ,
. get_address = nvmf_get_address ,
} ;
static void
nvme_fc_connect_ctrl_work ( struct work_struct * work )
{
int ret ;
struct nvme_fc_ctrl * ctrl =
container_of ( to_delayed_work ( work ) ,
struct nvme_fc_ctrl , connect_work ) ;
ret = nvme_fc_create_association ( ctrl ) ;
2017-05-16 03:10:16 +03:00
if ( ret )
nvme_fc_reconnect_or_delete ( ctrl , ret ) ;
else
2017-04-23 18:30:08 +03:00
dev_info ( ctrl - > ctrl . device ,
" NVME-FC{%d}: controller reconnect complete \n " ,
ctrl - > cnum ) ;
}
static const struct blk_mq_ops nvme_fc_admin_mq_ops = {
. queue_rq = nvme_fc_queue_rq ,
. complete = nvme_fc_complete_rq ,
2017-06-13 10:15:20 +03:00
. init_request = nvme_fc_init_request ,
2017-04-23 18:30:08 +03:00
. exit_request = nvme_fc_exit_request ,
. init_hctx = nvme_fc_init_admin_hctx ,
. timeout = nvme_fc_timeout ,
} ;
2016-12-02 11:28:42 +03:00
static struct nvme_ctrl *
2017-04-23 18:30:08 +03:00
nvme_fc_init_ctrl ( struct device * dev , struct nvmf_ctrl_options * opts ,
2016-12-02 11:28:42 +03:00
struct nvme_fc_lport * lport , struct nvme_fc_rport * rport )
{
struct nvme_fc_ctrl * ctrl ;
unsigned long flags ;
int ret , idx ;
2017-05-06 02:13:15 +03:00
if ( ! ( rport - > remoteport . port_role &
( FC_PORT_ROLE_NVME_DISCOVERY | FC_PORT_ROLE_NVME_TARGET ) ) ) {
ret = - EBADR ;
goto out_fail ;
}
2016-12-02 11:28:42 +03:00
ctrl = kzalloc ( sizeof ( * ctrl ) , GFP_KERNEL ) ;
if ( ! ctrl ) {
ret = - ENOMEM ;
goto out_fail ;
}
idx = ida_simple_get ( & nvme_fc_ctrl_cnt , 0 , 0 , GFP_KERNEL ) ;
if ( idx < 0 ) {
ret = - ENOSPC ;
goto out_free_ctrl ;
}
ctrl - > ctrl . opts = opts ;
INIT_LIST_HEAD ( & ctrl - > ctrl_list ) ;
ctrl - > lport = lport ;
ctrl - > rport = rport ;
ctrl - > dev = lport - > dev ;
ctrl - > cnum = idx ;
get_device ( ctrl - > dev ) ;
kref_init ( & ctrl - > ref ) ;
2017-04-23 18:30:08 +03:00
INIT_WORK ( & ctrl - > delete_work , nvme_fc_delete_ctrl_work ) ;
2017-06-15 16:41:08 +03:00
INIT_WORK ( & ctrl - > ctrl . reset_work , nvme_fc_reset_ctrl_work ) ;
2017-04-23 18:30:08 +03:00
INIT_DELAYED_WORK ( & ctrl - > connect_work , nvme_fc_connect_ctrl_work ) ;
2016-12-02 11:28:42 +03:00
spin_lock_init ( & ctrl - > lock ) ;
/* io queue count */
2017-04-24 10:58:29 +03:00
ctrl - > ctrl . queue_count = min_t ( unsigned int ,
2016-12-02 11:28:42 +03:00
opts - > nr_io_queues ,
lport - > ops - > max_hw_queues ) ;
2017-04-24 10:58:29 +03:00
ctrl - > ctrl . queue_count + + ; /* +1 for admin queue */
2016-12-02 11:28:42 +03:00
ctrl - > ctrl . sqsize = opts - > queue_size - 1 ;
ctrl - > ctrl . kato = opts - > kato ;
ret = - ENOMEM ;
2017-04-24 10:58:29 +03:00
ctrl - > queues = kcalloc ( ctrl - > ctrl . queue_count ,
sizeof ( struct nvme_fc_queue ) , GFP_KERNEL ) ;
2016-12-02 11:28:42 +03:00
if ( ! ctrl - > queues )
2017-04-23 18:30:08 +03:00
goto out_free_ida ;
2016-12-02 11:28:42 +03:00
2017-04-23 18:30:08 +03:00
memset ( & ctrl - > admin_tag_set , 0 , sizeof ( ctrl - > admin_tag_set ) ) ;
ctrl - > admin_tag_set . ops = & nvme_fc_admin_mq_ops ;
ctrl - > admin_tag_set . queue_depth = NVME_FC_AQ_BLKMQ_DEPTH ;
ctrl - > admin_tag_set . reserved_tags = 2 ; /* fabric connect + Keep-Alive */
ctrl - > admin_tag_set . numa_node = NUMA_NO_NODE ;
ctrl - > admin_tag_set . cmd_size = sizeof ( struct nvme_fc_fcp_op ) +
( SG_CHUNK_SIZE *
sizeof ( struct scatterlist ) ) +
ctrl - > lport - > ops - > fcprqst_priv_sz ;
ctrl - > admin_tag_set . driver_data = ctrl ;
ctrl - > admin_tag_set . nr_hw_queues = 1 ;
ctrl - > admin_tag_set . timeout = ADMIN_TIMEOUT ;
2016-12-02 11:28:42 +03:00
2017-04-23 18:30:08 +03:00
ret = blk_mq_alloc_tag_set ( & ctrl - > admin_tag_set ) ;
2016-12-02 11:28:42 +03:00
if ( ret )
2017-04-23 18:30:08 +03:00
goto out_free_queues ;
2017-07-10 09:22:29 +03:00
ctrl - > ctrl . admin_tagset = & ctrl - > admin_tag_set ;
2016-12-02 11:28:42 +03:00
2017-04-23 18:30:08 +03:00
ctrl - > ctrl . admin_q = blk_mq_init_queue ( & ctrl - > admin_tag_set ) ;
if ( IS_ERR ( ctrl - > ctrl . admin_q ) ) {
ret = PTR_ERR ( ctrl - > ctrl . admin_q ) ;
goto out_free_admin_tag_set ;
2016-12-02 11:28:42 +03:00
}
2017-04-23 18:30:08 +03:00
/*
* Would have been nice to init io queues tag set as well .
* However , we require interaction from the controller
* for max io queue count before we can do so .
* Defer this to the connect path .
*/
2016-12-02 11:28:42 +03:00
2017-04-23 18:30:08 +03:00
ret = nvme_init_ctrl ( & ctrl - > ctrl , dev , & nvme_fc_ctrl_ops , 0 ) ;
if ( ret )
goto out_cleanup_admin_q ;
2016-12-02 11:28:42 +03:00
2017-04-23 18:30:08 +03:00
/* at this point, teardown path changes to ref counting on nvme ctrl */
2016-12-02 11:28:42 +03:00
spin_lock_irqsave ( & rport - > lock , flags ) ;
list_add_tail ( & ctrl - > ctrl_list , & rport - > ctrl_list ) ;
spin_unlock_irqrestore ( & rport - > lock , flags ) ;
2017-04-23 18:30:08 +03:00
ret = nvme_fc_create_association ( ctrl ) ;
if ( ret ) {
2017-04-24 20:24:16 +03:00
ctrl - > ctrl . opts = NULL ;
2017-04-23 18:30:08 +03:00
/* initiate nvme ctrl ref counting teardown */
nvme_uninit_ctrl ( & ctrl - > ctrl ) ;
2017-06-06 01:03:42 +03:00
nvme_put_ctrl ( & ctrl - > ctrl ) ;
2017-04-23 18:30:08 +03:00
2017-06-16 09:40:54 +03:00
/* Remove core ctrl ref. */
nvme_put_ctrl ( & ctrl - > ctrl ) ;
2017-04-23 18:30:08 +03:00
/* as we're past the point where we transition to the ref
* counting teardown path , if we return a bad pointer here ,
* the calling routine , thinking it ' s prior to the
* transition , will do an rport put . Since the teardown
* path also does a rport put , we do an extra get here to
* so proper order / teardown happens .
*/
nvme_fc_rport_get ( rport ) ;
if ( ret > 0 )
ret = - EIO ;
return ERR_PTR ( ret ) ;
2016-12-02 11:28:42 +03:00
}
2017-05-16 03:10:22 +03:00
kref_get ( & ctrl - > ctrl . kref ) ;
2017-04-23 18:30:08 +03:00
dev_info ( ctrl - > ctrl . device ,
" NVME-FC{%d}: new ctrl: NQN \" %s \" \n " ,
ctrl - > cnum , ctrl - > ctrl . opts - > subsysnqn ) ;
2016-12-02 11:28:42 +03:00
2017-04-23 18:30:08 +03:00
return & ctrl - > ctrl ;
2016-12-02 11:28:42 +03:00
2017-04-23 18:30:08 +03:00
out_cleanup_admin_q :
blk_cleanup_queue ( ctrl - > ctrl . admin_q ) ;
out_free_admin_tag_set :
blk_mq_free_tag_set ( & ctrl - > admin_tag_set ) ;
out_free_queues :
kfree ( ctrl - > queues ) ;
2016-12-02 11:28:42 +03:00
out_free_ida :
2017-04-23 18:30:08 +03:00
put_device ( ctrl - > dev ) ;
2016-12-02 11:28:42 +03:00
ida_simple_remove ( & nvme_fc_ctrl_cnt , ctrl - > cnum ) ;
out_free_ctrl :
kfree ( ctrl ) ;
out_fail :
/* exit via here doesn't follow ctlr ref points */
return ERR_PTR ( ret ) ;
}
struct nvmet_fc_traddr {
u64 nn ;
u64 pn ;
} ;
static int
2017-07-17 23:59:39 +03:00
__nvme_fc_parse_u64 ( substring_t * sstr , u64 * val )
2016-12-02 11:28:42 +03:00
{
u64 token64 ;
2017-07-17 23:59:39 +03:00
if ( match_u64 ( sstr , & token64 ) )
return - EINVAL ;
* val = token64 ;
2016-12-02 11:28:42 +03:00
2017-07-17 23:59:39 +03:00
return 0 ;
}
2016-12-02 11:28:42 +03:00
2017-07-17 23:59:39 +03:00
/*
* This routine validates and extracts the WWN ' s from the TRADDR string .
* As kernel parsers need the 0 x to determine number base , universally
* build string to parse with 0 x prefix before parsing name strings .
*/
static int
nvme_fc_parse_traddr ( struct nvmet_fc_traddr * traddr , char * buf , size_t blen )
{
char name [ 2 + NVME_FC_TRADDR_HEXNAMELEN + 1 ] ;
substring_t wwn = { name , & name [ sizeof ( name ) - 1 ] } ;
int nnoffset , pnoffset ;
/* validate it string one of the 2 allowed formats */
if ( strnlen ( buf , blen ) = = NVME_FC_TRADDR_MAXLENGTH & &
! strncmp ( buf , " nn-0x " , NVME_FC_TRADDR_OXNNLEN ) & &
! strncmp ( & buf [ NVME_FC_TRADDR_MAX_PN_OFFSET ] ,
" pn-0x " , NVME_FC_TRADDR_OXNNLEN ) ) {
nnoffset = NVME_FC_TRADDR_OXNNLEN ;
pnoffset = NVME_FC_TRADDR_MAX_PN_OFFSET +
NVME_FC_TRADDR_OXNNLEN ;
} else if ( ( strnlen ( buf , blen ) = = NVME_FC_TRADDR_MINLENGTH & &
! strncmp ( buf , " nn- " , NVME_FC_TRADDR_NNLEN ) & &
! strncmp ( & buf [ NVME_FC_TRADDR_MIN_PN_OFFSET ] ,
" pn- " , NVME_FC_TRADDR_NNLEN ) ) ) {
nnoffset = NVME_FC_TRADDR_NNLEN ;
pnoffset = NVME_FC_TRADDR_MIN_PN_OFFSET + NVME_FC_TRADDR_NNLEN ;
} else
goto out_einval ;
2016-12-02 11:28:42 +03:00
2017-07-17 23:59:39 +03:00
name [ 0 ] = ' 0 ' ;
name [ 1 ] = ' x ' ;
name [ 2 + NVME_FC_TRADDR_HEXNAMELEN ] = 0 ;
memcpy ( & name [ 2 ] , & buf [ nnoffset ] , NVME_FC_TRADDR_HEXNAMELEN ) ;
if ( __nvme_fc_parse_u64 ( & wwn , & traddr - > nn ) )
goto out_einval ;
memcpy ( & name [ 2 ] , & buf [ pnoffset ] , NVME_FC_TRADDR_HEXNAMELEN ) ;
if ( __nvme_fc_parse_u64 ( & wwn , & traddr - > pn ) )
goto out_einval ;
return 0 ;
out_einval :
pr_warn ( " %s: bad traddr string \n " , __func__ ) ;
return - EINVAL ;
2016-12-02 11:28:42 +03:00
}
static struct nvme_ctrl *
nvme_fc_create_ctrl ( struct device * dev , struct nvmf_ctrl_options * opts )
{
struct nvme_fc_lport * lport ;
struct nvme_fc_rport * rport ;
2017-04-23 18:30:08 +03:00
struct nvme_ctrl * ctrl ;
2016-12-02 11:28:42 +03:00
struct nvmet_fc_traddr laddr = { 0L , 0L } ;
struct nvmet_fc_traddr raddr = { 0L , 0L } ;
unsigned long flags ;
int ret ;
2017-07-17 23:59:39 +03:00
ret = nvme_fc_parse_traddr ( & raddr , opts - > traddr , NVMF_TRADDR_SIZE ) ;
2016-12-02 11:28:42 +03:00
if ( ret | | ! raddr . nn | | ! raddr . pn )
return ERR_PTR ( - EINVAL ) ;
2017-07-17 23:59:39 +03:00
ret = nvme_fc_parse_traddr ( & laddr , opts - > host_traddr , NVMF_TRADDR_SIZE ) ;
2016-12-02 11:28:42 +03:00
if ( ret | | ! laddr . nn | | ! laddr . pn )
return ERR_PTR ( - EINVAL ) ;
/* find the host and remote ports to connect together */
spin_lock_irqsave ( & nvme_fc_lock , flags ) ;
list_for_each_entry ( lport , & nvme_fc_lport_list , port_list ) {
if ( lport - > localport . node_name ! = laddr . nn | |
lport - > localport . port_name ! = laddr . pn )
continue ;
list_for_each_entry ( rport , & lport - > endp_list , endp_list ) {
if ( rport - > remoteport . node_name ! = raddr . nn | |
rport - > remoteport . port_name ! = raddr . pn )
continue ;
/* if fail to get reference fall through. Will error */
if ( ! nvme_fc_rport_get ( rport ) )
break ;
spin_unlock_irqrestore ( & nvme_fc_lock , flags ) ;
2017-04-23 18:30:08 +03:00
ctrl = nvme_fc_init_ctrl ( dev , opts , lport , rport ) ;
if ( IS_ERR ( ctrl ) )
nvme_fc_rport_put ( rport ) ;
return ctrl ;
2016-12-02 11:28:42 +03:00
}
}
spin_unlock_irqrestore ( & nvme_fc_lock , flags ) ;
return ERR_PTR ( - ENOENT ) ;
}
static struct nvmf_transport_ops nvme_fc_transport = {
. name = " fc " ,
. required_opts = NVMF_OPT_TRADDR | NVMF_OPT_HOST_TRADDR ,
2017-05-16 03:10:16 +03:00
. allowed_opts = NVMF_OPT_RECONNECT_DELAY | NVMF_OPT_CTRL_LOSS_TMO ,
2016-12-02 11:28:42 +03:00
. create_ctrl = nvme_fc_create_ctrl ,
} ;
static int __init nvme_fc_init_module ( void )
{
2017-06-07 21:31:55 +03:00
return nvmf_register_transport ( & nvme_fc_transport ) ;
2016-12-02 11:28:42 +03:00
}
static void __exit nvme_fc_exit_module ( void )
{
/* sanity check - all lports should be removed */
if ( ! list_empty ( & nvme_fc_lport_list ) )
pr_warn ( " %s: localport list not empty \n " , __func__ ) ;
nvmf_unregister_transport ( & nvme_fc_transport ) ;
ida_destroy ( & nvme_fc_local_port_cnt ) ;
ida_destroy ( & nvme_fc_ctrl_cnt ) ;
}
module_init ( nvme_fc_init_module ) ;
module_exit ( nvme_fc_exit_module ) ;
MODULE_LICENSE ( " GPL v2 " ) ;