Commit | Line | Data |
---|---|---|
a1eaecbc BH |
1 | /* |
2 | * Device operations for the pnfs client. | |
3 | * | |
4 | * Copyright (c) 2002 | |
5 | * The Regents of the University of Michigan | |
6 | * All Rights Reserved | |
7 | * | |
8 | * Dean Hildebrand <dhildebz@umich.edu> | |
9 | * Garth Goodson <Garth.Goodson@netapp.com> | |
10 | * | |
11 | * Permission is granted to use, copy, create derivative works, and | |
12 | * redistribute this software and such derivative works for any purpose, | |
13 | * so long as the name of the University of Michigan is not used in | |
14 | * any advertising or publicity pertaining to the use or distribution | |
15 | * of this software without specific, written prior authorization. If | |
16 | * the above copyright notice or any other identification of the | |
17 | * University of Michigan is included in any copy of any portion of | |
18 | * this software, then the disclaimer below must also be included. | |
19 | * | |
20 | * This software is provided as is, without representation or warranty | |
21 | * of any kind either express or implied, including without limitation | |
22 | * the implied warranties of merchantability, fitness for a particular | |
23 | * purpose, or noninfringement. The Regents of the University of | |
24 | * Michigan shall not be liable for any damages, including special, | |
25 | * indirect, incidental, or consequential damages, with respect to any | |
26 | * claim arising out of or in connection with the use of the software, | |
27 | * even if it has been or is hereafter advised of the possibility of | |
28 | * such damages. | |
29 | */ | |
30 | ||
afeacc8c | 31 | #include <linux/export.h> |
a1eaecbc BH |
32 | #include "pnfs.h" |
33 | ||
34 | #define NFSDBG_FACILITY NFSDBG_PNFS | |
35 | ||
36 | /* | |
37 | * Device ID RCU cache. A device ID is unique per server and layout type. | |
38 | */ | |
39 | #define NFS4_DEVICE_ID_HASH_BITS 5 | |
40 | #define NFS4_DEVICE_ID_HASH_SIZE (1 << NFS4_DEVICE_ID_HASH_BITS) | |
41 | #define NFS4_DEVICE_ID_HASH_MASK (NFS4_DEVICE_ID_HASH_SIZE - 1) | |
42 | ||
1dfed273 TM |
43 | #define PNFS_DEVICE_RETRY_TIMEOUT (120*HZ) |
44 | ||
a1eaecbc BH |
45 | static struct hlist_head nfs4_deviceid_cache[NFS4_DEVICE_ID_HASH_SIZE]; |
46 | static DEFINE_SPINLOCK(nfs4_deviceid_lock); | |
47 | ||
6f00866d | 48 | #ifdef NFS_DEBUG |
a1eaecbc BH |
49 | void |
50 | nfs4_print_deviceid(const struct nfs4_deviceid *id) | |
51 | { | |
52 | u32 *p = (u32 *)id; | |
53 | ||
54 | dprintk("%s: device id= [%x%x%x%x]\n", __func__, | |
55 | p[0], p[1], p[2], p[3]); | |
56 | } | |
57 | EXPORT_SYMBOL_GPL(nfs4_print_deviceid); | |
6f00866d | 58 | #endif |
a1eaecbc BH |
59 | |
60 | static inline u32 | |
61 | nfs4_deviceid_hash(const struct nfs4_deviceid *id) | |
62 | { | |
63 | unsigned char *cptr = (unsigned char *)id->data; | |
64 | unsigned int nbytes = NFS4_DEVICEID4_SIZE; | |
65 | u32 x = 0; | |
66 | ||
67 | while (nbytes--) { | |
68 | x *= 37; | |
69 | x += *cptr++; | |
70 | } | |
71 | return x & NFS4_DEVICE_ID_HASH_MASK; | |
72 | } | |
73 | ||
1be5683b | 74 | static struct nfs4_deviceid_node * |
35c8bb54 BH |
75 | _lookup_deviceid(const struct pnfs_layoutdriver_type *ld, |
76 | const struct nfs_client *clp, const struct nfs4_deviceid *id, | |
1be5683b ME |
77 | long hash) |
78 | { | |
79 | struct nfs4_deviceid_node *d; | |
80 | struct hlist_node *n; | |
81 | ||
82 | hlist_for_each_entry_rcu(d, n, &nfs4_deviceid_cache[hash], node) | |
35c8bb54 BH |
83 | if (d->ld == ld && d->nfs_client == clp && |
84 | !memcmp(&d->deviceid, id, sizeof(*id))) { | |
1be5683b ME |
85 | if (atomic_read(&d->ref)) |
86 | return d; | |
87 | else | |
88 | continue; | |
89 | } | |
90 | return NULL; | |
91 | } | |
92 | ||
a1eaecbc BH |
93 | /* |
94 | * Lookup a deviceid in cache and get a reference count on it if found | |
95 | * | |
96 | * @clp nfs_client associated with deviceid | |
97 | * @id deviceid to look up | |
98 | */ | |
17280175 | 99 | static struct nfs4_deviceid_node * |
35c8bb54 BH |
100 | _find_get_deviceid(const struct pnfs_layoutdriver_type *ld, |
101 | const struct nfs_client *clp, const struct nfs4_deviceid *id, | |
1be5683b ME |
102 | long hash) |
103 | { | |
104 | struct nfs4_deviceid_node *d; | |
105 | ||
106 | rcu_read_lock(); | |
35c8bb54 | 107 | d = _lookup_deviceid(ld, clp, id, hash); |
47cb498e TM |
108 | if (d != NULL) |
109 | atomic_inc(&d->ref); | |
1be5683b ME |
110 | rcu_read_unlock(); |
111 | return d; | |
112 | } | |
113 | ||
a1eaecbc | 114 | struct nfs4_deviceid_node * |
35c8bb54 BH |
115 | nfs4_find_get_deviceid(const struct pnfs_layoutdriver_type *ld, |
116 | const struct nfs_client *clp, const struct nfs4_deviceid *id) | |
1be5683b | 117 | { |
35c8bb54 | 118 | return _find_get_deviceid(ld, clp, id, nfs4_deviceid_hash(id)); |
1be5683b ME |
119 | } |
120 | EXPORT_SYMBOL_GPL(nfs4_find_get_deviceid); | |
121 | ||
122 | /* | |
47cb498e | 123 | * Remove a deviceid from cache |
1be5683b ME |
124 | * |
125 | * @clp nfs_client associated with deviceid | |
126 | * @id the deviceid to unhash | |
127 | * | |
128 | * @ret the unhashed node, if found and dereferenced to zero, NULL otherwise. | |
129 | */ | |
47cb498e TM |
130 | void |
131 | nfs4_delete_deviceid(const struct pnfs_layoutdriver_type *ld, | |
35c8bb54 | 132 | const struct nfs_client *clp, const struct nfs4_deviceid *id) |
a1eaecbc BH |
133 | { |
134 | struct nfs4_deviceid_node *d; | |
a1eaecbc | 135 | |
1be5683b | 136 | spin_lock(&nfs4_deviceid_lock); |
a1eaecbc | 137 | rcu_read_lock(); |
35c8bb54 | 138 | d = _lookup_deviceid(ld, clp, id, nfs4_deviceid_hash(id)); |
a1eaecbc | 139 | rcu_read_unlock(); |
1be5683b ME |
140 | if (!d) { |
141 | spin_unlock(&nfs4_deviceid_lock); | |
47cb498e | 142 | return; |
1be5683b ME |
143 | } |
144 | hlist_del_init_rcu(&d->node); | |
145 | spin_unlock(&nfs4_deviceid_lock); | |
146 | synchronize_rcu(); | |
147 | ||
148 | /* balance the initial ref set in pnfs_insert_deviceid */ | |
149 | if (atomic_dec_and_test(&d->ref)) | |
47cb498e | 150 | d->ld->free_deviceid_node(d); |
1be5683b ME |
151 | } |
152 | EXPORT_SYMBOL_GPL(nfs4_delete_deviceid); | |
a1eaecbc BH |
153 | |
154 | void | |
155 | nfs4_init_deviceid_node(struct nfs4_deviceid_node *d, | |
1775bc34 | 156 | const struct pnfs_layoutdriver_type *ld, |
a1eaecbc BH |
157 | const struct nfs_client *nfs_client, |
158 | const struct nfs4_deviceid *id) | |
159 | { | |
1775bc34 | 160 | INIT_HLIST_NODE(&d->node); |
9e3bd4e2 | 161 | INIT_HLIST_NODE(&d->tmpnode); |
1775bc34 | 162 | d->ld = ld; |
a1eaecbc | 163 | d->nfs_client = nfs_client; |
c47abcf8 | 164 | d->flags = 0; |
a1eaecbc | 165 | d->deviceid = *id; |
1775bc34 | 166 | atomic_set(&d->ref, 1); |
a1eaecbc BH |
167 | } |
168 | EXPORT_SYMBOL_GPL(nfs4_init_deviceid_node); | |
169 | ||
170 | /* | |
171 | * Uniquely initialize and insert a deviceid node into cache | |
172 | * | |
173 | * @new new deviceid node | |
1775bc34 BH |
174 | * Note that the caller must set up the following members: |
175 | * new->ld | |
176 | * new->nfs_client | |
177 | * new->deviceid | |
a1eaecbc BH |
178 | * |
179 | * @ret the inserted node, if none found, otherwise, the found entry. | |
180 | */ | |
181 | struct nfs4_deviceid_node * | |
182 | nfs4_insert_deviceid_node(struct nfs4_deviceid_node *new) | |
183 | { | |
184 | struct nfs4_deviceid_node *d; | |
185 | long hash; | |
186 | ||
187 | spin_lock(&nfs4_deviceid_lock); | |
1be5683b | 188 | hash = nfs4_deviceid_hash(&new->deviceid); |
35c8bb54 | 189 | d = _find_get_deviceid(new->ld, new->nfs_client, &new->deviceid, hash); |
a1eaecbc BH |
190 | if (d) { |
191 | spin_unlock(&nfs4_deviceid_lock); | |
192 | return d; | |
193 | } | |
194 | ||
a1eaecbc BH |
195 | hlist_add_head_rcu(&new->node, &nfs4_deviceid_cache[hash]); |
196 | spin_unlock(&nfs4_deviceid_lock); | |
1d92a08d | 197 | atomic_inc(&new->ref); |
a1eaecbc BH |
198 | |
199 | return new; | |
200 | } | |
201 | EXPORT_SYMBOL_GPL(nfs4_insert_deviceid_node); | |
202 | ||
203 | /* | |
204 | * Dereference a deviceid node and delete it when its reference count drops | |
205 | * to zero. | |
206 | * | |
207 | * @d deviceid node to put | |
208 | * | |
47cb498e TM |
209 | * return true iff the node was deleted |
210 | * Note that since the test for d->ref == 0 is sufficient to establish | |
211 | * that the node is no longer hashed in the global device id cache. | |
a1eaecbc BH |
212 | */ |
213 | bool | |
214 | nfs4_put_deviceid_node(struct nfs4_deviceid_node *d) | |
215 | { | |
47cb498e | 216 | if (!atomic_dec_and_test(&d->ref)) |
a1eaecbc | 217 | return false; |
1775bc34 | 218 | d->ld->free_deviceid_node(d); |
a1eaecbc BH |
219 | return true; |
220 | } | |
221 | EXPORT_SYMBOL_GPL(nfs4_put_deviceid_node); | |
1775bc34 | 222 | |
1dfed273 TM |
223 | void |
224 | nfs4_mark_deviceid_unavailable(struct nfs4_deviceid_node *node) | |
225 | { | |
226 | node->timestamp_unavailable = jiffies; | |
227 | set_bit(NFS_DEVICEID_UNAVAILABLE, &node->flags); | |
228 | } | |
229 | EXPORT_SYMBOL_GPL(nfs4_mark_deviceid_unavailable); | |
230 | ||
231 | bool | |
232 | nfs4_test_deviceid_unavailable(struct nfs4_deviceid_node *node) | |
233 | { | |
234 | if (test_bit(NFS_DEVICEID_UNAVAILABLE, &node->flags)) { | |
235 | unsigned long start, end; | |
236 | ||
237 | end = jiffies; | |
238 | start = end - PNFS_DEVICE_RETRY_TIMEOUT; | |
239 | if (time_in_range(node->timestamp_unavailable, start, end)) | |
240 | return true; | |
241 | clear_bit(NFS_DEVICEID_UNAVAILABLE, &node->flags); | |
242 | } | |
243 | return false; | |
244 | } | |
245 | EXPORT_SYMBOL_GPL(nfs4_test_deviceid_unavailable); | |
246 | ||
1775bc34 BH |
247 | static void |
248 | _deviceid_purge_client(const struct nfs_client *clp, long hash) | |
249 | { | |
250 | struct nfs4_deviceid_node *d; | |
9e3bd4e2 | 251 | struct hlist_node *n; |
1775bc34 BH |
252 | HLIST_HEAD(tmp); |
253 | ||
9e3bd4e2 | 254 | spin_lock(&nfs4_deviceid_lock); |
1775bc34 BH |
255 | rcu_read_lock(); |
256 | hlist_for_each_entry_rcu(d, n, &nfs4_deviceid_cache[hash], node) | |
257 | if (d->nfs_client == clp && atomic_read(&d->ref)) { | |
258 | hlist_del_init_rcu(&d->node); | |
9e3bd4e2 | 259 | hlist_add_head(&d->tmpnode, &tmp); |
1775bc34 BH |
260 | } |
261 | rcu_read_unlock(); | |
9e3bd4e2 | 262 | spin_unlock(&nfs4_deviceid_lock); |
1775bc34 BH |
263 | |
264 | if (hlist_empty(&tmp)) | |
265 | return; | |
266 | ||
267 | synchronize_rcu(); | |
9e3bd4e2 WAA |
268 | while (!hlist_empty(&tmp)) { |
269 | d = hlist_entry(tmp.first, struct nfs4_deviceid_node, tmpnode); | |
270 | hlist_del(&d->tmpnode); | |
1775bc34 BH |
271 | if (atomic_dec_and_test(&d->ref)) |
272 | d->ld->free_deviceid_node(d); | |
9e3bd4e2 | 273 | } |
1775bc34 BH |
274 | } |
275 | ||
276 | void | |
277 | nfs4_deviceid_purge_client(const struct nfs_client *clp) | |
278 | { | |
279 | long h; | |
280 | ||
9e3bd4e2 WAA |
281 | if (!(clp->cl_exchange_flags & EXCHGID4_FLAG_USE_PNFS_MDS)) |
282 | return; | |
1775bc34 BH |
283 | for (h = 0; h < NFS4_DEVICE_ID_HASH_SIZE; h++) |
284 | _deviceid_purge_client(clp, h); | |
1775bc34 | 285 | } |
c47abcf8 AA |
286 | |
287 | /* | |
288 | * Stop use of all deviceids associated with an nfs_client | |
289 | */ | |
290 | void | |
291 | nfs4_deviceid_mark_client_invalid(struct nfs_client *clp) | |
292 | { | |
293 | struct nfs4_deviceid_node *d; | |
294 | struct hlist_node *n; | |
295 | int i; | |
296 | ||
297 | rcu_read_lock(); | |
298 | for (i = 0; i < NFS4_DEVICE_ID_HASH_SIZE; i ++){ | |
299 | hlist_for_each_entry_rcu(d, n, &nfs4_deviceid_cache[i], node) | |
300 | if (d->nfs_client == clp) | |
301 | set_bit(NFS_DEVICEID_INVALID, &d->flags); | |
302 | } | |
303 | rcu_read_unlock(); | |
304 | } | |
1dfed273 | 305 |