2013-09-10 03:38:16 +00:00
/*
* Copyright ( c ) 2013 , Cisco Systems , Inc . All rights reserved .
*
2015-09-30 13:34:00 -07:00
* This software is available to you under a choice of one of two
* licenses . You may choose to be licensed under the terms of the GNU
* General Public License ( GPL ) Version 2 , available from the file
* COPYING in the main directory of this source tree , or the
* BSD license below :
*
* Redistribution and use in source and binary forms , with or
* without modification , are permitted provided that the following
* conditions are met :
*
* - Redistributions of source code must retain the above
* copyright notice , this list of conditions and the following
* disclaimer .
*
* - Redistributions in binary form must reproduce the above
* copyright notice , this list of conditions and the following
* disclaimer in the documentation and / or other materials
* provided with the distribution .
2013-09-10 03:38:16 +00:00
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND ,
* EXPRESS OR IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY , FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT . IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER LIABILITY , WHETHER IN AN
* ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING FROM , OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE .
*
*/
2014-01-09 14:48:09 -08:00
# include <linux/bug.h>
2013-09-10 03:38:16 +00:00
# include <linux/errno.h>
# include <linux/module.h>
# include <linux/spinlock.h>
# include "usnic_log.h"
# include "usnic_vnic.h"
# include "usnic_fwd.h"
# include "usnic_uiom.h"
2014-01-09 15:40:58 -08:00
# include "usnic_debugfs.h"
2013-09-10 03:38:16 +00:00
# include "usnic_ib_qp_grp.h"
# include "usnic_ib_sysfs.h"
# include "usnic_transport.h"
2014-01-09 14:48:09 -08:00
# define DFLT_RQ_IDX 0
2013-09-10 03:38:16 +00:00
const char * usnic_ib_qp_grp_state_to_string ( enum ib_qp_state state )
{
switch ( state ) {
case IB_QPS_RESET :
return " Rst " ;
case IB_QPS_INIT :
return " Init " ;
case IB_QPS_RTR :
return " RTR " ;
case IB_QPS_RTS :
return " RTS " ;
case IB_QPS_SQD :
return " SQD " ;
case IB_QPS_SQE :
return " SQE " ;
case IB_QPS_ERR :
return " ERR " ;
default :
2015-12-13 15:26:11 +09:00
return " UNKNOWN STATE " ;
2013-09-10 03:38:16 +00:00
}
}
int usnic_ib_qp_grp_dump_hdr ( char * buf , int buf_sz )
{
return scnprintf ( buf , buf_sz , " |QPN \t |State \t |PID \t |VF Idx \t |Fil ID " ) ;
}
int usnic_ib_qp_grp_dump_rows ( void * obj , char * buf , int buf_sz )
{
struct usnic_ib_qp_grp * qp_grp = obj ;
2014-01-09 14:48:09 -08:00
struct usnic_ib_qp_grp_flow * default_flow ;
2013-09-10 03:38:16 +00:00
if ( obj ) {
2014-01-09 14:48:09 -08:00
default_flow = list_first_entry ( & qp_grp - > flows_lst ,
struct usnic_ib_qp_grp_flow , link ) ;
2013-09-10 03:38:16 +00:00
return scnprintf ( buf , buf_sz , " |%d \t |%s \t |%d \t |%hu \t |%d " ,
qp_grp - > ibqp . qp_num ,
usnic_ib_qp_grp_state_to_string (
qp_grp - > state ) ,
qp_grp - > owner_pid ,
usnic_vnic_get_index ( qp_grp - > vf - > vnic ) ,
2014-01-09 14:48:09 -08:00
default_flow - > flow - > flow_id ) ;
2013-09-10 03:38:16 +00:00
} else {
return scnprintf ( buf , buf_sz , " |N/A \t |N/A \t |N/A \t |N/A \t |N/A " ) ;
}
}
2014-01-09 14:48:09 -08:00
static struct usnic_vnic_res_chunk *
get_qp_res_chunk ( struct usnic_ib_qp_grp * qp_grp )
2013-09-10 03:38:16 +00:00
{
2014-01-09 14:47:33 -08:00
lockdep_assert_held ( & qp_grp - > lock ) ;
2014-01-09 14:48:09 -08:00
/*
* The QP res chunk , used to derive qp indices ,
* are just indices of the RQs
*/
return usnic_ib_qp_grp_get_chunk ( qp_grp , USNIC_VNIC_RES_TYPE_RQ ) ;
2013-09-10 03:38:16 +00:00
}
static int enable_qp_grp ( struct usnic_ib_qp_grp * qp_grp )
{
int status ;
int i , vnic_idx ;
struct usnic_vnic_res_chunk * res_chunk ;
struct usnic_vnic_res * res ;
2014-01-09 14:47:33 -08:00
lockdep_assert_held ( & qp_grp - > lock ) ;
2013-09-10 03:38:16 +00:00
vnic_idx = usnic_vnic_get_index ( qp_grp - > vf - > vnic ) ;
2014-01-09 14:48:09 -08:00
res_chunk = get_qp_res_chunk ( qp_grp ) ;
2016-11-11 20:04:26 +01:00
if ( IS_ERR ( res_chunk ) ) {
2014-01-09 14:48:09 -08:00
usnic_err ( " Unable to get qp res with err %ld \n " ,
PTR_ERR ( res_chunk ) ) ;
2016-11-11 20:04:26 +01:00
return PTR_ERR ( res_chunk ) ;
2013-09-10 03:38:16 +00:00
}
for ( i = 0 ; i < res_chunk - > cnt ; i + + ) {
res = res_chunk - > res [ i ] ;
2014-01-09 14:48:09 -08:00
status = usnic_fwd_enable_qp ( qp_grp - > ufdev , vnic_idx ,
2013-09-10 03:38:16 +00:00
res - > vnic_idx ) ;
if ( status ) {
2014-01-09 14:48:09 -08:00
usnic_err ( " Failed to enable qp %d of %s:%d \n with err %d \n " ,
res - > vnic_idx , qp_grp - > ufdev - > name ,
2013-09-10 03:38:16 +00:00
vnic_idx , status ) ;
goto out_err ;
}
}
return 0 ;
out_err :
for ( i - - ; i > = 0 ; i - - ) {
res = res_chunk - > res [ i ] ;
2014-01-09 14:48:09 -08:00
usnic_fwd_disable_qp ( qp_grp - > ufdev , vnic_idx ,
2013-09-10 03:38:16 +00:00
res - > vnic_idx ) ;
}
return status ;
}
static int disable_qp_grp ( struct usnic_ib_qp_grp * qp_grp )
{
int i , vnic_idx ;
struct usnic_vnic_res_chunk * res_chunk ;
struct usnic_vnic_res * res ;
int status = 0 ;
2014-01-09 14:47:33 -08:00
lockdep_assert_held ( & qp_grp - > lock ) ;
2013-09-10 03:38:16 +00:00
vnic_idx = usnic_vnic_get_index ( qp_grp - > vf - > vnic ) ;
2014-01-09 14:48:09 -08:00
res_chunk = get_qp_res_chunk ( qp_grp ) ;
2016-11-11 20:04:26 +01:00
if ( IS_ERR ( res_chunk ) ) {
2014-01-09 14:48:09 -08:00
usnic_err ( " Unable to get qp res with err %ld \n " ,
2013-09-10 03:38:16 +00:00
PTR_ERR ( res_chunk ) ) ;
2016-11-11 20:04:26 +01:00
return PTR_ERR ( res_chunk ) ;
2013-09-10 03:38:16 +00:00
}
for ( i = 0 ; i < res_chunk - > cnt ; i + + ) {
res = res_chunk - > res [ i ] ;
2014-01-09 14:48:09 -08:00
status = usnic_fwd_disable_qp ( qp_grp - > ufdev , vnic_idx ,
2013-09-10 03:38:16 +00:00
res - > vnic_idx ) ;
if ( status ) {
usnic_err ( " Failed to disable rq %d of %s:%d \n with err %d \n " ,
res - > vnic_idx ,
2014-01-09 14:48:09 -08:00
qp_grp - > ufdev - > name ,
2013-09-10 03:38:16 +00:00
vnic_idx , status ) ;
}
}
return status ;
}
2014-01-09 14:48:09 -08:00
static int init_filter_action ( struct usnic_ib_qp_grp * qp_grp ,
struct usnic_filter_action * uaction )
{
struct usnic_vnic_res_chunk * res_chunk ;
res_chunk = usnic_ib_qp_grp_get_chunk ( qp_grp , USNIC_VNIC_RES_TYPE_RQ ) ;
2016-11-11 20:04:26 +01:00
if ( IS_ERR ( res_chunk ) ) {
2014-01-09 14:48:09 -08:00
usnic_err ( " Unable to get %s with err %ld \n " ,
usnic_vnic_res_type_to_str ( USNIC_VNIC_RES_TYPE_RQ ) ,
PTR_ERR ( res_chunk ) ) ;
2016-11-11 20:04:26 +01:00
return PTR_ERR ( res_chunk ) ;
2014-01-09 14:48:09 -08:00
}
uaction - > vnic_idx = usnic_vnic_get_index ( qp_grp - > vf - > vnic ) ;
uaction - > action . type = FILTER_ACTION_RQ_STEERING ;
uaction - > action . u . rq_idx = res_chunk - > res [ DFLT_RQ_IDX ] - > vnic_idx ;
return 0 ;
}
static struct usnic_ib_qp_grp_flow *
create_roce_custom_flow ( struct usnic_ib_qp_grp * qp_grp ,
struct usnic_transport_spec * trans_spec )
{
uint16_t port_num ;
int err ;
struct filter filter ;
struct usnic_filter_action uaction ;
struct usnic_ib_qp_grp_flow * qp_flow ;
struct usnic_fwd_flow * flow ;
enum usnic_transport_type trans_type ;
trans_type = trans_spec - > trans_type ;
port_num = trans_spec - > usnic_roce . port_num ;
/* Reserve Port */
port_num = usnic_transport_rsrv_port ( trans_type , port_num ) ;
if ( port_num = = 0 )
return ERR_PTR ( - EINVAL ) ;
/* Create Flow */
usnic_fwd_init_usnic_filter ( & filter , port_num ) ;
err = init_filter_action ( qp_grp , & uaction ) ;
if ( err )
goto out_unreserve_port ;
flow = usnic_fwd_alloc_flow ( qp_grp - > ufdev , & filter , & uaction ) ;
if ( IS_ERR_OR_NULL ( flow ) ) {
2014-01-09 14:48:36 -08:00
err = flow ? PTR_ERR ( flow ) : - EFAULT ;
2014-01-09 14:48:09 -08:00
goto out_unreserve_port ;
}
/* Create Flow Handle */
qp_flow = kzalloc ( sizeof ( * qp_flow ) , GFP_ATOMIC ) ;
2015-10-19 16:57:10 +00:00
if ( ! qp_flow ) {
err = - ENOMEM ;
2014-01-09 14:48:09 -08:00
goto out_dealloc_flow ;
}
qp_flow - > flow = flow ;
qp_flow - > trans_type = trans_type ;
qp_flow - > usnic_roce . port_num = port_num ;
qp_flow - > qp_grp = qp_grp ;
return qp_flow ;
out_dealloc_flow :
usnic_fwd_dealloc_flow ( flow ) ;
out_unreserve_port :
usnic_transport_unrsrv_port ( trans_type , port_num ) ;
return ERR_PTR ( err ) ;
}
static void release_roce_custom_flow ( struct usnic_ib_qp_grp_flow * qp_flow )
{
usnic_fwd_dealloc_flow ( qp_flow - > flow ) ;
usnic_transport_unrsrv_port ( qp_flow - > trans_type ,
qp_flow - > usnic_roce . port_num ) ;
kfree ( qp_flow ) ;
}
2014-01-09 14:48:18 -08:00
static struct usnic_ib_qp_grp_flow *
create_udp_flow ( struct usnic_ib_qp_grp * qp_grp ,
struct usnic_transport_spec * trans_spec )
{
struct socket * sock ;
int sock_fd ;
int err ;
struct filter filter ;
struct usnic_filter_action uaction ;
struct usnic_ib_qp_grp_flow * qp_flow ;
struct usnic_fwd_flow * flow ;
enum usnic_transport_type trans_type ;
uint32_t addr ;
uint16_t port_num ;
int proto ;
trans_type = trans_spec - > trans_type ;
sock_fd = trans_spec - > udp . sock_fd ;
/* Get and check socket */
sock = usnic_transport_get_socket ( sock_fd ) ;
if ( IS_ERR_OR_NULL ( sock ) )
return ERR_CAST ( sock ) ;
err = usnic_transport_sock_get_addr ( sock , & proto , & addr , & port_num ) ;
if ( err )
goto out_put_sock ;
if ( proto ! = IPPROTO_UDP ) {
usnic_err ( " Protocol for fd %d is not UDP " , sock_fd ) ;
err = - EPERM ;
goto out_put_sock ;
}
/* Create flow */
usnic_fwd_init_udp_filter ( & filter , addr , port_num ) ;
err = init_filter_action ( qp_grp , & uaction ) ;
if ( err )
goto out_put_sock ;
flow = usnic_fwd_alloc_flow ( qp_grp - > ufdev , & filter , & uaction ) ;
if ( IS_ERR_OR_NULL ( flow ) ) {
2014-01-09 14:48:36 -08:00
err = flow ? PTR_ERR ( flow ) : - EFAULT ;
2014-01-09 14:48:18 -08:00
goto out_put_sock ;
}
/* Create qp_flow */
qp_flow = kzalloc ( sizeof ( * qp_flow ) , GFP_ATOMIC ) ;
2015-10-19 16:57:10 +00:00
if ( ! qp_flow ) {
err = - ENOMEM ;
2014-01-09 14:48:18 -08:00
goto out_dealloc_flow ;
}
qp_flow - > flow = flow ;
qp_flow - > trans_type = trans_type ;
qp_flow - > udp . sock = sock ;
qp_flow - > qp_grp = qp_grp ;
return qp_flow ;
out_dealloc_flow :
usnic_fwd_dealloc_flow ( flow ) ;
out_put_sock :
usnic_transport_put_socket ( sock ) ;
return ERR_PTR ( err ) ;
}
static void release_udp_flow ( struct usnic_ib_qp_grp_flow * qp_flow )
{
usnic_fwd_dealloc_flow ( qp_flow - > flow ) ;
usnic_transport_put_socket ( qp_flow - > udp . sock ) ;
kfree ( qp_flow ) ;
}
2014-01-09 14:48:09 -08:00
static struct usnic_ib_qp_grp_flow *
create_and_add_flow ( struct usnic_ib_qp_grp * qp_grp ,
struct usnic_transport_spec * trans_spec )
{
struct usnic_ib_qp_grp_flow * qp_flow ;
enum usnic_transport_type trans_type ;
trans_type = trans_spec - > trans_type ;
switch ( trans_type ) {
case USNIC_TRANSPORT_ROCE_CUSTOM :
qp_flow = create_roce_custom_flow ( qp_grp , trans_spec ) ;
break ;
2014-01-09 14:48:18 -08:00
case USNIC_TRANSPORT_IPV4_UDP :
qp_flow = create_udp_flow ( qp_grp , trans_spec ) ;
break ;
2014-01-09 14:48:09 -08:00
default :
usnic_err ( " Unsupported transport %u \n " ,
trans_spec - > trans_type ) ;
return ERR_PTR ( - EINVAL ) ;
}
2014-01-09 15:40:58 -08:00
if ( ! IS_ERR_OR_NULL ( qp_flow ) ) {
2014-01-09 14:48:09 -08:00
list_add_tail ( & qp_flow - > link , & qp_grp - > flows_lst ) ;
2014-01-09 15:40:58 -08:00
usnic_debugfs_flow_add ( qp_flow ) ;
}
2014-01-09 14:48:09 -08:00
return qp_flow ;
}
static void release_and_remove_flow ( struct usnic_ib_qp_grp_flow * qp_flow )
{
2014-01-09 15:40:58 -08:00
usnic_debugfs_flow_remove ( qp_flow ) ;
2014-01-09 14:48:09 -08:00
list_del ( & qp_flow - > link ) ;
switch ( qp_flow - > trans_type ) {
case USNIC_TRANSPORT_ROCE_CUSTOM :
release_roce_custom_flow ( qp_flow ) ;
break ;
2014-01-09 14:48:18 -08:00
case USNIC_TRANSPORT_IPV4_UDP :
release_udp_flow ( qp_flow ) ;
break ;
2014-01-09 14:48:09 -08:00
default :
WARN ( 1 , " Unsupported transport %u \n " ,
qp_flow - > trans_type ) ;
break ;
}
}
static void release_and_remove_all_flows ( struct usnic_ib_qp_grp * qp_grp )
{
struct usnic_ib_qp_grp_flow * qp_flow , * tmp ;
list_for_each_entry_safe ( qp_flow , tmp , & qp_grp - > flows_lst , link )
release_and_remove_flow ( qp_flow ) ;
}
2013-09-10 03:38:16 +00:00
int usnic_ib_qp_grp_modify ( struct usnic_ib_qp_grp * qp_grp ,
enum ib_qp_state new_state ,
2014-01-09 14:48:09 -08:00
void * data )
2013-09-10 03:38:16 +00:00
{
int status = 0 ;
struct ib_event ib_event ;
enum ib_qp_state old_state ;
2014-01-09 14:48:09 -08:00
struct usnic_transport_spec * trans_spec ;
struct usnic_ib_qp_grp_flow * qp_flow ;
2013-09-10 03:38:16 +00:00
old_state = qp_grp - > state ;
2014-01-09 14:48:09 -08:00
trans_spec = ( struct usnic_transport_spec * ) data ;
2013-09-10 03:38:16 +00:00
spin_lock ( & qp_grp - > lock ) ;
switch ( new_state ) {
case IB_QPS_RESET :
switch ( old_state ) {
case IB_QPS_RESET :
/* NO-OP */
break ;
case IB_QPS_INIT :
2014-01-09 14:48:09 -08:00
release_and_remove_all_flows ( qp_grp ) ;
status = 0 ;
2013-09-10 03:38:16 +00:00
break ;
case IB_QPS_RTR :
case IB_QPS_RTS :
case IB_QPS_ERR :
status = disable_qp_grp ( qp_grp ) ;
2014-01-09 14:48:09 -08:00
release_and_remove_all_flows ( qp_grp ) ;
2013-09-10 03:38:16 +00:00
break ;
default :
status = - EINVAL ;
}
break ;
case IB_QPS_INIT :
switch ( old_state ) {
case IB_QPS_RESET :
2014-01-09 14:48:09 -08:00
if ( trans_spec ) {
qp_flow = create_and_add_flow ( qp_grp ,
trans_spec ) ;
if ( IS_ERR_OR_NULL ( qp_flow ) ) {
2014-01-09 14:48:36 -08:00
status = qp_flow ? PTR_ERR ( qp_flow ) : - EFAULT ;
2014-01-09 14:48:09 -08:00
break ;
}
} else {
/*
* Optional to specify filters .
*/
status = 0 ;
}
2013-09-10 03:38:16 +00:00
break ;
case IB_QPS_INIT :
2014-01-09 14:48:09 -08:00
if ( trans_spec ) {
qp_flow = create_and_add_flow ( qp_grp ,
trans_spec ) ;
if ( IS_ERR_OR_NULL ( qp_flow ) ) {
2014-01-09 14:48:36 -08:00
status = qp_flow ? PTR_ERR ( qp_flow ) : - EFAULT ;
2014-01-09 14:48:09 -08:00
break ;
}
} else {
/*
* Doesn ' t make sense to go into INIT state
* from INIT state w / o adding filters .
*/
status = - EINVAL ;
}
2013-09-10 03:38:16 +00:00
break ;
case IB_QPS_RTR :
status = disable_qp_grp ( qp_grp ) ;
break ;
case IB_QPS_RTS :
status = disable_qp_grp ( qp_grp ) ;
break ;
default :
status = - EINVAL ;
}
break ;
case IB_QPS_RTR :
switch ( old_state ) {
case IB_QPS_INIT :
status = enable_qp_grp ( qp_grp ) ;
break ;
default :
status = - EINVAL ;
}
break ;
case IB_QPS_RTS :
switch ( old_state ) {
case IB_QPS_RTR :
/* NO-OP FOR NOW */
break ;
default :
status = - EINVAL ;
}
break ;
case IB_QPS_ERR :
ib_event . device = & qp_grp - > vf - > pf - > ib_dev ;
ib_event . element . qp = & qp_grp - > ibqp ;
ib_event . event = IB_EVENT_QP_FATAL ;
switch ( old_state ) {
case IB_QPS_RESET :
qp_grp - > ibqp . event_handler ( & ib_event ,
qp_grp - > ibqp . qp_context ) ;
break ;
case IB_QPS_INIT :
2014-01-09 14:48:09 -08:00
release_and_remove_all_flows ( qp_grp ) ;
2013-09-10 03:38:16 +00:00
qp_grp - > ibqp . event_handler ( & ib_event ,
qp_grp - > ibqp . qp_context ) ;
break ;
case IB_QPS_RTR :
case IB_QPS_RTS :
status = disable_qp_grp ( qp_grp ) ;
2014-01-09 14:48:09 -08:00
release_and_remove_all_flows ( qp_grp ) ;
2013-09-10 03:38:16 +00:00
qp_grp - > ibqp . event_handler ( & ib_event ,
qp_grp - > ibqp . qp_context ) ;
break ;
default :
status = - EINVAL ;
}
break ;
default :
status = - EINVAL ;
}
spin_unlock ( & qp_grp - > lock ) ;
if ( ! status ) {
qp_grp - > state = new_state ;
2015-12-09 10:42:16 -08:00
usnic_info ( " Transitioned %u from %s to %s " ,
2013-09-10 03:38:16 +00:00
qp_grp - > grp_id ,
usnic_ib_qp_grp_state_to_string ( old_state ) ,
usnic_ib_qp_grp_state_to_string ( new_state ) ) ;
} else {
2014-08-06 22:31:28 +09:00
usnic_err ( " Failed to transition %u from %s to %s " ,
2013-09-10 03:38:16 +00:00
qp_grp - > grp_id ,
usnic_ib_qp_grp_state_to_string ( old_state ) ,
usnic_ib_qp_grp_state_to_string ( new_state ) ) ;
}
return status ;
}
static struct usnic_vnic_res_chunk * *
alloc_res_chunk_list ( struct usnic_vnic * vnic ,
struct usnic_vnic_res_spec * res_spec , void * owner_obj )
{
enum usnic_vnic_res_type res_type ;
struct usnic_vnic_res_chunk * * res_chunk_list ;
int err , i , res_cnt , res_lst_sz ;
for ( res_lst_sz = 0 ;
res_spec - > resources [ res_lst_sz ] . type ! = USNIC_VNIC_RES_TYPE_EOL ;
res_lst_sz + + ) {
/* Do Nothing */
}
res_chunk_list = kzalloc ( sizeof ( * res_chunk_list ) * ( res_lst_sz + 1 ) ,
GFP_ATOMIC ) ;
if ( ! res_chunk_list )
return ERR_PTR ( - ENOMEM ) ;
for ( i = 0 ; res_spec - > resources [ i ] . type ! = USNIC_VNIC_RES_TYPE_EOL ;
i + + ) {
res_type = res_spec - > resources [ i ] . type ;
res_cnt = res_spec - > resources [ i ] . cnt ;
res_chunk_list [ i ] = usnic_vnic_get_resources ( vnic , res_type ,
res_cnt , owner_obj ) ;
if ( IS_ERR_OR_NULL ( res_chunk_list [ i ] ) ) {
2014-01-09 14:48:36 -08:00
err = res_chunk_list [ i ] ?
PTR_ERR ( res_chunk_list [ i ] ) : - ENOMEM ;
2013-09-10 03:38:16 +00:00
usnic_err ( " Failed to get %s from %s with err %d \n " ,
usnic_vnic_res_type_to_str ( res_type ) ,
usnic_vnic_pci_name ( vnic ) ,
err ) ;
goto out_free_res ;
}
}
return res_chunk_list ;
out_free_res :
2015-12-09 10:42:18 -08:00
for ( i - - ; i > = 0 ; i - - )
2013-09-10 03:38:16 +00:00
usnic_vnic_put_resources ( res_chunk_list [ i ] ) ;
kfree ( res_chunk_list ) ;
return ERR_PTR ( err ) ;
}
static void free_qp_grp_res ( struct usnic_vnic_res_chunk * * res_chunk_list )
{
int i ;
for ( i = 0 ; res_chunk_list [ i ] ; i + + )
usnic_vnic_put_resources ( res_chunk_list [ i ] ) ;
kfree ( res_chunk_list ) ;
}
static int qp_grp_and_vf_bind ( struct usnic_ib_vf * vf ,
struct usnic_ib_pd * pd ,
struct usnic_ib_qp_grp * qp_grp )
{
int err ;
struct pci_dev * pdev ;
2014-01-09 14:47:33 -08:00
lockdep_assert_held ( & vf - > lock ) ;
2013-09-10 03:38:16 +00:00
pdev = usnic_vnic_get_pdev ( vf - > vnic ) ;
if ( vf - > qp_grp_ref_cnt = = 0 ) {
err = usnic_uiom_attach_dev_to_pd ( pd - > umem_pd , & pdev - > dev ) ;
if ( err ) {
usnic_err ( " Failed to attach %s to domain \n " ,
pci_name ( pdev ) ) ;
return err ;
}
vf - > pd = pd ;
}
vf - > qp_grp_ref_cnt + + ;
WARN_ON ( vf - > pd ! = pd ) ;
qp_grp - > vf = vf ;
return 0 ;
}
static void qp_grp_and_vf_unbind ( struct usnic_ib_qp_grp * qp_grp )
{
struct pci_dev * pdev ;
struct usnic_ib_pd * pd ;
2014-01-09 14:47:33 -08:00
lockdep_assert_held ( & qp_grp - > vf - > lock ) ;
2013-09-10 03:38:16 +00:00
pd = qp_grp - > vf - > pd ;
pdev = usnic_vnic_get_pdev ( qp_grp - > vf - > vnic ) ;
if ( - - qp_grp - > vf - > qp_grp_ref_cnt = = 0 ) {
qp_grp - > vf - > pd = NULL ;
usnic_uiom_detach_dev_from_pd ( pd - > umem_pd , & pdev - > dev ) ;
}
qp_grp - > vf = NULL ;
}
static void log_spec ( struct usnic_vnic_res_spec * res_spec )
{
char buf [ 512 ] ;
usnic_vnic_spec_dump ( buf , sizeof ( buf ) , res_spec ) ;
usnic_dbg ( " %s \n " , buf ) ;
}
2014-01-09 14:48:09 -08:00
static int qp_grp_id_from_flow ( struct usnic_ib_qp_grp_flow * qp_flow ,
uint32_t * id )
{
enum usnic_transport_type trans_type = qp_flow - > trans_type ;
2014-01-09 14:48:18 -08:00
int err ;
2014-01-23 22:38:04 +00:00
uint16_t port_num = 0 ;
2014-01-09 14:48:09 -08:00
switch ( trans_type ) {
case USNIC_TRANSPORT_ROCE_CUSTOM :
* id = qp_flow - > usnic_roce . port_num ;
break ;
2014-01-09 14:48:18 -08:00
case USNIC_TRANSPORT_IPV4_UDP :
err = usnic_transport_sock_get_addr ( qp_flow - > udp . sock ,
NULL , NULL ,
2014-01-23 22:38:04 +00:00
& port_num ) ;
2014-01-09 14:48:18 -08:00
if ( err )
return err ;
2014-01-23 22:38:04 +00:00
/*
* Copy port_num to stack first and then to * id ,
* so that the short to int cast works for little
* and big endian systems .
*/
* id = port_num ;
2014-01-09 14:48:18 -08:00
break ;
2014-01-09 14:48:09 -08:00
default :
usnic_err ( " Unsupported transport %u \n " , trans_type ) ;
return - EINVAL ;
}
return 0 ;
}
2013-09-10 03:38:16 +00:00
struct usnic_ib_qp_grp *
2014-01-09 14:48:09 -08:00
usnic_ib_qp_grp_create ( struct usnic_fwd_dev * ufdev , struct usnic_ib_vf * vf ,
2013-09-10 03:38:16 +00:00
struct usnic_ib_pd * pd ,
struct usnic_vnic_res_spec * res_spec ,
2014-01-09 14:48:09 -08:00
struct usnic_transport_spec * transport_spec )
2013-09-10 03:38:16 +00:00
{
struct usnic_ib_qp_grp * qp_grp ;
int err ;
2014-01-09 14:48:09 -08:00
enum usnic_transport_type transport = transport_spec - > trans_type ;
struct usnic_ib_qp_grp_flow * qp_flow ;
2013-09-10 03:38:16 +00:00
2014-01-09 14:47:33 -08:00
lockdep_assert_held ( & vf - > lock ) ;
2013-09-10 03:38:16 +00:00
err = usnic_vnic_res_spec_satisfied ( & min_transport_spec [ transport ] ,
res_spec ) ;
if ( err ) {
usnic_err ( " Spec does not meet miniumum req for transport %d \n " ,
transport ) ;
log_spec ( res_spec ) ;
return ERR_PTR ( err ) ;
}
qp_grp = kzalloc ( sizeof ( * qp_grp ) , GFP_ATOMIC ) ;
2016-11-03 16:44:21 +02:00
if ( ! qp_grp )
2013-09-10 03:38:16 +00:00
return NULL ;
qp_grp - > res_chunk_list = alloc_res_chunk_list ( vf - > vnic , res_spec ,
qp_grp ) ;
if ( IS_ERR_OR_NULL ( qp_grp - > res_chunk_list ) ) {
err = qp_grp - > res_chunk_list ?
PTR_ERR ( qp_grp - > res_chunk_list ) : - ENOMEM ;
2014-01-09 14:48:09 -08:00
goto out_free_qp_grp ;
2013-09-10 03:38:16 +00:00
}
2014-01-09 14:48:09 -08:00
err = qp_grp_and_vf_bind ( vf , pd , qp_grp ) ;
if ( err )
goto out_free_res ;
INIT_LIST_HEAD ( & qp_grp - > flows_lst ) ;
2013-09-10 03:38:16 +00:00
spin_lock_init ( & qp_grp - > lock ) ;
qp_grp - > ufdev = ufdev ;
qp_grp - > state = IB_QPS_RESET ;
qp_grp - > owner_pid = current - > pid ;
2014-01-09 14:48:09 -08:00
qp_flow = create_and_add_flow ( qp_grp , transport_spec ) ;
if ( IS_ERR_OR_NULL ( qp_flow ) ) {
usnic_err ( " Unable to create and add flow with err %ld \n " ,
PTR_ERR ( qp_flow ) ) ;
2014-01-09 14:48:36 -08:00
err = qp_flow ? PTR_ERR ( qp_flow ) : - EFAULT ;
2014-01-09 14:48:09 -08:00
goto out_qp_grp_vf_unbind ;
}
2013-09-10 03:38:16 +00:00
2014-01-09 14:48:09 -08:00
err = qp_grp_id_from_flow ( qp_flow , & qp_grp - > grp_id ) ;
2013-09-10 03:38:16 +00:00
if ( err )
2014-01-09 14:48:09 -08:00
goto out_release_flow ;
qp_grp - > ibqp . qp_num = qp_grp - > grp_id ;
2013-09-10 03:38:16 +00:00
usnic_ib_sysfs_qpn_add ( qp_grp ) ;
return qp_grp ;
2014-01-09 14:48:09 -08:00
out_release_flow :
release_and_remove_flow ( qp_flow ) ;
out_qp_grp_vf_unbind :
qp_grp_and_vf_unbind ( qp_grp ) ;
out_free_res :
free_qp_grp_res ( qp_grp - > res_chunk_list ) ;
out_free_qp_grp :
2013-09-10 03:38:16 +00:00
kfree ( qp_grp ) ;
return ERR_PTR ( err ) ;
}
void usnic_ib_qp_grp_destroy ( struct usnic_ib_qp_grp * qp_grp )
{
WARN_ON ( qp_grp - > state ! = IB_QPS_RESET ) ;
2014-01-09 14:47:33 -08:00
lockdep_assert_held ( & qp_grp - > vf - > lock ) ;
2013-09-10 03:38:16 +00:00
2014-01-09 15:40:58 -08:00
release_and_remove_all_flows ( qp_grp ) ;
2013-09-10 03:38:16 +00:00
usnic_ib_sysfs_qpn_remove ( qp_grp ) ;
qp_grp_and_vf_unbind ( qp_grp ) ;
free_qp_grp_res ( qp_grp - > res_chunk_list ) ;
kfree ( qp_grp ) ;
}
struct usnic_vnic_res_chunk *
usnic_ib_qp_grp_get_chunk ( struct usnic_ib_qp_grp * qp_grp ,
enum usnic_vnic_res_type res_type )
{
int i ;
for ( i = 0 ; qp_grp - > res_chunk_list [ i ] ; i + + ) {
if ( qp_grp - > res_chunk_list [ i ] - > type = = res_type )
return qp_grp - > res_chunk_list [ i ] ;
}
return ERR_PTR ( - EINVAL ) ;
}