diff options
author | Marc Eshel <eshel@almaden.ibm.com> | 2011-05-22 12:47:09 -0400 |
---|---|---|
committer | Boaz Harrosh <bharrosh@panasas.com> | 2011-05-29 13:52:31 -0400 |
commit | 1be5683b03a766670b3b629bf6bfeab3ca9239d8 (patch) | |
tree | 613f4c0dea8b0d8447a3158b82b3e2046ee9dcb5 /fs/nfs | |
parent | 1775bc342c6eacd6304493cbb2e0cda1a0182246 (diff) |
pnfs: CB_NOTIFY_DEVICEID
Note: This functionlaity is incomplete as all layout segments referring to
the 'to be removed device id' need to be reaped, and all in flight I/O drained.
[use be32 res in nfs4_callback_devicenotify]
[use nfs_client to qualify deviceid for cb_notify_deviceid]
[use global deviceid cache for CB_NOTIFY_DEVICEID]
[refactor device cache _lookup_deviceid]
[refactor device cache _find_get_deviceid]
Signed-off-by: Benny Halevy <bhalevy@panasas.com>
[Bug in new global-device-cache code]
[layout_driver MUST set free_deviceid_node if using dev-cache]
Signed-off-by: Boaz Harrosh <bharrosh@panasas.com>
Signed-off-by: Benny Halevy <bhalevy@panasas.com>
Diffstat (limited to 'fs/nfs')
-rw-r--r-- | fs/nfs/callback.h | 17 | ||||
-rw-r--r-- | fs/nfs/callback_proc.c | 47 | ||||
-rw-r--r-- | fs/nfs/callback_xdr.c | 96 | ||||
-rw-r--r-- | fs/nfs/pnfs.h | 1 | ||||
-rw-r--r-- | fs/nfs/pnfs_dev.c | 95 |
5 files changed, 241 insertions, 15 deletions
diff --git a/fs/nfs/callback.h b/fs/nfs/callback.h index 46d93ce7311b..b257383bb565 100644 --- a/fs/nfs/callback.h +++ b/fs/nfs/callback.h | |||
@@ -167,6 +167,23 @@ extern unsigned nfs4_callback_layoutrecall( | |||
167 | 167 | ||
168 | extern void nfs4_check_drain_bc_complete(struct nfs4_session *ses); | 168 | extern void nfs4_check_drain_bc_complete(struct nfs4_session *ses); |
169 | extern void nfs4_cb_take_slot(struct nfs_client *clp); | 169 | extern void nfs4_cb_take_slot(struct nfs_client *clp); |
170 | |||
171 | struct cb_devicenotifyitem { | ||
172 | uint32_t cbd_notify_type; | ||
173 | uint32_t cbd_layout_type; | ||
174 | struct nfs4_deviceid cbd_dev_id; | ||
175 | uint32_t cbd_immediate; | ||
176 | }; | ||
177 | |||
178 | struct cb_devicenotifyargs { | ||
179 | int ndevs; | ||
180 | struct cb_devicenotifyitem *devs; | ||
181 | }; | ||
182 | |||
183 | extern __be32 nfs4_callback_devicenotify( | ||
184 | struct cb_devicenotifyargs *args, | ||
185 | void *dummy, struct cb_process_state *cps); | ||
186 | |||
170 | #endif /* CONFIG_NFS_V4_1 */ | 187 | #endif /* CONFIG_NFS_V4_1 */ |
171 | extern int check_gss_callback_principal(struct nfs_client *, struct svc_rqst *); | 188 | extern int check_gss_callback_principal(struct nfs_client *, struct svc_rqst *); |
172 | extern __be32 nfs4_callback_getattr(struct cb_getattrargs *args, | 189 | extern __be32 nfs4_callback_getattr(struct cb_getattrargs *args, |
diff --git a/fs/nfs/callback_proc.c b/fs/nfs/callback_proc.c index 2f41dccea18e..fb5e5b9a97ae 100644 --- a/fs/nfs/callback_proc.c +++ b/fs/nfs/callback_proc.c | |||
@@ -241,6 +241,53 @@ static void pnfs_recall_all_layouts(struct nfs_client *clp) | |||
241 | do_callback_layoutrecall(clp, &args); | 241 | do_callback_layoutrecall(clp, &args); |
242 | } | 242 | } |
243 | 243 | ||
244 | __be32 nfs4_callback_devicenotify(struct cb_devicenotifyargs *args, | ||
245 | void *dummy, struct cb_process_state *cps) | ||
246 | { | ||
247 | int i; | ||
248 | __be32 res = 0; | ||
249 | struct nfs_client *clp = cps->clp; | ||
250 | struct nfs_server *server = NULL; | ||
251 | |||
252 | dprintk("%s: -->\n", __func__); | ||
253 | |||
254 | if (!clp) { | ||
255 | res = cpu_to_be32(NFS4ERR_OP_NOT_IN_SESSION); | ||
256 | goto out; | ||
257 | } | ||
258 | |||
259 | for (i = 0; i < args->ndevs; i++) { | ||
260 | struct cb_devicenotifyitem *dev = &args->devs[i]; | ||
261 | |||
262 | if (!server || | ||
263 | server->pnfs_curr_ld->id != dev->cbd_layout_type) { | ||
264 | rcu_read_lock(); | ||
265 | list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) | ||
266 | if (server->pnfs_curr_ld && | ||
267 | server->pnfs_curr_ld->id == dev->cbd_layout_type) { | ||
268 | rcu_read_unlock(); | ||
269 | goto found; | ||
270 | } | ||
271 | rcu_read_unlock(); | ||
272 | dprintk("%s: layout type %u not found\n", | ||
273 | __func__, dev->cbd_layout_type); | ||
274 | continue; | ||
275 | } | ||
276 | |||
277 | found: | ||
278 | if (dev->cbd_notify_type == NOTIFY_DEVICEID4_CHANGE) | ||
279 | dprintk("%s: NOTIFY_DEVICEID4_CHANGE not supported, " | ||
280 | "deleting instead\n", __func__); | ||
281 | nfs4_delete_deviceid(clp, &dev->cbd_dev_id); | ||
282 | } | ||
283 | |||
284 | out: | ||
285 | kfree(args->devs); | ||
286 | dprintk("%s: exit with status = %u\n", | ||
287 | __func__, be32_to_cpu(res)); | ||
288 | return res; | ||
289 | } | ||
290 | |||
244 | int nfs41_validate_delegation_stateid(struct nfs_delegation *delegation, const nfs4_stateid *stateid) | 291 | int nfs41_validate_delegation_stateid(struct nfs_delegation *delegation, const nfs4_stateid *stateid) |
245 | { | 292 | { |
246 | if (delegation == NULL) | 293 | if (delegation == NULL) |
diff --git a/fs/nfs/callback_xdr.c b/fs/nfs/callback_xdr.c index 00ecf62ce7c1..c6c86a77e043 100644 --- a/fs/nfs/callback_xdr.c +++ b/fs/nfs/callback_xdr.c | |||
@@ -25,6 +25,7 @@ | |||
25 | 25 | ||
26 | #if defined(CONFIG_NFS_V4_1) | 26 | #if defined(CONFIG_NFS_V4_1) |
27 | #define CB_OP_LAYOUTRECALL_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ) | 27 | #define CB_OP_LAYOUTRECALL_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ) |
28 | #define CB_OP_DEVICENOTIFY_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ) | ||
28 | #define CB_OP_SEQUENCE_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ + \ | 29 | #define CB_OP_SEQUENCE_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ + \ |
29 | 4 + 1 + 3) | 30 | 4 + 1 + 3) |
30 | #define CB_OP_RECALLANY_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ) | 31 | #define CB_OP_RECALLANY_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ) |
@@ -284,6 +285,93 @@ out: | |||
284 | return status; | 285 | return status; |
285 | } | 286 | } |
286 | 287 | ||
288 | static | ||
289 | __be32 decode_devicenotify_args(struct svc_rqst *rqstp, | ||
290 | struct xdr_stream *xdr, | ||
291 | struct cb_devicenotifyargs *args) | ||
292 | { | ||
293 | __be32 *p; | ||
294 | __be32 status = 0; | ||
295 | u32 tmp; | ||
296 | int n, i; | ||
297 | args->ndevs = 0; | ||
298 | |||
299 | /* Num of device notifications */ | ||
300 | p = read_buf(xdr, sizeof(uint32_t)); | ||
301 | if (unlikely(p == NULL)) { | ||
302 | status = htonl(NFS4ERR_BADXDR); | ||
303 | goto out; | ||
304 | } | ||
305 | n = ntohl(*p++); | ||
306 | if (n <= 0) | ||
307 | goto out; | ||
308 | |||
309 | args->devs = kmalloc(n * sizeof(*args->devs), GFP_KERNEL); | ||
310 | if (!args->devs) { | ||
311 | status = htonl(NFS4ERR_DELAY); | ||
312 | goto out; | ||
313 | } | ||
314 | |||
315 | /* Decode each dev notification */ | ||
316 | for (i = 0; i < n; i++) { | ||
317 | struct cb_devicenotifyitem *dev = &args->devs[i]; | ||
318 | |||
319 | p = read_buf(xdr, (4 * sizeof(uint32_t)) + NFS4_DEVICEID4_SIZE); | ||
320 | if (unlikely(p == NULL)) { | ||
321 | status = htonl(NFS4ERR_BADXDR); | ||
322 | goto err; | ||
323 | } | ||
324 | |||
325 | tmp = ntohl(*p++); /* bitmap size */ | ||
326 | if (tmp != 1) { | ||
327 | status = htonl(NFS4ERR_INVAL); | ||
328 | goto err; | ||
329 | } | ||
330 | dev->cbd_notify_type = ntohl(*p++); | ||
331 | if (dev->cbd_notify_type != NOTIFY_DEVICEID4_CHANGE && | ||
332 | dev->cbd_notify_type != NOTIFY_DEVICEID4_DELETE) { | ||
333 | status = htonl(NFS4ERR_INVAL); | ||
334 | goto err; | ||
335 | } | ||
336 | |||
337 | tmp = ntohl(*p++); /* opaque size */ | ||
338 | if (((dev->cbd_notify_type == NOTIFY_DEVICEID4_CHANGE) && | ||
339 | (tmp != NFS4_DEVICEID4_SIZE + 8)) || | ||
340 | ((dev->cbd_notify_type == NOTIFY_DEVICEID4_DELETE) && | ||
341 | (tmp != NFS4_DEVICEID4_SIZE + 4))) { | ||
342 | status = htonl(NFS4ERR_INVAL); | ||
343 | goto err; | ||
344 | } | ||
345 | dev->cbd_layout_type = ntohl(*p++); | ||
346 | memcpy(dev->cbd_dev_id.data, p, NFS4_DEVICEID4_SIZE); | ||
347 | p += XDR_QUADLEN(NFS4_DEVICEID4_SIZE); | ||
348 | |||
349 | if (dev->cbd_layout_type == NOTIFY_DEVICEID4_CHANGE) { | ||
350 | p = read_buf(xdr, sizeof(uint32_t)); | ||
351 | if (unlikely(p == NULL)) { | ||
352 | status = htonl(NFS4ERR_BADXDR); | ||
353 | goto err; | ||
354 | } | ||
355 | dev->cbd_immediate = ntohl(*p++); | ||
356 | } else { | ||
357 | dev->cbd_immediate = 0; | ||
358 | } | ||
359 | |||
360 | args->ndevs++; | ||
361 | |||
362 | dprintk("%s: type %d layout 0x%x immediate %d\n", | ||
363 | __func__, dev->cbd_notify_type, dev->cbd_layout_type, | ||
364 | dev->cbd_immediate); | ||
365 | } | ||
366 | out: | ||
367 | dprintk("%s: status %d ndevs %d\n", | ||
368 | __func__, ntohl(status), args->ndevs); | ||
369 | return status; | ||
370 | err: | ||
371 | kfree(args->devs); | ||
372 | goto out; | ||
373 | } | ||
374 | |||
287 | static __be32 decode_sessionid(struct xdr_stream *xdr, | 375 | static __be32 decode_sessionid(struct xdr_stream *xdr, |
288 | struct nfs4_sessionid *sid) | 376 | struct nfs4_sessionid *sid) |
289 | { | 377 | { |
@@ -639,10 +727,10 @@ preprocess_nfs41_op(int nop, unsigned int op_nr, struct callback_op **op) | |||
639 | case OP_CB_RECALL_ANY: | 727 | case OP_CB_RECALL_ANY: |
640 | case OP_CB_RECALL_SLOT: | 728 | case OP_CB_RECALL_SLOT: |
641 | case OP_CB_LAYOUTRECALL: | 729 | case OP_CB_LAYOUTRECALL: |
730 | case OP_CB_NOTIFY_DEVICEID: | ||
642 | *op = &callback_ops[op_nr]; | 731 | *op = &callback_ops[op_nr]; |
643 | break; | 732 | break; |
644 | 733 | ||
645 | case OP_CB_NOTIFY_DEVICEID: | ||
646 | case OP_CB_NOTIFY: | 734 | case OP_CB_NOTIFY: |
647 | case OP_CB_PUSH_DELEG: | 735 | case OP_CB_PUSH_DELEG: |
648 | case OP_CB_RECALLABLE_OBJ_AVAIL: | 736 | case OP_CB_RECALLABLE_OBJ_AVAIL: |
@@ -849,6 +937,12 @@ static struct callback_op callback_ops[] = { | |||
849 | (callback_decode_arg_t)decode_layoutrecall_args, | 937 | (callback_decode_arg_t)decode_layoutrecall_args, |
850 | .res_maxsize = CB_OP_LAYOUTRECALL_RES_MAXSZ, | 938 | .res_maxsize = CB_OP_LAYOUTRECALL_RES_MAXSZ, |
851 | }, | 939 | }, |
940 | [OP_CB_NOTIFY_DEVICEID] = { | ||
941 | .process_op = (callback_process_op_t)nfs4_callback_devicenotify, | ||
942 | .decode_args = | ||
943 | (callback_decode_arg_t)decode_devicenotify_args, | ||
944 | .res_maxsize = CB_OP_DEVICENOTIFY_RES_MAXSZ, | ||
945 | }, | ||
852 | [OP_CB_SEQUENCE] = { | 946 | [OP_CB_SEQUENCE] = { |
853 | .process_op = (callback_process_op_t)nfs4_callback_sequence, | 947 | .process_op = (callback_process_op_t)nfs4_callback_sequence, |
854 | .decode_args = (callback_decode_arg_t)decode_cb_sequence_args, | 948 | .decode_args = (callback_decode_arg_t)decode_cb_sequence_args, |
diff --git a/fs/nfs/pnfs.h b/fs/nfs/pnfs.h index 80a5d0e2cc43..fbd3f7cd9e71 100644 --- a/fs/nfs/pnfs.h +++ b/fs/nfs/pnfs.h | |||
@@ -173,6 +173,7 @@ struct nfs4_deviceid_node { | |||
173 | void nfs4_print_deviceid(const struct nfs4_deviceid *dev_id); | 173 | void nfs4_print_deviceid(const struct nfs4_deviceid *dev_id); |
174 | struct nfs4_deviceid_node *nfs4_find_get_deviceid(const struct nfs_client *, const struct nfs4_deviceid *); | 174 | struct nfs4_deviceid_node *nfs4_find_get_deviceid(const struct nfs_client *, const struct nfs4_deviceid *); |
175 | struct nfs4_deviceid_node *nfs4_unhash_put_deviceid(const struct nfs_client *, const struct nfs4_deviceid *); | 175 | struct nfs4_deviceid_node *nfs4_unhash_put_deviceid(const struct nfs_client *, const struct nfs4_deviceid *); |
176 | void nfs4_delete_deviceid(const struct nfs_client *, const struct nfs4_deviceid *); | ||
176 | void nfs4_init_deviceid_node(struct nfs4_deviceid_node *, | 177 | void nfs4_init_deviceid_node(struct nfs4_deviceid_node *, |
177 | const struct pnfs_layoutdriver_type *, | 178 | const struct pnfs_layoutdriver_type *, |
178 | const struct nfs_client *, | 179 | const struct nfs_client *, |
diff --git a/fs/nfs/pnfs_dev.c b/fs/nfs/pnfs_dev.c index 64a4b85c7dbc..8fd3839df299 100644 --- a/fs/nfs/pnfs_dev.c +++ b/fs/nfs/pnfs_dev.c | |||
@@ -66,6 +66,23 @@ nfs4_deviceid_hash(const struct nfs4_deviceid *id) | |||
66 | return x & NFS4_DEVICE_ID_HASH_MASK; | 66 | return x & NFS4_DEVICE_ID_HASH_MASK; |
67 | } | 67 | } |
68 | 68 | ||
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 | |||
69 | /* | 86 | /* |
70 | * Lookup a deviceid in cache and get a reference count on it if found | 87 | * Lookup a deviceid in cache and get a reference count on it if found |
71 | * | 88 | * |
@@ -73,26 +90,76 @@ nfs4_deviceid_hash(const struct nfs4_deviceid *id) | |||
73 | * @id deviceid to look up | 90 | * @id deviceid to look up |
74 | */ | 91 | */ |
75 | struct nfs4_deviceid_node * | 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 | |||
106 | struct nfs4_deviceid_node * | ||
76 | nfs4_find_get_deviceid(const struct nfs_client *clp, const struct nfs4_deviceid *id) | 107 | nfs4_find_get_deviceid(const struct nfs_client *clp, const struct nfs4_deviceid *id) |
77 | { | 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) | ||
123 | { | ||
78 | struct nfs4_deviceid_node *d; | 124 | struct nfs4_deviceid_node *d; |
79 | struct hlist_node *n; | ||
80 | long hash = nfs4_deviceid_hash(id); | ||
81 | 125 | ||
126 | spin_lock(&nfs4_deviceid_lock); | ||
82 | rcu_read_lock(); | 127 | rcu_read_lock(); |
83 | hlist_for_each_entry_rcu(d, n, &nfs4_deviceid_cache[hash], node) { | 128 | d = _lookup_deviceid(clp, id, nfs4_deviceid_hash(id)); |
84 | if (d->nfs_client == clp && !memcmp(&d->deviceid, id, sizeof(*id))) { | ||
85 | if (!atomic_inc_not_zero(&d->ref)) | ||
86 | goto fail; | ||
87 | rcu_read_unlock(); | ||
88 | return d; | ||
89 | } | ||
90 | } | ||
91 | fail: | ||
92 | rcu_read_unlock(); | 129 | rcu_read_unlock(); |
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 | |||
93 | return NULL; | 142 | return NULL; |
94 | } | 143 | } |
95 | EXPORT_SYMBOL_GPL(nfs4_find_get_deviceid); | 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); | ||
96 | 163 | ||
97 | void | 164 | void |
98 | nfs4_init_deviceid_node(struct nfs4_deviceid_node *d, | 165 | nfs4_init_deviceid_node(struct nfs4_deviceid_node *d, |
@@ -126,13 +193,13 @@ nfs4_insert_deviceid_node(struct nfs4_deviceid_node *new) | |||
126 | long hash; | 193 | long hash; |
127 | 194 | ||
128 | spin_lock(&nfs4_deviceid_lock); | 195 | spin_lock(&nfs4_deviceid_lock); |
129 | d = nfs4_find_get_deviceid(new->nfs_client, &new->deviceid); | 196 | hash = nfs4_deviceid_hash(&new->deviceid); |
197 | d = _find_get_deviceid(new->nfs_client, &new->deviceid, hash); | ||
130 | if (d) { | 198 | if (d) { |
131 | spin_unlock(&nfs4_deviceid_lock); | 199 | spin_unlock(&nfs4_deviceid_lock); |
132 | return d; | 200 | return d; |
133 | } | 201 | } |
134 | 202 | ||
135 | hash = nfs4_deviceid_hash(&new->deviceid); | ||
136 | hlist_add_head_rcu(&new->node, &nfs4_deviceid_cache[hash]); | 203 | hlist_add_head_rcu(&new->node, &nfs4_deviceid_cache[hash]); |
137 | spin_unlock(&nfs4_deviceid_lock); | 204 | spin_unlock(&nfs4_deviceid_lock); |
138 | 205 | ||