Blame view

fs/nfs/cache_lib.c 3.83 KB
e571cbf1a   Trond Myklebust   NFS: Add a dns re...
1
2
3
4
5
6
7
8
9
10
11
12
  /*
   * 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: ...
13
  #include <linux/slab.h>
e571cbf1a   Trond Myklebust   NFS: Add a dns re...
14
15
  #include <linux/sunrpc/cache.h>
  #include <linux/sunrpc/rpc_pipe_fs.h>
5c1cacb17   Stanislav Kinsbursky   NFS: handle NFS c...
16
  #include <net/net_namespace.h>
e571cbf1a   Trond Myklebust   NFS: Add a dns re...
17
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
70
71
72
73
74
75
76
77
  
  #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)
  {
  	if (atomic_dec_and_test(&dreq->count))
  		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...
78
  	complete(&dreq->completion);
e571cbf1a   Trond Myklebust   NFS: Add a dns re...
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
  	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;
  	atomic_inc(&dreq->count);
  
  	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);
  		atomic_set(&dreq->count, 1);
  		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...
113
  int nfs_cache_register_sb(struct super_block *sb, struct cache_detail *cd)
5c1cacb17   Stanislav Kinsbursky   NFS: handle NFS c...
114
115
116
117
118
  {
  	int ret;
  	struct dentry *dir;
  
  	dir = rpc_d_lookup_sb(sb, "cache");
5c1cacb17   Stanislav Kinsbursky   NFS: handle NFS c...
119
120
121
122
123
124
  	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...
125
  {
5c1cacb17   Stanislav Kinsbursky   NFS: handle NFS c...
126
  	struct super_block *pipefs_sb;
39cb67b9a   Stanislav Kinsbursky   NFS: remove RPC P...
127
  	int ret = 0;
e571cbf1a   Trond Myklebust   NFS: Add a dns re...
128

462b8f6bf   Stanislav Kinsbursky   NFS: simplify and...
129
  	sunrpc_init_cache_detail(cd);
5c1cacb17   Stanislav Kinsbursky   NFS: handle NFS c...
130
  	pipefs_sb = rpc_get_sb_net(net);
39cb67b9a   Stanislav Kinsbursky   NFS: remove RPC P...
131
132
133
  	if (pipefs_sb) {
  		ret = nfs_cache_register_sb(pipefs_sb, cd);
  		rpc_put_sb_net(net);
462b8f6bf   Stanislav Kinsbursky   NFS: simplify and...
134
135
  		if (ret)
  			sunrpc_destroy_cache_detail(cd);
5c1cacb17   Stanislav Kinsbursky   NFS: handle NFS c...
136
  	}
e571cbf1a   Trond Myklebust   NFS: Add a dns re...
137
138
  	return ret;
  }
9df69c81b   Stanislav Kinsbursky   NFS: DNS resolver...
139
  void nfs_cache_unregister_sb(struct super_block *sb, struct cache_detail *cd)
e571cbf1a   Trond Myklebust   NFS: Add a dns re...
140
  {
5c1cacb17   Stanislav Kinsbursky   NFS: handle NFS c...
141
142
143
144
145
146
147
148
149
150
151
152
153
  	if (cd->u.pipefs.dir)
  		sunrpc_cache_unregister_pipefs(cd);
  }
  
  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);
  }