2016-04-04 16:00:37 +03:00
/* Client connection-specific management code.
*
* Copyright ( C ) 2016 Red Hat , Inc . All Rights Reserved .
* Written by David Howells ( dhowells @ redhat . com )
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public Licence
* as published by the Free Software Foundation ; either version
* 2 of the Licence , or ( at your option ) any later version .
*/
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
# include <linux/slab.h>
# include <linux/idr.h>
# include <linux/timer.h>
# include "ar-internal.h"
/*
* We use machine - unique IDs for our client connections .
*/
DEFINE_IDR ( rxrpc_client_conn_ids ) ;
static DEFINE_SPINLOCK ( rxrpc_conn_id_lock ) ;
/*
* Get a connection ID and epoch for a client connection from the global pool .
* The connection struct pointer is then recorded in the idr radix tree . The
* epoch is changed if this wraps .
*
* TODO : The IDR tree gets very expensive on memory if the connection IDs are
* widely scattered throughout the number space , so we shall need to retire
* connections that have , say , an ID more than four times the maximum number of
* client conns away from the current allocation point to try and keep the IDs
* concentrated . We will also need to retire connections from an old epoch .
*/
2016-06-17 17:42:35 +03:00
int rxrpc_get_client_connection_id ( struct rxrpc_connection * conn , gfp_t gfp )
2016-04-04 16:00:37 +03:00
{
u32 epoch ;
int id ;
_enter ( " " ) ;
idr_preload ( gfp ) ;
spin_lock ( & rxrpc_conn_id_lock ) ;
epoch = rxrpc_epoch ;
/* We could use idr_alloc_cyclic() here, but we really need to know
* when the thing wraps so that we can advance the epoch .
*/
if ( rxrpc_client_conn_ids . cur = = 0 )
rxrpc_client_conn_ids . cur = 1 ;
id = idr_alloc ( & rxrpc_client_conn_ids , conn ,
rxrpc_client_conn_ids . cur , 0x40000000 , GFP_NOWAIT ) ;
if ( id < 0 ) {
if ( id ! = - ENOSPC )
goto error ;
id = idr_alloc ( & rxrpc_client_conn_ids , conn ,
1 , 0x40000000 , GFP_NOWAIT ) ;
if ( id < 0 )
goto error ;
epoch + + ;
rxrpc_epoch = epoch ;
}
rxrpc_client_conn_ids . cur = id + 1 ;
spin_unlock ( & rxrpc_conn_id_lock ) ;
idr_preload_end ( ) ;
conn - > proto . epoch = epoch ;
conn - > proto . cid = id < < RXRPC_CIDSHIFT ;
set_bit ( RXRPC_CONN_HAS_IDR , & conn - > flags ) ;
_leave ( " [CID %x:%x] " , epoch , conn - > proto . cid ) ;
return 0 ;
error :
spin_unlock ( & rxrpc_conn_id_lock ) ;
idr_preload_end ( ) ;
_leave ( " = %d " , id ) ;
return id ;
}
/*
* Release a connection ID for a client connection from the global pool .
*/
void rxrpc_put_client_connection_id ( struct rxrpc_connection * conn )
{
if ( test_bit ( RXRPC_CONN_HAS_IDR , & conn - > flags ) ) {
spin_lock ( & rxrpc_conn_id_lock ) ;
idr_remove ( & rxrpc_client_conn_ids ,
conn - > proto . cid > > RXRPC_CIDSHIFT ) ;
spin_unlock ( & rxrpc_conn_id_lock ) ;
}
}