2019-05-19 15:08:20 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2005-04-17 02:20:36 +04:00
/*
* linux / fs / lockd / svc . c
*
* This is the central lockd service .
*
* FIXME : Separate the lockd NFS server functionality from the lockd NFS
* client functionality . Oh why didn ' t Sun create two separate
* services in the first place ?
*
* Authors : Olaf Kirch ( okir @ monad . swb . de )
*
* Copyright ( C ) 1995 , 1996 Olaf Kirch < okir @ monad . swb . de >
*/
# include <linux/module.h>
# include <linux/init.h>
# include <linux/sysctl.h>
# include <linux/moduleparam.h>
2017-02-08 20:51:30 +03:00
# include <linux/sched/signal.h>
2005-04-17 02:20:36 +04:00
# include <linux/errno.h>
# include <linux/in.h>
# include <linux/uio.h>
# include <linux/smp.h>
2006-03-26 13:37:12 +04:00
# include <linux/mutex.h>
2008-02-08 00:34:55 +03:00
# include <linux/kthread.h>
2007-07-17 15:03:35 +04:00
# include <linux/freezer.h>
2015-12-12 00:46:00 +03:00
# include <linux/inetdevice.h>
2005-04-17 02:20:36 +04:00
# include <linux/sunrpc/types.h>
# include <linux/sunrpc/stats.h>
# include <linux/sunrpc/clnt.h>
# include <linux/sunrpc/svc.h>
# include <linux/sunrpc/svcsock.h>
2015-12-12 00:46:00 +03:00
# include <linux/sunrpc/svc_xprt.h>
2006-10-02 13:17:45 +04:00
# include <net/ip.h>
2015-12-12 00:46:00 +03:00
# include <net/addrconf.h>
# include <net/ipv6.h>
2005-04-17 02:20:36 +04:00
# include <linux/lockd/lockd.h>
# include <linux/nfs.h>
2012-01-31 15:07:57 +04:00
# include "netns.h"
2014-09-13 00:40:20 +04:00
# include "procfs.h"
2012-01-31 15:07:57 +04:00
2005-04-17 02:20:36 +04:00
# define NLMDBG_FACILITY NLMDBG_SVC
# define LOCKD_BUFSIZE (1024 + NLMSVC_XDRSIZE)
# define ALLOWED_SIGS (sigmask(SIGKILL))
static struct svc_program nlmsvc_program ;
2015-12-24 00:25:13 +03:00
const struct nlmsvc_binding * nlmsvc_ops ;
2008-12-23 23:21:33 +03:00
EXPORT_SYMBOL_GPL ( nlmsvc_ops ) ;
2005-04-17 02:20:36 +04:00
2006-03-26 13:37:12 +04:00
static DEFINE_MUTEX ( nlmsvc_mutex ) ;
2005-04-17 02:20:36 +04:00
static unsigned int nlmsvc_users ;
2008-02-08 00:34:55 +03:00
static struct task_struct * nlmsvc_task ;
2008-06-11 18:03:12 +04:00
static struct svc_rqst * nlmsvc_rqst ;
2005-04-17 02:20:36 +04:00
unsigned long nlmsvc_timeout ;
2018-02-07 13:29:47 +03:00
static atomic_t nlm_ntf_refcnt = ATOMIC_INIT ( 0 ) ;
static DECLARE_WAIT_QUEUE_HEAD ( nlm_ntf_wq ) ;
2017-11-10 10:19:26 +03:00
netns: make struct pernet_operations::id unsigned int
Make struct pernet_operations::id unsigned.
There are 2 reasons to do so:
1)
This field is really an index into an zero based array and
thus is unsigned entity. Using negative value is out-of-bound
access by definition.
2)
On x86_64 unsigned 32-bit data which are mixed with pointers
via array indexing or offsets added or subtracted to pointers
are preffered to signed 32-bit data.
"int" being used as an array index needs to be sign-extended
to 64-bit before being used.
void f(long *p, int i)
{
g(p[i]);
}
roughly translates to
movsx rsi, esi
mov rdi, [rsi+...]
call g
MOVSX is 3 byte instruction which isn't necessary if the variable is
unsigned because x86_64 is zero extending by default.
Now, there is net_generic() function which, you guessed it right, uses
"int" as an array index:
static inline void *net_generic(const struct net *net, int id)
{
...
ptr = ng->ptr[id - 1];
...
}
And this function is used a lot, so those sign extensions add up.
Patch snipes ~1730 bytes on allyesconfig kernel (without all junk
messing with code generation):
add/remove: 0/0 grow/shrink: 70/598 up/down: 396/-2126 (-1730)
Unfortunately some functions actually grow bigger.
This is a semmingly random artefact of code generation with register
allocator being used differently. gcc decides that some variable
needs to live in new r8+ registers and every access now requires REX
prefix. Or it is shifted into r12, so [r12+0] addressing mode has to be
used which is longer than [r8]
However, overall balance is in negative direction:
add/remove: 0/0 grow/shrink: 70/598 up/down: 396/-2126 (-1730)
function old new delta
nfsd4_lock 3886 3959 +73
tipc_link_build_proto_msg 1096 1140 +44
mac80211_hwsim_new_radio 2776 2808 +32
tipc_mon_rcv 1032 1058 +26
svcauth_gss_legacy_init 1413 1429 +16
tipc_bcbase_select_primary 379 392 +13
nfsd4_exchange_id 1247 1260 +13
nfsd4_setclientid_confirm 782 793 +11
...
put_client_renew_locked 494 480 -14
ip_set_sockfn_get 730 716 -14
geneve_sock_add 829 813 -16
nfsd4_sequence_done 721 703 -18
nlmclnt_lookup_host 708 686 -22
nfsd4_lockt 1085 1063 -22
nfs_get_client 1077 1050 -27
tcf_bpf_init 1106 1076 -30
nfsd4_encode_fattr 5997 5930 -67
Total: Before=154856051, After=154854321, chg -0.00%
Signed-off-by: Alexey Dobriyan <adobriyan@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-11-17 04:58:21 +03:00
unsigned int lockd_net_id ;
2012-01-31 15:07:57 +04:00
2005-04-17 02:20:36 +04:00
/*
* These can be set at insmod time ( useful for NFS as root filesystem ) ,
* and also changed through the sysctl interface . - - Jamie Lokier , Aug 2003
*/
static unsigned long nlm_grace_period ;
static unsigned long nlm_timeout = LOCKD_DFLT_TIMEO ;
static int nlm_udpport , nlm_tcpport ;
2008-10-20 19:51:58 +04:00
/* RLIM_NOFILE defaults to 1024. That seems like a reasonable default here. */
static unsigned int nlm_max_connections = 1024 ;
2005-04-17 02:20:36 +04:00
/*
* Constants needed for the sysctl interface .
*/
static const unsigned long nlm_grace_period_min = 0 ;
static const unsigned long nlm_grace_period_max = 240 ;
static const unsigned long nlm_timeout_min = 3 ;
static const unsigned long nlm_timeout_max = 20 ;
static const int nlm_port_min = 0 , nlm_port_max = 65535 ;
2008-03-14 21:18:30 +03:00
# ifdef CONFIG_SYSCTL
2005-04-17 02:20:36 +04:00
static struct ctl_table_header * nlm_sysctl_table ;
2008-03-14 21:18:30 +03:00
# endif
2005-04-17 02:20:36 +04:00
2007-07-17 15:04:35 +04:00
static unsigned long get_lockd_grace_period ( void )
2005-04-17 02:20:36 +04:00
{
/* Note: nlm_timeout should always be nonzero */
if ( nlm_grace_period )
2007-07-17 15:04:35 +04:00
return roundup ( nlm_grace_period , nlm_timeout ) * HZ ;
2005-04-17 02:20:36 +04:00
else
2007-07-17 15:04:35 +04:00
return nlm_timeout * 5 * HZ ;
}
2012-07-25 16:56:50 +04:00
static void grace_ender ( struct work_struct * grace )
2007-07-17 15:04:35 +04:00
{
2016-01-01 17:06:29 +03:00
struct delayed_work * dwork = to_delayed_work ( grace ) ;
2012-07-25 16:56:50 +04:00
struct lockd_net * ln = container_of ( dwork , struct lockd_net ,
grace_period_end ) ;
locks_end_grace ( & ln - > lockd_manager ) ;
2005-04-17 02:20:36 +04:00
}
2012-07-25 16:57:22 +04:00
static void set_grace_period ( struct net * net )
2005-04-17 02:20:36 +04:00
{
2007-09-06 20:34:25 +04:00
unsigned long grace_period = get_lockd_grace_period ( ) ;
2012-07-25 16:57:22 +04:00
struct lockd_net * ln = net_generic ( net , lockd_net_id ) ;
2008-03-19 02:00:19 +03:00
2012-07-25 16:57:22 +04:00
locks_start_grace ( net , & ln - > lockd_manager ) ;
2012-07-25 16:56:43 +04:00
cancel_delayed_work_sync ( & ln - > grace_period_end ) ;
schedule_delayed_work ( & ln - > grace_period_end , grace_period ) ;
2005-04-17 02:20:36 +04:00
}
2009-05-07 00:32:54 +04:00
static void restart_grace ( void )
{
if ( nlmsvc_ops ) {
2012-07-25 16:57:22 +04:00
struct net * net = & init_net ;
struct lockd_net * ln = net_generic ( net , lockd_net_id ) ;
2012-07-25 16:56:43 +04:00
cancel_delayed_work_sync ( & ln - > grace_period_end ) ;
2012-07-25 16:56:50 +04:00
locks_end_grace ( & ln - > lockd_manager ) ;
2009-05-07 00:32:54 +04:00
nlmsvc_invalidate_all ( ) ;
2012-07-25 16:57:22 +04:00
set_grace_period ( net ) ;
2009-05-07 00:32:54 +04:00
}
}
2005-04-17 02:20:36 +04:00
/*
* This is the lockd kernel thread
*/
2008-02-08 00:34:55 +03:00
static int
lockd ( void * vrqstp )
2005-04-17 02:20:36 +04:00
{
2012-08-18 05:47:53 +04:00
int err = 0 ;
2008-02-08 00:34:55 +03:00
struct svc_rqst * rqstp = vrqstp ;
2017-03-29 04:25:08 +03:00
struct net * net = & init_net ;
struct lockd_net * ln = net_generic ( net , lockd_net_id ) ;
2005-04-17 02:20:36 +04:00
2008-02-08 00:34:55 +03:00
/* try_to_freeze() is called from svc_recv() */
2007-07-17 15:03:35 +04:00
set_freezable ( ) ;
2005-04-17 02:20:36 +04:00
2008-02-08 00:34:55 +03:00
/* Allow SIGKILL to tell lockd to drop all of its locks */
2005-04-17 02:20:36 +04:00
allow_signal ( SIGKILL ) ;
dprintk ( " NFS locking service started (ver " LOCKD_VERSION " ). \n " ) ;
/*
* The main request loop . We don ' t terminate until the last
2008-02-08 00:34:55 +03:00
* NFS mount or NFS daemon has gone away .
2005-04-17 02:20:36 +04:00
*/
2008-02-08 00:34:55 +03:00
while ( ! kthread_should_stop ( ) ) {
2005-04-17 02:20:36 +04:00
long timeout = MAX_SCHEDULE_TIMEOUT ;
2008-02-21 10:57:45 +03:00
RPC_IFDEBUG ( char buf [ RPC_MAX_ADDRBUFLEN ] ) ;
2005-04-17 02:20:36 +04:00
2008-10-20 19:51:58 +04:00
/* update sv_maxconn if it has changed */
rqstp - > rq_server - > sv_maxconn = nlm_max_connections ;
2005-04-17 02:20:36 +04:00
if ( signalled ( ) ) {
flush_signals ( current ) ;
2009-05-07 00:32:54 +04:00
restart_grace ( ) ;
2008-02-08 00:34:55 +03:00
continue ;
2005-04-17 02:20:36 +04:00
}
2008-01-24 19:11:34 +03:00
timeout = nlmsvc_retry_blocked ( ) ;
2005-04-17 02:20:36 +04:00
/*
* Find a socket with data available and call its
* recvfrom routine .
*/
2006-10-02 13:17:50 +04:00
err = svc_recv ( rqstp , timeout ) ;
2012-08-18 05:47:53 +04:00
if ( err = = - EAGAIN | | err = = - EINTR )
2005-04-17 02:20:36 +04:00
continue ;
2007-02-12 11:53:32 +03:00
dprintk ( " lockd: request from %s \n " ,
svc_print_addr ( rqstp , buf , sizeof ( buf ) ) ) ;
2005-04-17 02:20:36 +04:00
2006-10-02 13:17:50 +04:00
svc_process ( rqstp ) ;
2005-04-17 02:20:36 +04:00
}
2006-01-03 11:55:19 +03:00
flush_signals ( current ) ;
2008-02-08 00:34:55 +03:00
if ( nlmsvc_ops )
nlmsvc_invalidate_all ( ) ;
nlm_shutdown_hosts ( ) ;
2017-03-29 04:25:08 +03:00
cancel_delayed_work_sync ( & ln - > grace_period_end ) ;
locks_end_grace ( & ln - > lockd_manager ) ;
2008-02-08 00:34:55 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2009-03-19 03:47:59 +03:00
static int create_lockd_listener ( struct svc_serv * serv , const char * name ,
2012-01-31 15:07:48 +04:00
struct net * net , const int family ,
2019-04-09 19:13:39 +03:00
const unsigned short port ,
const struct cred * cred )
2009-01-01 00:06:04 +03:00
{
struct svc_xprt * xprt ;
2012-01-31 15:07:48 +04:00
xprt = svc_find_xprt ( serv , name , net , family , 0 ) ;
2009-01-01 00:06:04 +03:00
if ( xprt = = NULL )
2012-01-31 15:07:48 +04:00
return svc_create_xprt ( serv , name , net , family , port ,
2019-04-09 19:13:37 +03:00
SVC_SOCK_DEFAULTS , cred ) ;
2009-01-01 00:06:04 +03:00
svc_xprt_put ( xprt ) ;
return 0 ;
}
2012-01-31 15:07:48 +04:00
static int create_lockd_family ( struct svc_serv * serv , struct net * net ,
2019-04-09 19:13:39 +03:00
const int family , const struct cred * cred )
2009-03-19 03:47:59 +03:00
{
int err ;
2019-04-09 19:13:39 +03:00
err = create_lockd_listener ( serv , " udp " , net , family , nlm_udpport ,
cred ) ;
2009-03-19 03:47:59 +03:00
if ( err < 0 )
return err ;
2019-04-09 19:13:39 +03:00
return create_lockd_listener ( serv , " tcp " , net , family , nlm_tcpport ,
cred ) ;
2009-03-19 03:47:59 +03:00
}
2007-02-12 11:53:29 +03:00
/*
2008-10-04 01:15:23 +04:00
* Ensure there are active UDP and TCP listeners for lockd .
*
* Even if we have only TCP NFS mounts and / or TCP NFSDs , some
* local services ( such as rpc . statd ) still require UDP , and
* some NFS servers do not yet support NLM over TCP .
*
* Returns zero if all listeners are available ; otherwise a
* negative errno value is returned .
2007-02-12 11:53:29 +03:00
*/
2019-04-09 19:13:39 +03:00
static int make_socks ( struct svc_serv * serv , struct net * net ,
const struct cred * cred )
2006-10-02 13:17:45 +04:00
{
2007-02-12 11:53:29 +03:00
static int warned ;
2009-01-01 00:06:11 +03:00
int err ;
2007-02-12 11:53:29 +03:00
2019-04-09 19:13:39 +03:00
err = create_lockd_family ( serv , net , PF_INET , cred ) ;
2009-01-01 00:06:11 +03:00
if ( err < 0 )
goto out_err ;
2019-04-09 19:13:39 +03:00
err = create_lockd_family ( serv , net , PF_INET6 , cred ) ;
2009-03-19 03:47:59 +03:00
if ( err < 0 & & err ! = - EAFNOSUPPORT )
2009-01-01 00:06:11 +03:00
goto out_err ;
warned = 0 ;
return 0 ;
out_err :
if ( warned + + = = 0 )
2006-10-02 13:17:52 +04:00
printk ( KERN_WARNING
2009-01-01 00:06:11 +03:00
" lockd_up: makesock failed, error=%d \n " , err ) ;
2014-03-25 22:55:26 +04:00
svc_shutdown_net ( serv , net ) ;
2006-10-02 13:17:45 +04:00
return err ;
}
2019-04-09 19:13:39 +03:00
static int lockd_up_net ( struct svc_serv * serv , struct net * net ,
const struct cred * cred )
2012-01-31 15:08:05 +04:00
{
struct lockd_net * ln = net_generic ( net , lockd_net_id ) ;
int error ;
2012-05-04 12:49:41 +04:00
if ( ln - > nlmsvc_users + + )
2012-01-31 15:08:05 +04:00
return 0 ;
2012-04-25 18:22:47 +04:00
error = svc_bind ( serv , net ) ;
2012-01-31 15:08:05 +04:00
if ( error )
2012-04-25 18:22:47 +04:00
goto err_bind ;
2012-01-31 15:08:05 +04:00
2019-04-09 19:13:39 +03:00
error = make_socks ( serv , net , cred ) ;
2012-01-31 15:08:05 +04:00
if ( error < 0 )
2014-08-30 00:25:50 +04:00
goto err_bind ;
2012-07-25 16:57:29 +04:00
set_grace_period ( net ) ;
2017-11-08 08:55:55 +03:00
dprintk ( " %s: per-net data created; net=%x \n " , __func__ , net - > ns . inum ) ;
2012-01-31 15:08:05 +04:00
return 0 ;
2012-04-25 18:22:47 +04:00
err_bind :
2012-05-04 12:49:41 +04:00
ln - > nlmsvc_users - - ;
2012-01-31 15:08:05 +04:00
return error ;
}
2012-04-25 18:22:40 +04:00
static void lockd_down_net ( struct svc_serv * serv , struct net * net )
2012-01-31 15:08:05 +04:00
{
struct lockd_net * ln = net_generic ( net , lockd_net_id ) ;
if ( ln - > nlmsvc_users ) {
2012-01-31 15:08:29 +04:00
if ( - - ln - > nlmsvc_users = = 0 ) {
nlm_shutdown_hosts_net ( net ) ;
2017-11-02 13:03:42 +03:00
cancel_delayed_work_sync ( & ln - > grace_period_end ) ;
locks_end_grace ( & ln - > lockd_manager ) ;
2012-01-31 15:08:05 +04:00
svc_shutdown_net ( serv , net ) ;
2017-11-08 08:55:55 +03:00
dprintk ( " %s: per-net data destroyed; net=%x \n " ,
__func__ , net - > ns . inum ) ;
2012-01-31 15:08:29 +04:00
}
2012-01-31 15:08:05 +04:00
} else {
2017-11-08 08:55:55 +03:00
pr_err ( " %s: no users! task=%p, net=%x \n " ,
__func__ , nlmsvc_task , net - > ns . inum ) ;
2012-01-31 15:08:05 +04:00
BUG ( ) ;
}
}
2015-12-12 00:46:00 +03:00
static int lockd_inetaddr_event ( struct notifier_block * this ,
unsigned long event , void * ptr )
{
struct in_ifaddr * ifa = ( struct in_ifaddr * ) ptr ;
struct sockaddr_in sin ;
2017-11-10 10:19:26 +03:00
if ( ( event ! = NETDEV_DOWN ) | |
! atomic_inc_not_zero ( & nlm_ntf_refcnt ) )
2015-12-12 00:46:00 +03:00
goto out ;
if ( nlmsvc_rqst ) {
dprintk ( " lockd_inetaddr_event: removed %pI4 \n " ,
& ifa - > ifa_local ) ;
sin . sin_family = AF_INET ;
sin . sin_addr . s_addr = ifa - > ifa_local ;
svc_age_temp_xprts_now ( nlmsvc_rqst - > rq_server ,
( struct sockaddr * ) & sin ) ;
}
2017-11-10 10:19:26 +03:00
atomic_dec ( & nlm_ntf_refcnt ) ;
wake_up ( & nlm_ntf_wq ) ;
2015-12-12 00:46:00 +03:00
out :
return NOTIFY_DONE ;
}
static struct notifier_block lockd_inetaddr_notifier = {
. notifier_call = lockd_inetaddr_event ,
} ;
# if IS_ENABLED(CONFIG_IPV6)
static int lockd_inet6addr_event ( struct notifier_block * this ,
unsigned long event , void * ptr )
{
struct inet6_ifaddr * ifa = ( struct inet6_ifaddr * ) ptr ;
struct sockaddr_in6 sin6 ;
2017-11-10 10:19:26 +03:00
if ( ( event ! = NETDEV_DOWN ) | |
! atomic_inc_not_zero ( & nlm_ntf_refcnt ) )
2015-12-12 00:46:00 +03:00
goto out ;
if ( nlmsvc_rqst ) {
dprintk ( " lockd_inet6addr_event: removed %pI6 \n " , & ifa - > addr ) ;
sin6 . sin6_family = AF_INET6 ;
sin6 . sin6_addr = ifa - > addr ;
2017-01-06 00:34:50 +03:00
if ( ipv6_addr_type ( & sin6 . sin6_addr ) & IPV6_ADDR_LINKLOCAL )
sin6 . sin6_scope_id = ifa - > idev - > dev - > ifindex ;
2015-12-12 00:46:00 +03:00
svc_age_temp_xprts_now ( nlmsvc_rqst - > rq_server ,
( struct sockaddr * ) & sin6 ) ;
}
2017-11-10 10:19:26 +03:00
atomic_dec ( & nlm_ntf_refcnt ) ;
wake_up ( & nlm_ntf_wq ) ;
2015-12-12 00:46:00 +03:00
out :
return NOTIFY_DONE ;
}
static struct notifier_block lockd_inet6addr_notifier = {
. notifier_call = lockd_inet6addr_event ,
} ;
# endif
2016-06-30 17:39:32 +03:00
static void lockd_unregister_notifiers ( void )
2015-12-12 00:46:00 +03:00
{
unregister_inetaddr_notifier ( & lockd_inetaddr_notifier ) ;
# if IS_ENABLED(CONFIG_IPV6)
unregister_inet6addr_notifier ( & lockd_inet6addr_notifier ) ;
# endif
2017-11-10 10:19:26 +03:00
wait_event ( nlm_ntf_wq , atomic_read ( & nlm_ntf_refcnt ) = = 0 ) ;
2016-06-30 17:39:32 +03:00
}
static void lockd_svc_exit_thread ( void )
{
2017-11-10 10:19:26 +03:00
atomic_dec ( & nlm_ntf_refcnt ) ;
2016-06-30 17:39:32 +03:00
lockd_unregister_notifiers ( ) ;
2015-12-12 00:46:00 +03:00
svc_exit_thread ( nlmsvc_rqst ) ;
}
2012-04-25 18:23:09 +04:00
static int lockd_start_svc ( struct svc_serv * serv )
{
int error ;
if ( nlmsvc_rqst )
return 0 ;
/*
* Create the kernel thread and wait for it to start .
*/
nlmsvc_rqst = svc_prepare_thread ( serv , & serv - > sv_pools [ 0 ] , NUMA_NO_NODE ) ;
if ( IS_ERR ( nlmsvc_rqst ) ) {
error = PTR_ERR ( nlmsvc_rqst ) ;
printk ( KERN_WARNING
" lockd_up: svc_rqst allocation failed, error=%d \n " ,
error ) ;
2017-10-20 17:33:18 +03:00
lockd_unregister_notifiers ( ) ;
2012-04-25 18:23:09 +04:00
goto out_rqst ;
}
2017-11-10 10:19:26 +03:00
atomic_inc ( & nlm_ntf_refcnt ) ;
2012-04-25 18:23:09 +04:00
svc_sock_update_bufs ( serv ) ;
serv - > sv_maxconn = nlm_max_connections ;
2014-09-02 21:58:57 +04:00
nlmsvc_task = kthread_create ( lockd , nlmsvc_rqst , " %s " , serv - > sv_name ) ;
2012-04-25 18:23:09 +04:00
if ( IS_ERR ( nlmsvc_task ) ) {
error = PTR_ERR ( nlmsvc_task ) ;
printk ( KERN_WARNING
" lockd_up: kthread_run failed, error=%d \n " , error ) ;
goto out_task ;
}
2014-08-03 21:03:07 +04:00
nlmsvc_rqst - > rq_task = nlmsvc_task ;
2014-09-02 21:58:57 +04:00
wake_up_process ( nlmsvc_task ) ;
2014-08-03 21:03:07 +04:00
2012-04-25 18:23:09 +04:00
dprintk ( " lockd_up: service started \n " ) ;
return 0 ;
out_task :
2015-12-12 00:46:00 +03:00
lockd_svc_exit_thread ( ) ;
2012-04-25 18:23:09 +04:00
nlmsvc_task = NULL ;
out_rqst :
nlmsvc_rqst = NULL ;
return error ;
}
2017-08-01 19:00:06 +03:00
static const struct svc_serv_ops lockd_sv_ops = {
2015-06-08 22:06:51 +03:00
. svo_shutdown = svc_rpcb_cleanup ,
. svo_enqueue_xprt = svc_xprt_do_enqueue ,
2015-06-08 22:03:32 +03:00
} ;
2012-04-25 18:22:54 +04:00
static struct svc_serv * lockd_create_svc ( void )
2005-04-17 02:20:36 +04:00
{
2008-02-08 00:34:55 +03:00
struct svc_serv * serv ;
2005-04-17 02:20:36 +04:00
/*
* Check whether we ' re already up and running .
*/
2012-01-31 15:08:05 +04:00
if ( nlmsvc_rqst ) {
2012-04-25 18:22:54 +04:00
/*
* Note : increase service usage , because later in case of error
* svc_destroy ( ) will be called .
*/
svc_get ( nlmsvc_rqst - > rq_server ) ;
return nlmsvc_rqst - > rq_server ;
2012-01-31 15:08:05 +04:00
}
2005-04-17 02:20:36 +04:00
/*
* Sanity check : if there ' s no pid ,
* we should be the first user . . .
*/
2006-10-02 13:17:53 +04:00
if ( nlmsvc_users )
2005-04-17 02:20:36 +04:00
printk ( KERN_WARNING
" lockd_up: no pid, %d users?? \n " , nlmsvc_users ) ;
2015-01-02 23:05:25 +03:00
if ( ! nlm_timeout )
nlm_timeout = LOCKD_DFLT_TIMEO ;
nlmsvc_timeout = nlm_timeout * HZ ;
2015-06-08 22:03:32 +03:00
serv = svc_create ( & nlmsvc_program , LOCKD_BUFSIZE , & lockd_sv_ops ) ;
2005-04-17 02:20:36 +04:00
if ( ! serv ) {
printk ( KERN_WARNING " lockd_up: create service failed \n " ) ;
2012-04-25 18:22:54 +04:00
return ERR_PTR ( - ENOMEM ) ;
}
2015-12-12 00:46:00 +03:00
register_inetaddr_notifier ( & lockd_inetaddr_notifier ) ;
# if IS_ENABLED(CONFIG_IPV6)
register_inet6addr_notifier ( & lockd_inet6addr_notifier ) ;
# endif
2012-04-25 18:23:16 +04:00
dprintk ( " lockd_up: service created \n " ) ;
2012-04-25 18:22:54 +04:00
return serv ;
}
/*
* Bring up the lockd process if it ' s not already up .
*/
2019-04-09 19:13:39 +03:00
int lockd_up ( struct net * net , const struct cred * cred )
2012-04-25 18:22:54 +04:00
{
struct svc_serv * serv ;
2012-04-25 18:23:02 +04:00
int error ;
2012-04-25 18:22:54 +04:00
mutex_lock ( & nlmsvc_mutex ) ;
serv = lockd_create_svc ( ) ;
if ( IS_ERR ( serv ) ) {
error = PTR_ERR ( serv ) ;
goto err_create ;
2005-04-17 02:20:36 +04:00
}
2019-04-09 19:13:39 +03:00
error = lockd_up_net ( serv , net , cred ) ;
2017-10-20 17:33:18 +03:00
if ( error < 0 ) {
lockd_unregister_notifiers ( ) ;
goto err_put ;
}
2005-04-17 02:20:36 +04:00
2012-04-25 18:23:09 +04:00
error = lockd_start_svc ( serv ) ;
2017-10-20 17:33:18 +03:00
if ( error < 0 ) {
lockd_down_net ( serv , net ) ;
goto err_put ;
}
2012-04-25 18:23:02 +04:00
nlmsvc_users + + ;
2005-04-17 02:20:36 +04:00
/*
* Note : svc_serv structures have an initial use count of 1 ,
* so we exit through here on both success and failure .
*/
2016-06-30 17:39:32 +03:00
err_put :
2005-04-17 02:20:36 +04:00
svc_destroy ( serv ) ;
2012-04-25 18:22:54 +04:00
err_create :
2006-03-26 13:37:12 +04:00
mutex_unlock ( & nlmsvc_mutex ) ;
2005-04-17 02:20:36 +04:00
return error ;
}
2008-12-23 23:21:33 +03:00
EXPORT_SYMBOL_GPL ( lockd_up ) ;
2005-04-17 02:20:36 +04:00
/*
* Decrement the user count and bring down lockd if we ' re the last .
*/
void
2012-03-29 18:54:33 +04:00
lockd_down ( struct net * net )
2005-04-17 02:20:36 +04:00
{
2006-03-26 13:37:12 +04:00
mutex_lock ( & nlmsvc_mutex ) ;
2012-04-25 18:22:40 +04:00
lockd_down_net ( nlmsvc_rqst - > rq_server , net ) ;
2005-04-17 02:20:36 +04:00
if ( nlmsvc_users ) {
2012-05-04 12:49:41 +04:00
if ( - - nlmsvc_users )
2005-04-17 02:20:36 +04:00
goto out ;
2008-02-08 00:34:55 +03:00
} else {
printk ( KERN_ERR " lockd_down: no users! task=%p \n " ,
nlmsvc_task ) ;
BUG ( ) ;
2005-04-17 02:20:36 +04:00
}
2008-02-08 00:34:55 +03:00
if ( ! nlmsvc_task ) {
printk ( KERN_ERR " lockd_down: no lockd running. \n " ) ;
BUG ( ) ;
2005-04-17 02:20:36 +04:00
}
2008-02-08 00:34:55 +03:00
kthread_stop ( nlmsvc_task ) ;
2012-04-25 18:23:16 +04:00
dprintk ( " lockd_down: service stopped \n " ) ;
2015-12-12 00:46:00 +03:00
lockd_svc_exit_thread ( ) ;
2012-04-25 18:23:16 +04:00
dprintk ( " lockd_down: service destroyed \n " ) ;
2008-06-11 18:03:12 +04:00
nlmsvc_task = NULL ;
nlmsvc_rqst = NULL ;
2005-04-17 02:20:36 +04:00
out :
2006-03-26 13:37:12 +04:00
mutex_unlock ( & nlmsvc_mutex ) ;
2005-04-17 02:20:36 +04:00
}
2008-12-23 23:21:33 +03:00
EXPORT_SYMBOL_GPL ( lockd_down ) ;
2005-04-17 02:20:36 +04:00
2008-03-14 21:18:30 +03:00
# ifdef CONFIG_SYSCTL
2005-04-17 02:20:36 +04:00
/*
* Sysctl parameters ( same as module parameters , different interface ) .
*/
2014-06-07 01:38:02 +04:00
static struct ctl_table nlm_sysctls [ ] = {
2005-04-17 02:20:36 +04:00
{
. procname = " nlm_grace_period " ,
. data = & nlm_grace_period ,
2005-07-13 12:10:47 +04:00
. maxlen = sizeof ( unsigned long ) ,
2005-04-17 02:20:36 +04:00
. mode = 0644 ,
2009-11-16 14:11:48 +03:00
. proc_handler = proc_doulongvec_minmax ,
2005-04-17 02:20:36 +04:00
. extra1 = ( unsigned long * ) & nlm_grace_period_min ,
. extra2 = ( unsigned long * ) & nlm_grace_period_max ,
} ,
{
. procname = " nlm_timeout " ,
. data = & nlm_timeout ,
2005-07-13 12:10:47 +04:00
. maxlen = sizeof ( unsigned long ) ,
2005-04-17 02:20:36 +04:00
. mode = 0644 ,
2009-11-16 14:11:48 +03:00
. proc_handler = proc_doulongvec_minmax ,
2005-04-17 02:20:36 +04:00
. extra1 = ( unsigned long * ) & nlm_timeout_min ,
. extra2 = ( unsigned long * ) & nlm_timeout_max ,
} ,
{
. procname = " nlm_udpport " ,
. data = & nlm_udpport ,
. maxlen = sizeof ( int ) ,
. mode = 0644 ,
2009-11-16 14:11:48 +03:00
. proc_handler = proc_dointvec_minmax ,
2005-04-17 02:20:36 +04:00
. extra1 = ( int * ) & nlm_port_min ,
. extra2 = ( int * ) & nlm_port_max ,
} ,
{
. procname = " nlm_tcpport " ,
. data = & nlm_tcpport ,
. maxlen = sizeof ( int ) ,
. mode = 0644 ,
2009-11-16 14:11:48 +03:00
. proc_handler = proc_dointvec_minmax ,
2005-04-17 02:20:36 +04:00
. extra1 = ( int * ) & nlm_port_min ,
. extra2 = ( int * ) & nlm_port_max ,
} ,
2006-10-04 13:16:01 +04:00
{
. procname = " nsm_use_hostnames " ,
. data = & nsm_use_hostnames ,
. maxlen = sizeof ( int ) ,
. mode = 0644 ,
2021-08-03 13:59:37 +03:00
. proc_handler = proc_dobool ,
2006-10-04 13:16:01 +04:00
} ,
2006-10-04 13:16:03 +04:00
{
. procname = " nsm_local_state " ,
. data = & nsm_local_state ,
. maxlen = sizeof ( int ) ,
. mode = 0644 ,
2009-11-16 14:11:48 +03:00
. proc_handler = proc_dointvec ,
2006-10-04 13:16:03 +04:00
} ,
2009-11-06 01:25:10 +03:00
{ }
2005-04-17 02:20:36 +04:00
} ;
2014-06-07 01:38:02 +04:00
static struct ctl_table nlm_sysctl_dir [ ] = {
2005-04-17 02:20:36 +04:00
{
. procname = " nfs " ,
. mode = 0555 ,
. child = nlm_sysctls ,
} ,
2009-11-06 01:25:10 +03:00
{ }
2005-04-17 02:20:36 +04:00
} ;
2014-06-07 01:38:02 +04:00
static struct ctl_table nlm_sysctl_root [ ] = {
2005-04-17 02:20:36 +04:00
{
. procname = " fs " ,
. mode = 0555 ,
. child = nlm_sysctl_dir ,
} ,
2009-11-06 01:25:10 +03:00
{ }
2005-04-17 02:20:36 +04:00
} ;
2008-03-14 21:18:30 +03:00
# endif /* CONFIG_SYSCTL */
2005-04-17 02:20:36 +04:00
/*
2007-02-17 21:13:42 +03:00
* Module ( and sysfs ) parameters .
2005-04-17 02:20:36 +04:00
*/
# define param_set_min_max(name, type, which_strtol, min, max) \
treewide: Fix function prototypes for module_param_call()
Several function prototypes for the set/get functions defined by
module_param_call() have a slightly wrong argument types. This fixes
those in an effort to clean up the calls when running under type-enforced
compiler instrumentation for CFI. This is the result of running the
following semantic patch:
@match_module_param_call_function@
declarer name module_param_call;
identifier _name, _set_func, _get_func;
expression _arg, _mode;
@@
module_param_call(_name, _set_func, _get_func, _arg, _mode);
@fix_set_prototype
depends on match_module_param_call_function@
identifier match_module_param_call_function._set_func;
identifier _val, _param;
type _val_type, _param_type;
@@
int _set_func(
-_val_type _val
+const char * _val
,
-_param_type _param
+const struct kernel_param * _param
) { ... }
@fix_get_prototype
depends on match_module_param_call_function@
identifier match_module_param_call_function._get_func;
identifier _val, _param;
type _val_type, _param_type;
@@
int _get_func(
-_val_type _val
+char * _val
,
-_param_type _param
+const struct kernel_param * _param
) { ... }
Two additional by-hand changes are included for places where the above
Coccinelle script didn't notice them:
drivers/platform/x86/thinkpad_acpi.c
fs/lockd/svc.c
Signed-off-by: Kees Cook <keescook@chromium.org>
Signed-off-by: Jessica Yu <jeyu@kernel.org>
2017-10-18 05:04:42 +03:00
static int param_set_ # # name ( const char * val , const struct kernel_param * kp ) \
2005-04-17 02:20:36 +04:00
{ \
char * endp ; \
__typeof__ ( type ) num = which_strtol ( val , & endp , 0 ) ; \
if ( endp = = val | | * endp | | num < ( min ) | | num > ( max ) ) \
return - EINVAL ; \
2012-02-07 08:35:42 +04:00
* ( ( type * ) kp - > arg ) = num ; \
2005-04-17 02:20:36 +04:00
return 0 ; \
}
static inline int is_callback ( u32 proc )
{
return proc = = NLMPROC_GRANTED
| | proc = = NLMPROC_GRANTED_MSG
| | proc = = NLMPROC_TEST_RES
| | proc = = NLMPROC_LOCK_RES
| | proc = = NLMPROC_CANCEL_RES
| | proc = = NLMPROC_UNLOCK_RES
| | proc = = NLMPROC_NSM_NOTIFY ;
}
static int lockd_authenticate ( struct svc_rqst * rqstp )
{
rqstp - > rq_client = NULL ;
switch ( rqstp - > rq_authop - > flavour ) {
case RPC_AUTH_NULL :
case RPC_AUTH_UNIX :
2021-07-15 22:52:12 +03:00
rqstp - > rq_auth_stat = rpc_auth_ok ;
2005-04-17 02:20:36 +04:00
if ( rqstp - > rq_proc = = 0 )
return SVC_OK ;
if ( is_callback ( rqstp - > rq_proc ) ) {
/* Leave it to individual procedures to
* call nlmsvc_lookup_host ( rqstp )
*/
return SVC_OK ;
}
return svc_set_client ( rqstp ) ;
}
2021-07-15 22:52:12 +03:00
rqstp - > rq_auth_stat = rpc_autherr_badcred ;
2005-04-17 02:20:36 +04:00
return SVC_DENIED ;
}
param_set_min_max ( port , int , simple_strtol , 0 , 65535 )
param_set_min_max ( grace_period , unsigned long , simple_strtoul ,
nlm_grace_period_min , nlm_grace_period_max )
param_set_min_max ( timeout , unsigned long , simple_strtoul ,
nlm_timeout_min , nlm_timeout_max )
MODULE_AUTHOR ( " Olaf Kirch <okir@monad.swb.de> " ) ;
MODULE_DESCRIPTION ( " NFS file locking service version " LOCKD_VERSION " . " ) ;
MODULE_LICENSE ( " GPL " ) ;
module_param_call ( nlm_grace_period , param_set_grace_period , param_get_ulong ,
& nlm_grace_period , 0644 ) ;
module_param_call ( nlm_timeout , param_set_timeout , param_get_ulong ,
& nlm_timeout , 0644 ) ;
module_param_call ( nlm_udpport , param_set_port , param_get_int ,
& nlm_udpport , 0644 ) ;
module_param_call ( nlm_tcpport , param_set_port , param_get_int ,
& nlm_tcpport , 0644 ) ;
2006-10-04 13:16:01 +04:00
module_param ( nsm_use_hostnames , bool , 0644 ) ;
2008-10-20 19:51:58 +04:00
module_param ( nlm_max_connections , uint , 0644 ) ;
2005-04-17 02:20:36 +04:00
2012-01-31 15:07:57 +04:00
static int lockd_init_net ( struct net * net )
{
2012-07-25 16:56:43 +04:00
struct lockd_net * ln = net_generic ( net , lockd_net_id ) ;
INIT_DELAYED_WORK ( & ln - > grace_period_end , grace_ender ) ;
2014-09-13 00:40:20 +04:00
INIT_LIST_HEAD ( & ln - > lockd_manager . list ) ;
2015-08-06 19:47:02 +03:00
ln - > lockd_manager . block_opens = false ;
2015-09-23 15:49:29 +03:00
INIT_LIST_HEAD ( & ln - > nsm_handles ) ;
2012-01-31 15:07:57 +04:00
return 0 ;
}
static void lockd_exit_net ( struct net * net )
{
2017-11-06 16:23:24 +03:00
struct lockd_net * ln = net_generic ( net , lockd_net_id ) ;
WARN_ONCE ( ! list_empty ( & ln - > lockd_manager . list ) ,
" net %x %s: lockd_manager.list is not empty \n " ,
net - > ns . inum , __func__ ) ;
WARN_ONCE ( ! list_empty ( & ln - > nsm_handles ) ,
" net %x %s: nsm_handles list is not empty \n " ,
net - > ns . inum , __func__ ) ;
WARN_ONCE ( delayed_work_pending ( & ln - > grace_period_end ) ,
" net %x %s: grace_period_end was not cancelled \n " ,
net - > ns . inum , __func__ ) ;
2012-01-31 15:07:57 +04:00
}
static struct pernet_operations lockd_net_ops = {
. init = lockd_init_net ,
. exit = lockd_exit_net ,
. id = & lockd_net_id ,
. size = sizeof ( struct lockd_net ) ,
} ;
2005-04-17 02:20:36 +04:00
/*
* Initialising and terminating the module .
*/
static int __init init_nlm ( void )
{
2012-01-31 15:07:57 +04:00
int err ;
2008-03-14 21:18:30 +03:00
# ifdef CONFIG_SYSCTL
2012-01-31 15:07:57 +04:00
err = - ENOMEM ;
2007-02-14 11:34:09 +03:00
nlm_sysctl_table = register_sysctl_table ( nlm_sysctl_root ) ;
2012-01-31 15:07:57 +04:00
if ( nlm_sysctl_table = = NULL )
goto err_sysctl ;
# endif
err = register_pernet_subsys ( & lockd_net_ops ) ;
if ( err )
goto err_pernet ;
2014-09-13 00:40:20 +04:00
err = lockd_create_procfs ( ) ;
if ( err )
goto err_procfs ;
2008-03-14 21:18:30 +03:00
return 0 ;
2012-01-31 15:07:57 +04:00
2014-09-13 00:40:20 +04:00
err_procfs :
unregister_pernet_subsys ( & lockd_net_ops ) ;
2012-01-31 15:07:57 +04:00
err_pernet :
# ifdef CONFIG_SYSCTL
unregister_sysctl_table ( nlm_sysctl_table ) ;
err_sysctl :
2014-05-02 01:15:02 +04:00
# endif
2012-01-31 15:07:57 +04:00
return err ;
2005-04-17 02:20:36 +04:00
}
static void __exit exit_nlm ( void )
{
/* FIXME: delete all NLM clients */
nlm_shutdown_hosts ( ) ;
2014-09-13 00:40:20 +04:00
lockd_remove_procfs ( ) ;
2012-01-31 15:07:57 +04:00
unregister_pernet_subsys ( & lockd_net_ops ) ;
2008-03-14 21:18:30 +03:00
# ifdef CONFIG_SYSCTL
2005-04-17 02:20:36 +04:00
unregister_sysctl_table ( nlm_sysctl_table ) ;
2008-03-14 21:18:30 +03:00
# endif
2005-04-17 02:20:36 +04:00
}
module_init ( init_nlm ) ;
module_exit ( exit_nlm ) ;
2021-06-03 23:50:46 +03:00
/**
* nlmsvc_dispatch - Process an NLM Request
* @ rqstp : incoming request
* @ statp : pointer to location of accept_stat field in RPC Reply buffer
*
* Return values :
* % 0 : Processing complete ; do not send a Reply
* % 1 : Processing complete ; send Reply in rqstp - > rq_res
*/
static int nlmsvc_dispatch ( struct svc_rqst * rqstp , __be32 * statp )
{
const struct svc_procedure * procp = rqstp - > rq_procinfo ;
struct kvec * argv = rqstp - > rq_arg . head ;
struct kvec * resv = rqstp - > rq_res . head ;
svcxdr_init_decode ( rqstp ) ;
if ( ! procp - > pc_decode ( rqstp , argv - > iov_base ) )
goto out_decode_err ;
* statp = procp - > pc_func ( rqstp ) ;
if ( * statp = = rpc_drop_reply )
return 0 ;
if ( * statp ! = rpc_success )
return 1 ;
svcxdr_init_encode ( rqstp ) ;
if ( ! procp - > pc_encode ( rqstp , resv - > iov_base + resv - > iov_len ) )
goto out_encode_err ;
return 1 ;
out_decode_err :
* statp = rpc_garbage_args ;
return 1 ;
out_encode_err :
* statp = rpc_system_err ;
return 1 ;
}
2005-04-17 02:20:36 +04:00
/*
* Define NLM program and procedures
*/
2017-05-09 00:40:27 +03:00
static unsigned int nlmsvc_version1_count [ 17 ] ;
2017-05-12 17:21:37 +03:00
static const struct svc_version nlmsvc_version1 = {
. vs_vers = 1 ,
. vs_nproc = 17 ,
. vs_proc = nlmsvc_procedures ,
. vs_count = nlmsvc_version1_count ,
2021-06-03 23:50:46 +03:00
. vs_dispatch = nlmsvc_dispatch ,
2017-05-12 17:21:37 +03:00
. vs_xdrsize = NLMSVC_XDRSIZE ,
2005-04-17 02:20:36 +04:00
} ;
2017-05-09 00:40:27 +03:00
static unsigned int nlmsvc_version3_count [ 24 ] ;
2017-05-12 17:21:37 +03:00
static const struct svc_version nlmsvc_version3 = {
. vs_vers = 3 ,
. vs_nproc = 24 ,
. vs_proc = nlmsvc_procedures ,
. vs_count = nlmsvc_version3_count ,
2021-06-03 23:50:46 +03:00
. vs_dispatch = nlmsvc_dispatch ,
2017-05-12 17:21:37 +03:00
. vs_xdrsize = NLMSVC_XDRSIZE ,
2005-04-17 02:20:36 +04:00
} ;
# ifdef CONFIG_LOCKD_V4
2017-05-09 00:40:27 +03:00
static unsigned int nlmsvc_version4_count [ 24 ] ;
2017-05-12 17:21:37 +03:00
static const struct svc_version nlmsvc_version4 = {
. vs_vers = 4 ,
. vs_nproc = 24 ,
. vs_proc = nlmsvc_procedures4 ,
. vs_count = nlmsvc_version4_count ,
2021-06-03 23:50:46 +03:00
. vs_dispatch = nlmsvc_dispatch ,
2017-05-12 17:21:37 +03:00
. vs_xdrsize = NLMSVC_XDRSIZE ,
2005-04-17 02:20:36 +04:00
} ;
# endif
2017-05-12 17:21:37 +03:00
static const struct svc_version * nlmsvc_version [ ] = {
2005-04-17 02:20:36 +04:00
[ 1 ] = & nlmsvc_version1 ,
[ 3 ] = & nlmsvc_version3 ,
# ifdef CONFIG_LOCKD_V4
[ 4 ] = & nlmsvc_version4 ,
# endif
} ;
static struct svc_stat nlmsvc_stats ;
2006-03-24 14:15:34 +03:00
# define NLM_NRVERS ARRAY_SIZE(nlmsvc_version)
2005-04-17 02:20:36 +04:00
static struct svc_program nlmsvc_program = {
. pg_prog = NLM_PROGRAM , /* program number */
. pg_nvers = NLM_NRVERS , /* number of entries in nlmsvc_version */
. pg_vers = nlmsvc_version , /* version table */
. pg_name = " lockd " , /* service name */
. pg_class = " nfsd " , /* share authentication with nfsd */
. pg_stats = & nlmsvc_stats , /* stats table */
2019-04-09 18:46:15 +03:00
. pg_authenticate = & lockd_authenticate , /* export authentication */
. pg_init_request = svc_generic_init_request ,
2019-04-09 18:46:17 +03:00
. pg_rpcbind_set = svc_generic_rpcbind_set ,
2005-04-17 02:20:36 +04:00
} ;