Blame view

fs/nfs/cache_lib.c 3.84 KB
b24413180   Greg Kroah-Hartman   License cleanup: ...
1
  // SPDX-License-Identifier: GPL-2.0
e571cbf1a   Trond Myklebust   NFS: Add a dns re...
2
3
4
5
6
7
8
9
10
11
12
13
  /*
   * linux/fs/nfs/cache_lib.c
   *
   * Helper routines for the NFS client caches
   *
   * Copyright (c) 2009 Trond Myklebust <Trond.Myklebust@netapp.com>
   */
  #include <linux/kmod.h>
  #include <linux/module.h>
  #include <linux/moduleparam.h>
  #include <linux/mount.h>
  #include <linux/namei.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
14
  #include <linux/slab.h>
e571cbf1a   Trond Myklebust   NFS: Add a dns re...
15
16
  #include <linux/sunrpc/cache.h>
  #include <linux/sunrpc/rpc_pipe_fs.h>
5c1cacb17   Stanislav Kinsbursky   NFS: handle NFS c...
17
  #include <net/net_namespace.h>
e571cbf1a   Trond Myklebust   NFS: Add a dns re...
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
  
  #include "cache_lib.h"
  
  #define NFS_CACHE_UPCALL_PATHLEN 256
  #define NFS_CACHE_UPCALL_TIMEOUT 15
  
  static char nfs_cache_getent_prog[NFS_CACHE_UPCALL_PATHLEN] =
  				"/sbin/nfs_cache_getent";
  static unsigned long nfs_cache_getent_timeout = NFS_CACHE_UPCALL_TIMEOUT;
  
  module_param_string(cache_getent, nfs_cache_getent_prog,
  		sizeof(nfs_cache_getent_prog), 0600);
  MODULE_PARM_DESC(cache_getent, "Path to the client cache upcall program");
  module_param_named(cache_getent_timeout, nfs_cache_getent_timeout, ulong, 0600);
  MODULE_PARM_DESC(cache_getent_timeout, "Timeout (in seconds) after which "
  		"the cache upcall is assumed to have failed");
  
  int nfs_cache_upcall(struct cache_detail *cd, char *entry_name)
  {
  	static char *envp[] = { "HOME=/",
  		"TERM=linux",
  		"PATH=/sbin:/usr/sbin:/bin:/usr/bin",
  		NULL
  	};
  	char *argv[] = {
  		nfs_cache_getent_prog,
  		cd->name,
  		entry_name,
  		NULL
  	};
  	int ret = -EACCES;
  
  	if (nfs_cache_getent_prog[0] == '\0')
  		goto out;
  	ret = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC);
  	/*
  	 * Disable the upcall mechanism if we're getting an ENOENT or
  	 * EACCES error. The admin can re-enable it on the fly by using
  	 * sysfs to set the 'cache_getent' parameter once the problem
  	 * has been fixed.
  	 */
  	if (ret == -ENOENT || ret == -EACCES)
  		nfs_cache_getent_prog[0] = '\0';
  out:
  	return ret > 0 ? 0 : ret;
  }
  
  /*
   * Deferred request handling
   */
  void nfs_cache_defer_req_put(struct nfs_cache_defer_req *dreq)
  {
0896cade1   Elena Reshetova   fs, nfs: convert ...
70
  	if (refcount_dec_and_test(&dreq->count))
e571cbf1a   Trond Myklebust   NFS: Add a dns re...
71
72
73
74
75
76
77
78
  		kfree(dreq);
  }
  
  static void nfs_dns_cache_revisit(struct cache_deferred_req *d, int toomany)
  {
  	struct nfs_cache_defer_req *dreq;
  
  	dreq = container_of(d, struct nfs_cache_defer_req, deferred_req);
2a446a5d9   Daniel Wagner   NFS: cache_lib: u...
79
  	complete(&dreq->completion);
e571cbf1a   Trond Myklebust   NFS: Add a dns re...
80
81
82
83
84
85
86
87
88
  	nfs_cache_defer_req_put(dreq);
  }
  
  static struct cache_deferred_req *nfs_dns_cache_defer(struct cache_req *req)
  {
  	struct nfs_cache_defer_req *dreq;
  
  	dreq = container_of(req, struct nfs_cache_defer_req, req);
  	dreq->deferred_req.revisit = nfs_dns_cache_revisit;
0896cade1   Elena Reshetova   fs, nfs: convert ...
89
  	refcount_inc(&dreq->count);
e571cbf1a   Trond Myklebust   NFS: Add a dns re...
90
91
92
93
94
95
96
97
98
99
100
  
  	return &dreq->deferred_req;
  }
  
  struct nfs_cache_defer_req *nfs_cache_defer_req_alloc(void)
  {
  	struct nfs_cache_defer_req *dreq;
  
  	dreq = kzalloc(sizeof(*dreq), GFP_KERNEL);
  	if (dreq) {
  		init_completion(&dreq->completion);
0896cade1   Elena Reshetova   fs, nfs: convert ...
101
  		refcount_set(&dreq->count, 1);
e571cbf1a   Trond Myklebust   NFS: Add a dns re...
102
103
104
105
106
107
108
109
110
111
112
113
  		dreq->req.defer = nfs_dns_cache_defer;
  	}
  	return dreq;
  }
  
  int nfs_cache_wait_for_upcall(struct nfs_cache_defer_req *dreq)
  {
  	if (wait_for_completion_timeout(&dreq->completion,
  			nfs_cache_getent_timeout * HZ) == 0)
  		return -ETIMEDOUT;
  	return 0;
  }
9df69c81b   Stanislav Kinsbursky   NFS: DNS resolver...
114
  int nfs_cache_register_sb(struct super_block *sb, struct cache_detail *cd)
5c1cacb17   Stanislav Kinsbursky   NFS: handle NFS c...
115
116
117
118
119
  {
  	int ret;
  	struct dentry *dir;
  
  	dir = rpc_d_lookup_sb(sb, "cache");
5c1cacb17   Stanislav Kinsbursky   NFS: handle NFS c...
120
121
122
123
124
125
  	ret = sunrpc_cache_register_pipefs(dir, cd->name, 0600, cd);
  	dput(dir);
  	return ret;
  }
  
  int nfs_cache_register_net(struct net *net, struct cache_detail *cd)
e571cbf1a   Trond Myklebust   NFS: Add a dns re...
126
  {
5c1cacb17   Stanislav Kinsbursky   NFS: handle NFS c...
127
  	struct super_block *pipefs_sb;
39cb67b9a   Stanislav Kinsbursky   NFS: remove RPC P...
128
  	int ret = 0;
e571cbf1a   Trond Myklebust   NFS: Add a dns re...
129

462b8f6bf   Stanislav Kinsbursky   NFS: simplify and...
130
  	sunrpc_init_cache_detail(cd);
5c1cacb17   Stanislav Kinsbursky   NFS: handle NFS c...
131
  	pipefs_sb = rpc_get_sb_net(net);
39cb67b9a   Stanislav Kinsbursky   NFS: remove RPC P...
132
133
134
  	if (pipefs_sb) {
  		ret = nfs_cache_register_sb(pipefs_sb, cd);
  		rpc_put_sb_net(net);
462b8f6bf   Stanislav Kinsbursky   NFS: simplify and...
135
136
  		if (ret)
  			sunrpc_destroy_cache_detail(cd);
5c1cacb17   Stanislav Kinsbursky   NFS: handle NFS c...
137
  	}
e571cbf1a   Trond Myklebust   NFS: Add a dns re...
138
139
  	return ret;
  }
9df69c81b   Stanislav Kinsbursky   NFS: DNS resolver...
140
  void nfs_cache_unregister_sb(struct super_block *sb, struct cache_detail *cd)
e571cbf1a   Trond Myklebust   NFS: Add a dns re...
141
  {
863d7d9c2   Kinglong Mee   sunrpc/nfs: clean...
142
  	sunrpc_cache_unregister_pipefs(cd);
5c1cacb17   Stanislav Kinsbursky   NFS: handle NFS c...
143
144
145
146
147
148
149
150
151
152
153
  }
  
  void nfs_cache_unregister_net(struct net *net, struct cache_detail *cd)
  {
  	struct super_block *pipefs_sb;
  
  	pipefs_sb = rpc_get_sb_net(net);
  	if (pipefs_sb) {
  		nfs_cache_unregister_sb(pipefs_sb, cd);
  		rpc_put_sb_net(net);
  	}
9222b9550   Stanislav Kinsbursky   NFS: split cache ...
154
155
  	sunrpc_destroy_cache_detail(cd);
  }