]>
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 | ||
31 | #include "pnfs.h" | |
32 | ||
33 | #define NFSDBG_FACILITY NFSDBG_PNFS | |
34 | ||
35 | /* | |
36 | * Device ID RCU cache. A device ID is unique per server and layout type. | |
37 | */ | |
38 | #define NFS4_DEVICE_ID_HASH_BITS 5 | |
39 | #define NFS4_DEVICE_ID_HASH_SIZE (1 << NFS4_DEVICE_ID_HASH_BITS) | |
40 | #define NFS4_DEVICE_ID_HASH_MASK (NFS4_DEVICE_ID_HASH_SIZE - 1) | |
41 | ||
42 | static struct hlist_head nfs4_deviceid_cache[NFS4_DEVICE_ID_HASH_SIZE]; | |
43 | static DEFINE_SPINLOCK(nfs4_deviceid_lock); | |
44 | ||
45 | void | |
46 | nfs4_print_deviceid(const struct nfs4_deviceid *id) | |
47 | { | |
48 | u32 *p = (u32 *)id; | |
49 | ||
50 | dprintk("%s: device id= [%x%x%x%x]\n", __func__, | |
51 | p[0], p[1], p[2], p[3]); | |
52 | } | |
53 | EXPORT_SYMBOL_GPL(nfs4_print_deviceid); | |
54 | ||
55 | static inline u32 | |
56 | nfs4_deviceid_hash(const struct nfs4_deviceid *id) | |
57 | { | |
58 | unsigned char *cptr = (unsigned char *)id->data; | |
59 | unsigned int nbytes = NFS4_DEVICEID4_SIZE; | |
60 | u32 x = 0; | |
61 | ||
62 | while (nbytes--) { | |
63 | x *= 37; | |
64 | x += *cptr++; | |
65 | } | |
66 | return x & NFS4_DEVICE_ID_HASH_MASK; | |
67 | } | |
68 | ||
1be5683b ME |
69 | static struct nfs4_deviceid_node * |
70 | _lookup_deviceid(const struct nfs_client *clp, const struct nfs4_deviceid *id, | |
71 | long hash) | |
72 | { | |
73 | struct nfs4_deviceid_node *d; | |
74 | struct hlist_node *n; | |
75 | ||
76 | hlist_for_each_entry_rcu(d, n, &nfs4_deviceid_cache[hash], node) | |
77 | if (d->nfs_client == clp && !memcmp(&d->deviceid, id, sizeof(*id))) { | |
78 | if (atomic_read(&d->ref)) | |
79 | return d; | |
80 | else | |
81 | continue; | |
82 | } | |
83 | return NULL; | |
84 | } | |
85 | ||
a1eaecbc BH |
86 | /* |
87 | * Lookup a deviceid in cache and get a reference count on it if found | |
88 | * | |
89 | * @clp nfs_client associated with deviceid | |
90 | * @id deviceid to look up | |
91 | */ | |
1be5683b ME |
92 | struct nfs4_deviceid_node * |
93 | _find_get_deviceid(const struct nfs_client *clp, const struct nfs4_deviceid *id, | |
94 | long hash) | |
95 | { | |
96 | struct nfs4_deviceid_node *d; | |
97 | ||
98 | rcu_read_lock(); | |
99 | d = _lookup_deviceid(clp, id, hash); | |
100 | if (d && !atomic_inc_not_zero(&d->ref)) | |
101 | d = NULL; | |
102 | rcu_read_unlock(); | |
103 | return d; | |
104 | } | |
105 | ||
a1eaecbc BH |
106 | struct nfs4_deviceid_node * |
107 | nfs4_find_get_deviceid(const struct nfs_client *clp, const struct nfs4_deviceid *id) | |
1be5683b ME |
108 | { |
109 | return _find_get_deviceid(clp, id, nfs4_deviceid_hash(id)); | |
110 | } | |
111 | EXPORT_SYMBOL_GPL(nfs4_find_get_deviceid); | |
112 | ||
113 | /* | |
114 | * Unhash and put deviceid | |
115 | * | |
116 | * @clp nfs_client associated with deviceid | |
117 | * @id the deviceid to unhash | |
118 | * | |
119 | * @ret the unhashed node, if found and dereferenced to zero, NULL otherwise. | |
120 | */ | |
121 | struct nfs4_deviceid_node * | |
122 | nfs4_unhash_put_deviceid(const struct nfs_client *clp, const struct nfs4_deviceid *id) | |
a1eaecbc BH |
123 | { |
124 | struct nfs4_deviceid_node *d; | |
a1eaecbc | 125 | |
1be5683b | 126 | spin_lock(&nfs4_deviceid_lock); |
a1eaecbc | 127 | rcu_read_lock(); |
1be5683b | 128 | d = _lookup_deviceid(clp, id, nfs4_deviceid_hash(id)); |
a1eaecbc | 129 | rcu_read_unlock(); |
1be5683b ME |
130 | if (!d) { |
131 | spin_unlock(&nfs4_deviceid_lock); | |
132 | return NULL; | |
133 | } | |
134 | hlist_del_init_rcu(&d->node); | |
135 | spin_unlock(&nfs4_deviceid_lock); | |
136 | synchronize_rcu(); | |
137 | ||
138 | /* balance the initial ref set in pnfs_insert_deviceid */ | |
139 | if (atomic_dec_and_test(&d->ref)) | |
140 | return d; | |
141 | ||
a1eaecbc BH |
142 | return NULL; |
143 | } | |
1be5683b ME |
144 | EXPORT_SYMBOL_GPL(nfs4_unhash_put_deviceid); |
145 | ||
146 | /* | |
147 | * Delete a deviceid from cache | |
148 | * | |
149 | * @clp struct nfs_client qualifying the deviceid | |
150 | * @id deviceid to delete | |
151 | */ | |
152 | void | |
153 | nfs4_delete_deviceid(const struct nfs_client *clp, const struct nfs4_deviceid *id) | |
154 | { | |
155 | struct nfs4_deviceid_node *d; | |
156 | ||
157 | d = nfs4_unhash_put_deviceid(clp, id); | |
158 | if (!d) | |
159 | return; | |
160 | d->ld->free_deviceid_node(d); | |
161 | } | |
162 | EXPORT_SYMBOL_GPL(nfs4_delete_deviceid); | |
a1eaecbc BH |
163 | |
164 | void | |
165 | nfs4_init_deviceid_node(struct nfs4_deviceid_node *d, | |
1775bc34 | 166 | const struct pnfs_layoutdriver_type *ld, |
a1eaecbc BH |
167 | const struct nfs_client *nfs_client, |
168 | const struct nfs4_deviceid *id) | |
169 | { | |
1775bc34 BH |
170 | INIT_HLIST_NODE(&d->node); |
171 | d->ld = ld; | |
a1eaecbc BH |
172 | d->nfs_client = nfs_client; |
173 | d->deviceid = *id; | |
1775bc34 | 174 | atomic_set(&d->ref, 1); |
a1eaecbc BH |
175 | } |
176 | EXPORT_SYMBOL_GPL(nfs4_init_deviceid_node); | |
177 | ||
178 | /* | |
179 | * Uniquely initialize and insert a deviceid node into cache | |
180 | * | |
181 | * @new new deviceid node | |
1775bc34 BH |
182 | * Note that the caller must set up the following members: |
183 | * new->ld | |
184 | * new->nfs_client | |
185 | * new->deviceid | |
a1eaecbc BH |
186 | * |
187 | * @ret the inserted node, if none found, otherwise, the found entry. | |
188 | */ | |
189 | struct nfs4_deviceid_node * | |
190 | nfs4_insert_deviceid_node(struct nfs4_deviceid_node *new) | |
191 | { | |
192 | struct nfs4_deviceid_node *d; | |
193 | long hash; | |
194 | ||
195 | spin_lock(&nfs4_deviceid_lock); | |
1be5683b ME |
196 | hash = nfs4_deviceid_hash(&new->deviceid); |
197 | d = _find_get_deviceid(new->nfs_client, &new->deviceid, hash); | |
a1eaecbc BH |
198 | if (d) { |
199 | spin_unlock(&nfs4_deviceid_lock); | |
200 | return d; | |
201 | } | |
202 | ||
a1eaecbc BH |
203 | hlist_add_head_rcu(&new->node, &nfs4_deviceid_cache[hash]); |
204 | spin_unlock(&nfs4_deviceid_lock); | |
205 | ||
206 | return new; | |
207 | } | |
208 | EXPORT_SYMBOL_GPL(nfs4_insert_deviceid_node); | |
209 | ||
210 | /* | |
211 | * Dereference a deviceid node and delete it when its reference count drops | |
212 | * to zero. | |
213 | * | |
214 | * @d deviceid node to put | |
215 | * | |
216 | * @ret true iff the node was deleted | |
217 | */ | |
218 | bool | |
219 | nfs4_put_deviceid_node(struct nfs4_deviceid_node *d) | |
220 | { | |
221 | if (!atomic_dec_and_lock(&d->ref, &nfs4_deviceid_lock)) | |
222 | return false; | |
223 | hlist_del_init_rcu(&d->node); | |
224 | spin_unlock(&nfs4_deviceid_lock); | |
225 | synchronize_rcu(); | |
1775bc34 | 226 | d->ld->free_deviceid_node(d); |
a1eaecbc BH |
227 | return true; |
228 | } | |
229 | EXPORT_SYMBOL_GPL(nfs4_put_deviceid_node); | |
1775bc34 BH |
230 | |
231 | static void | |
232 | _deviceid_purge_client(const struct nfs_client *clp, long hash) | |
233 | { | |
234 | struct nfs4_deviceid_node *d; | |
235 | struct hlist_node *n, *next; | |
236 | HLIST_HEAD(tmp); | |
237 | ||
238 | rcu_read_lock(); | |
239 | hlist_for_each_entry_rcu(d, n, &nfs4_deviceid_cache[hash], node) | |
240 | if (d->nfs_client == clp && atomic_read(&d->ref)) { | |
241 | hlist_del_init_rcu(&d->node); | |
242 | hlist_add_head(&d->node, &tmp); | |
243 | } | |
244 | rcu_read_unlock(); | |
245 | ||
246 | if (hlist_empty(&tmp)) | |
247 | return; | |
248 | ||
249 | synchronize_rcu(); | |
250 | hlist_for_each_entry_safe(d, n, next, &tmp, node) | |
251 | if (atomic_dec_and_test(&d->ref)) | |
252 | d->ld->free_deviceid_node(d); | |
253 | } | |
254 | ||
255 | void | |
256 | nfs4_deviceid_purge_client(const struct nfs_client *clp) | |
257 | { | |
258 | long h; | |
259 | ||
260 | spin_lock(&nfs4_deviceid_lock); | |
261 | for (h = 0; h < NFS4_DEVICE_ID_HASH_SIZE; h++) | |
262 | _deviceid_purge_client(clp, h); | |
263 | spin_unlock(&nfs4_deviceid_lock); | |
264 | } |