author | jpk |
Fri, 24 Mar 2006 12:29:20 -0800 | |
changeset 1676 | 37f4a3e2bd99 |
parent 1665 | c2118a3da4a0 |
child 4033 | ce32e5a0eea0 |
permissions | -rw-r--r-- |
0 | 1 |
/* |
2 |
* CDDL HEADER START |
|
3 |
* |
|
4 |
* The contents of this file are subject to the terms of the |
|
1649
be48c32b76e8
6378538 nfsv4 client hung waiting for nfs4_server_lst_lock
dm120769
parents:
1232
diff
changeset
|
5 |
* Common Development and Distribution License (the "License"). |
be48c32b76e8
6378538 nfsv4 client hung waiting for nfs4_server_lst_lock
dm120769
parents:
1232
diff
changeset
|
6 |
* You may not use this file except in compliance with the License. |
0 | 7 |
* |
8 |
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE |
|
9 |
* or http://www.opensolaris.org/os/licensing. |
|
10 |
* See the License for the specific language governing permissions |
|
11 |
* and limitations under the License. |
|
12 |
* |
|
13 |
* When distributing Covered Code, include this CDDL HEADER in each |
|
14 |
* file and include the License file at usr/src/OPENSOLARIS.LICENSE. |
|
15 |
* If applicable, add the following below this CDDL HEADER, with the |
|
16 |
* fields enclosed by brackets "[]" replaced with your own identifying |
|
17 |
* information: Portions Copyright [yyyy] [name of copyright owner] |
|
18 |
* |
|
19 |
* CDDL HEADER END |
|
20 |
*/ |
|
21 |
/* |
|
1232 | 22 |
* Copyright 2006 Sun Microsystems, Inc. All rights reserved. |
0 | 23 |
* Use is subject to license terms. |
24 |
*/ |
|
25 |
||
26 |
/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ |
|
27 |
/* All Rights Reserved */ |
|
28 |
||
29 |
#pragma ident "%Z%%M% %I% %E% SMI" |
|
30 |
||
31 |
#include <sys/param.h> |
|
32 |
#include <sys/types.h> |
|
33 |
#include <sys/systm.h> |
|
34 |
#include <sys/cred.h> |
|
35 |
#include <sys/vfs.h> |
|
36 |
#include <sys/vnode.h> |
|
37 |
#include <sys/pathname.h> |
|
38 |
#include <sys/sysmacros.h> |
|
39 |
#include <sys/kmem.h> |
|
40 |
#include <sys/kstat.h> |
|
41 |
#include <sys/mkdev.h> |
|
42 |
#include <sys/mount.h> |
|
43 |
#include <sys/statvfs.h> |
|
44 |
#include <sys/errno.h> |
|
45 |
#include <sys/debug.h> |
|
46 |
#include <sys/cmn_err.h> |
|
47 |
#include <sys/utsname.h> |
|
48 |
#include <sys/bootconf.h> |
|
49 |
#include <sys/modctl.h> |
|
50 |
#include <sys/acl.h> |
|
51 |
#include <sys/flock.h> |
|
52 |
#include <sys/kstr.h> |
|
53 |
#include <sys/stropts.h> |
|
54 |
#include <sys/strsubr.h> |
|
55 |
#include <sys/atomic.h> |
|
56 |
#include <sys/disp.h> |
|
57 |
#include <sys/policy.h> |
|
58 |
#include <sys/list.h> |
|
59 |
#include <sys/zone.h> |
|
60 |
||
61 |
#include <rpc/types.h> |
|
62 |
#include <rpc/auth.h> |
|
63 |
#include <rpc/rpcsec_gss.h> |
|
64 |
#include <rpc/clnt.h> |
|
65 |
#include <rpc/xdr.h> |
|
66 |
||
67 |
#include <nfs/nfs.h> |
|
68 |
#include <nfs/nfs_clnt.h> |
|
69 |
#include <nfs/mount.h> |
|
70 |
#include <nfs/nfs_acl.h> |
|
71 |
||
72 |
#include <fs/fs_subr.h> |
|
73 |
||
74 |
#include <nfs/nfs4.h> |
|
75 |
#include <nfs/rnode4.h> |
|
76 |
#include <nfs/nfs4_clnt.h> |
|
77 |
#include <nfs/nfssys.h> |
|
78 |
||
79 |
#ifdef DEBUG |
|
80 |
/* |
|
81 |
* These are "special" state IDs and file handles that |
|
82 |
* match any delegation state ID or file handled. This |
|
83 |
* is for testing purposes only. |
|
84 |
*/ |
|
85 |
||
86 |
stateid4 nfs4_deleg_any = { 0x7FFFFFF0 }; |
|
87 |
char nfs4_deleg_fh[] = "\0377\0376\0375\0374"; |
|
88 |
nfs_fh4 nfs4_deleg_anyfh = { sizeof (nfs4_deleg_fh)-1, nfs4_deleg_fh }; |
|
89 |
int nfs4_deleg_accept_phony = OPEN_DELEGATE_NONE; |
|
90 |
nfsace4 nfs4_deleg_ace_phony; |
|
91 |
nfs_space_limit4 nfs4_deleg_space_phony = { NFS_LIMIT_SIZE, 8192 }; |
|
92 |
nfs_space_limit4 nfs4_deleg_space_phony2 = { NFS_LIMIT_BLOCKS, 0 }; |
|
93 |
nfs_modified_limit4 nfs4_deleg_space_phonyl = { 8, 512 }; |
|
94 |
changeid4 nfs4_deleg_change_phony = 0x7eeeeeee76666660LL; |
|
95 |
int nfs4_use_phony_limit; |
|
96 |
int nfs4_use_phony_recall; |
|
97 |
int nfs4_phony_recall_v; |
|
98 |
nfsstat4 cb4_getattr_fail = NFS4_OK; |
|
99 |
nfsstat4 cb4_recall_fail = NFS4_OK; |
|
100 |
||
101 |
int nfs4_callback_debug; |
|
102 |
int nfs4_recall_debug; |
|
103 |
int nfs4_drat_debug; |
|
104 |
||
105 |
#endif |
|
106 |
||
107 |
#define CB_NOTE(x) NFS4_DEBUG(nfs4_callback_debug, (CE_NOTE, x)) |
|
108 |
#define CB_WARN(x) NFS4_DEBUG(nfs4_callback_debug, (CE_WARN, x)) |
|
109 |
#define CB_WARN1(x, y) NFS4_DEBUG(nfs4_callback_debug, (CE_WARN, x, y)) |
|
110 |
||
111 |
enum nfs4_delegreturn_policy nfs4_delegreturn_policy = INACTIVE; |
|
112 |
||
113 |
static zone_key_t nfs4_callback_zone_key; |
|
114 |
||
115 |
/* |
|
116 |
* NFS4_MAPSIZE is the number of bytes we are willing to consume |
|
117 |
* for the block allocation map when the server grants a NFS_LIMIT_BLOCK |
|
118 |
* style delegation. |
|
119 |
*/ |
|
120 |
||
121 |
#define NFS4_MAPSIZE 8192 |
|
122 |
#define NFS4_MAPWORDS NFS4_MAPSIZE/sizeof (uint_t) |
|
123 |
#define NbPW (NBBY*sizeof (uint_t)) |
|
124 |
||
125 |
static int nfs4_num_prognums = 1024; |
|
126 |
static SVC_CALLOUT_TABLE nfs4_cb_sct; |
|
127 |
||
128 |
struct nfs4_dnode { |
|
129 |
list_node_t linkage; |
|
130 |
rnode4_t *rnodep; |
|
131 |
int flags; /* Flags for nfs4delegreturn_impl() */ |
|
132 |
}; |
|
133 |
||
134 |
static const struct nfs4_callback_stats nfs4_callback_stats_tmpl = { |
|
135 |
{ "delegations", KSTAT_DATA_UINT64 }, |
|
136 |
{ "cb_getattr", KSTAT_DATA_UINT64 }, |
|
137 |
{ "cb_recall", KSTAT_DATA_UINT64 }, |
|
138 |
{ "cb_null", KSTAT_DATA_UINT64 }, |
|
139 |
{ "cb_dispatch", KSTAT_DATA_UINT64 }, |
|
140 |
{ "delegaccept_r", KSTAT_DATA_UINT64 }, |
|
141 |
{ "delegaccept_rw", KSTAT_DATA_UINT64 }, |
|
142 |
{ "delegreturn", KSTAT_DATA_UINT64 }, |
|
143 |
{ "callbacks", KSTAT_DATA_UINT64 }, |
|
144 |
{ "claim_cur", KSTAT_DATA_UINT64 }, |
|
145 |
{ "claim_cur_ok", KSTAT_DATA_UINT64 }, |
|
146 |
{ "recall_trunc", KSTAT_DATA_UINT64 }, |
|
147 |
{ "recall_failed", KSTAT_DATA_UINT64 }, |
|
148 |
{ "return_limit_write", KSTAT_DATA_UINT64 }, |
|
149 |
{ "return_limit_addmap", KSTAT_DATA_UINT64 }, |
|
150 |
{ "deleg_recover", KSTAT_DATA_UINT64 }, |
|
151 |
{ "cb_illegal", KSTAT_DATA_UINT64 } |
|
152 |
}; |
|
153 |
||
154 |
struct nfs4_cb_port { |
|
155 |
list_node_t linkage; /* linkage into per-zone port list */ |
|
156 |
char netid[KNC_STRSIZE]; |
|
157 |
char uaddr[KNC_STRSIZE]; |
|
158 |
char protofmly[KNC_STRSIZE]; |
|
159 |
char proto[KNC_STRSIZE]; |
|
160 |
}; |
|
161 |
||
162 |
static int cb_getattr_bytes; |
|
163 |
||
164 |
struct cb_recall_pass { |
|
165 |
rnode4_t *rp; |
|
166 |
int flags; /* Flags for nfs4delegreturn_impl() */ |
|
167 |
bool_t truncate; |
|
168 |
}; |
|
169 |
||
170 |
static nfs4_open_stream_t *get_next_deleg_stream(rnode4_t *, int); |
|
171 |
static void nfs4delegreturn_thread(struct cb_recall_pass *); |
|
172 |
static int deleg_reopen(vnode_t *, bool_t *, struct nfs4_callback_globals *, |
|
173 |
int); |
|
174 |
static void nfs4_dlistadd(rnode4_t *, struct nfs4_callback_globals *, int); |
|
175 |
static void nfs4_dlistclean_impl(struct nfs4_callback_globals *, int); |
|
176 |
static int nfs4delegreturn_impl(rnode4_t *, int, |
|
177 |
struct nfs4_callback_globals *); |
|
178 |
static void nfs4delegreturn_cleanup_impl(rnode4_t *, nfs4_server_t *, |
|
179 |
struct nfs4_callback_globals *); |
|
180 |
||
181 |
static void |
|
182 |
cb_getattr(nfs_cb_argop4 *argop, nfs_cb_resop4 *resop, struct svc_req *req, |
|
183 |
struct compound_state *cs, struct nfs4_callback_globals *ncg) |
|
184 |
{ |
|
185 |
CB_GETATTR4args *args = &argop->nfs_cb_argop4_u.opcbgetattr; |
|
186 |
CB_GETATTR4res *resp = &resop->nfs_cb_resop4_u.opcbgetattr; |
|
187 |
rnode4_t *rp; |
|
188 |
vnode_t *vp; |
|
189 |
bool_t found = FALSE; |
|
190 |
struct nfs4_server *sp; |
|
191 |
struct fattr4 *fap; |
|
1232 | 192 |
rpc_inline_t *fdata; |
0 | 193 |
long mapcnt; |
194 |
fattr4_change change; |
|
195 |
fattr4_size size; |
|
196 |
uint_t rflag; |
|
197 |
||
198 |
ncg->nfs4_callback_stats.cb_getattr.value.ui64++; |
|
199 |
||
200 |
#ifdef DEBUG |
|
201 |
/* |
|
202 |
* error injection hook: set cb_getattr_fail global to |
|
203 |
* NFS4 pcol error to be returned |
|
204 |
*/ |
|
205 |
if (cb4_getattr_fail != NFS4_OK) { |
|
206 |
*cs->statusp = resp->status = cb4_getattr_fail; |
|
207 |
return; |
|
208 |
} |
|
209 |
#endif |
|
210 |
||
211 |
resp->obj_attributes.attrmask = 0; |
|
212 |
||
213 |
mutex_enter(&ncg->nfs4_cb_lock); |
|
214 |
sp = ncg->nfs4prog2server[req->rq_prog - NFS4_CALLBACK]; |
|
215 |
mutex_exit(&ncg->nfs4_cb_lock); |
|
216 |
||
217 |
if (nfs4_server_vlock(sp, 0) == FALSE) { |
|
218 |
||
219 |
CB_WARN("cb_getattr: cannot find server\n"); |
|
220 |
||
221 |
*cs->statusp = resp->status = NFS4ERR_BADHANDLE; |
|
222 |
return; |
|
223 |
} |
|
224 |
||
225 |
/* |
|
226 |
* In cb_compound, callback_ident was validated against rq_prog, |
|
227 |
* but we couldn't verify that it was set to the value we provided |
|
228 |
* at setclientid time (because we didn't have server struct yet). |
|
229 |
* Now we have the server struct, but don't have callback_ident |
|
230 |
* handy. So, validate server struct program number against req |
|
231 |
* RPC's prog number. At this point, we know the RPC prog num |
|
232 |
* is valid (else we wouldn't be here); however, we don't know |
|
233 |
* that it was the prog number we supplied to this server at |
|
234 |
* setclientid time. If the prog numbers aren't equivalent, then |
|
235 |
* log the problem and fail the request because either cbserv |
|
236 |
* and/or cbclient are confused. This will probably never happen. |
|
237 |
*/ |
|
238 |
if (sp->s_program != req->rq_prog) { |
|
239 |
#ifdef DEBUG |
|
240 |
zcmn_err(getzoneid(), CE_WARN, |
|
241 |
"cb_getattr: wrong server program number srv=%d req=%d\n", |
|
242 |
sp->s_program, req->rq_prog); |
|
243 |
#else |
|
244 |
zcmn_err(getzoneid(), CE_WARN, |
|
245 |
"cb_getattr: wrong server program number\n"); |
|
246 |
#endif |
|
247 |
mutex_exit(&sp->s_lock); |
|
248 |
nfs4_server_rele(sp); |
|
249 |
*cs->statusp = resp->status = NFS4ERR_BADHANDLE; |
|
250 |
return; |
|
251 |
} |
|
252 |
||
253 |
/* |
|
254 |
* Search the delegation list for a matching file handle; |
|
255 |
* mutex on sp prevents the list from changing. |
|
256 |
*/ |
|
257 |
||
258 |
rp = list_head(&sp->s_deleg_list); |
|
259 |
for (; rp != NULL; rp = list_next(&sp->s_deleg_list, rp)) { |
|
260 |
nfs4_fhandle_t fhandle; |
|
261 |
||
262 |
sfh4_copyval(rp->r_fh, &fhandle); |
|
263 |
||
264 |
if ((fhandle.fh_len == args->fh.nfs_fh4_len && |
|
265 |
bcmp(fhandle.fh_buf, args->fh.nfs_fh4_val, |
|
266 |
fhandle.fh_len) == 0)) { |
|
267 |
||
268 |
found = TRUE; |
|
269 |
break; |
|
270 |
} |
|
271 |
#ifdef DEBUG |
|
272 |
if (nfs4_deleg_anyfh.nfs_fh4_len == args->fh.nfs_fh4_len && |
|
273 |
bcmp(nfs4_deleg_anyfh.nfs_fh4_val, args->fh.nfs_fh4_val, |
|
274 |
args->fh.nfs_fh4_len) == 0) { |
|
275 |
||
276 |
found = TRUE; |
|
277 |
break; |
|
278 |
} |
|
279 |
#endif |
|
280 |
} |
|
281 |
||
282 |
/* |
|
283 |
* VN_HOLD the vnode before releasing s_lock to guarantee |
|
284 |
* we have a valid vnode reference. |
|
285 |
*/ |
|
286 |
if (found == TRUE) { |
|
287 |
vp = RTOV4(rp); |
|
288 |
VN_HOLD(vp); |
|
289 |
} |
|
290 |
||
291 |
mutex_exit(&sp->s_lock); |
|
292 |
nfs4_server_rele(sp); |
|
293 |
||
294 |
if (found == FALSE) { |
|
295 |
||
296 |
CB_WARN("cb_getattr: bad fhandle\n"); |
|
297 |
||
298 |
*cs->statusp = resp->status = NFS4ERR_BADHANDLE; |
|
299 |
return; |
|
300 |
} |
|
301 |
||
302 |
/* |
|
303 |
* Figure out which attributes the server wants. We only |
|
304 |
* offer FATTR4_CHANGE & FATTR4_SIZE; ignore the rest. |
|
305 |
*/ |
|
306 |
fdata = kmem_alloc(cb_getattr_bytes, KM_SLEEP); |
|
307 |
||
308 |
/* |
|
309 |
* Don't actually need to create XDR to encode these |
|
310 |
* simple data structures. |
|
311 |
* xdrmem_create(&xdr, fdata, cb_getattr_bytes, XDR_ENCODE); |
|
312 |
*/ |
|
313 |
fap = &resp->obj_attributes; |
|
314 |
||
315 |
fap->attrmask = 0; |
|
316 |
/* attrlist4_len starts at 0 and increases as attrs are processed */ |
|
1232 | 317 |
fap->attrlist4 = (char *)fdata; |
0 | 318 |
fap->attrlist4_len = 0; |
319 |
||
320 |
/* don't supply attrs if request was zero */ |
|
321 |
if (args->attr_request != 0) { |
|
322 |
if (args->attr_request & FATTR4_CHANGE_MASK) { |
|
323 |
/* |
|
324 |
* If the file is mmapped, then increment the change |
|
325 |
* attribute and return it. This will guarantee that |
|
326 |
* the server will perceive that the file has changed |
|
327 |
* if there is any chance that the client application |
|
328 |
* has changed it. Otherwise, just return the change |
|
329 |
* attribute as it has been updated by nfs4write_deleg. |
|
330 |
*/ |
|
331 |
||
332 |
mutex_enter(&rp->r_statelock); |
|
333 |
mapcnt = rp->r_mapcnt; |
|
334 |
rflag = rp->r_flags; |
|
335 |
mutex_exit(&rp->r_statelock); |
|
336 |
||
337 |
mutex_enter(&rp->r_statev4_lock); |
|
338 |
/* |
|
339 |
* If object mapped, then always return new change. |
|
340 |
* Otherwise, return change if object has dirty |
|
341 |
* pages. If object doesn't have any dirty pages, |
|
342 |
* then all changes have been pushed to server, so |
|
343 |
* reset change to grant change. |
|
344 |
*/ |
|
345 |
if (mapcnt) |
|
346 |
rp->r_deleg_change++; |
|
347 |
else if (! (rflag & R4DIRTY)) |
|
348 |
rp->r_deleg_change = rp->r_deleg_change_grant; |
|
349 |
change = rp->r_deleg_change; |
|
350 |
mutex_exit(&rp->r_statev4_lock); |
|
351 |
||
352 |
/* |
|
353 |
* Use inline XDR code directly, we know that we |
|
354 |
* going to a memory buffer and it has enough |
|
355 |
* space so it cannot fail. |
|
356 |
*/ |
|
357 |
IXDR_PUT_U_HYPER(fdata, change); |
|
358 |
fap->attrlist4_len += 2 * BYTES_PER_XDR_UNIT; |
|
1232 | 359 |
fap->attrmask |= FATTR4_CHANGE_MASK; |
0 | 360 |
} |
361 |
||
362 |
if (args->attr_request & FATTR4_SIZE_MASK) { |
|
363 |
/* |
|
364 |
* Use an atomic add of 0 to fetch a consistent view |
|
365 |
* of r_size; this avoids having to take rw_lock |
|
366 |
* which could cause a deadlock. |
|
367 |
*/ |
|
368 |
size = atomic_add_64_nv((uint64_t *)&rp->r_size, 0); |
|
369 |
||
370 |
/* |
|
371 |
* Use inline XDR code directly, we know that we |
|
372 |
* going to a memory buffer and it has enough |
|
373 |
* space so it cannot fail. |
|
374 |
*/ |
|
375 |
IXDR_PUT_U_HYPER(fdata, size); |
|
376 |
fap->attrlist4_len += 2 * BYTES_PER_XDR_UNIT; |
|
1232 | 377 |
fap->attrmask |= FATTR4_SIZE_MASK; |
0 | 378 |
} |
379 |
} |
|
380 |
||
381 |
VN_RELE(vp); |
|
382 |
||
383 |
*cs->statusp = resp->status = NFS4_OK; |
|
384 |
} |
|
385 |
||
386 |
static void |
|
387 |
cb_getattr_free(nfs_cb_resop4 *resop) |
|
388 |
{ |
|
389 |
if (resop->nfs_cb_resop4_u.opcbgetattr.obj_attributes.attrlist4) |
|
390 |
kmem_free(resop->nfs_cb_resop4_u.opcbgetattr. |
|
391 |
obj_attributes.attrlist4, |
|
392 |
cb_getattr_bytes); |
|
393 |
} |
|
394 |
||
395 |
static void |
|
396 |
cb_recall(nfs_cb_argop4 *argop, nfs_cb_resop4 *resop, struct svc_req *req, |
|
397 |
struct compound_state *cs, struct nfs4_callback_globals *ncg) |
|
398 |
{ |
|
399 |
CB_RECALL4args * args = &argop->nfs_cb_argop4_u.opcbrecall; |
|
400 |
CB_RECALL4res *resp = &resop->nfs_cb_resop4_u.opcbrecall; |
|
401 |
rnode4_t *rp; |
|
402 |
vnode_t *vp; |
|
403 |
struct nfs4_server *sp; |
|
404 |
bool_t found = FALSE; |
|
405 |
||
406 |
ncg->nfs4_callback_stats.cb_recall.value.ui64++; |
|
407 |
||
408 |
ASSERT(req->rq_prog >= NFS4_CALLBACK); |
|
409 |
ASSERT(req->rq_prog < NFS4_CALLBACK+nfs4_num_prognums); |
|
410 |
||
411 |
#ifdef DEBUG |
|
412 |
/* |
|
413 |
* error injection hook: set cb_recall_fail global to |
|
414 |
* NFS4 pcol error to be returned |
|
415 |
*/ |
|
416 |
if (cb4_recall_fail != NFS4_OK) { |
|
417 |
*cs->statusp = resp->status = cb4_recall_fail; |
|
418 |
return; |
|
419 |
} |
|
420 |
#endif |
|
421 |
||
422 |
mutex_enter(&ncg->nfs4_cb_lock); |
|
423 |
sp = ncg->nfs4prog2server[req->rq_prog - NFS4_CALLBACK]; |
|
424 |
mutex_exit(&ncg->nfs4_cb_lock); |
|
425 |
||
426 |
if (nfs4_server_vlock(sp, 0) == FALSE) { |
|
427 |
||
428 |
CB_WARN("cb_recall: cannot find server\n"); |
|
429 |
||
430 |
*cs->statusp = resp->status = NFS4ERR_BADHANDLE; |
|
431 |
return; |
|
432 |
} |
|
433 |
||
434 |
/* |
|
435 |
* Search the delegation list for a matching file handle |
|
436 |
* AND stateid; mutex on sp prevents the list from changing. |
|
437 |
*/ |
|
438 |
||
439 |
rp = list_head(&sp->s_deleg_list); |
|
440 |
for (; rp != NULL; rp = list_next(&sp->s_deleg_list, rp)) { |
|
441 |
mutex_enter(&rp->r_statev4_lock); |
|
442 |
||
443 |
/* check both state id and file handle! */ |
|
444 |
||
445 |
if ((bcmp(&rp->r_deleg_stateid, &args->stateid, |
|
446 |
sizeof (stateid4)) == 0)) { |
|
447 |
nfs4_fhandle_t fhandle; |
|
448 |
||
449 |
sfh4_copyval(rp->r_fh, &fhandle); |
|
450 |
if ((fhandle.fh_len == args->fh.nfs_fh4_len && |
|
451 |
bcmp(fhandle.fh_buf, args->fh.nfs_fh4_val, |
|
452 |
fhandle.fh_len) == 0)) { |
|
453 |
||
454 |
found = TRUE; |
|
455 |
break; |
|
456 |
} else { |
|
457 |
#ifdef DEBUG |
|
458 |
CB_WARN("cb_recall: stateid OK, bad fh"); |
|
459 |
#endif |
|
460 |
} |
|
461 |
} |
|
462 |
#ifdef DEBUG |
|
463 |
if (bcmp(&args->stateid, &nfs4_deleg_any, |
|
464 |
sizeof (stateid4)) == 0) { |
|
465 |
||
466 |
found = TRUE; |
|
467 |
break; |
|
468 |
} |
|
469 |
#endif |
|
470 |
mutex_exit(&rp->r_statev4_lock); |
|
471 |
} |
|
472 |
||
473 |
/* |
|
474 |
* VN_HOLD the vnode before releasing s_lock to guarantee |
|
475 |
* we have a valid vnode reference. The async thread will |
|
476 |
* release the hold when it's done. |
|
477 |
*/ |
|
478 |
if (found == TRUE) { |
|
479 |
mutex_exit(&rp->r_statev4_lock); |
|
480 |
vp = RTOV4(rp); |
|
481 |
VN_HOLD(vp); |
|
482 |
} |
|
483 |
mutex_exit(&sp->s_lock); |
|
484 |
nfs4_server_rele(sp); |
|
485 |
||
486 |
if (found == FALSE) { |
|
487 |
||
488 |
CB_WARN("cb_recall: bad stateid\n"); |
|
489 |
||
490 |
*cs->statusp = resp->status = NFS4ERR_BAD_STATEID; |
|
491 |
return; |
|
492 |
} |
|
493 |
||
494 |
/* Fire up a thread to do the delegreturn */ |
|
495 |
nfs4delegreturn_async(rp, NFS4_DR_RECALL|NFS4_DR_REOPEN, |
|
496 |
args->truncate); |
|
497 |
||
498 |
*cs->statusp = resp->status = 0; |
|
499 |
} |
|
500 |
||
501 |
/* ARGSUSED */ |
|
502 |
static void |
|
503 |
cb_recall_free(nfs_cb_resop4 *resop) |
|
504 |
{ |
|
505 |
/* nothing to do here, cb_recall doesn't kmem_alloc */ |
|
506 |
} |
|
507 |
||
508 |
/* |
|
509 |
* This function handles the CB_NULL proc call from an NFSv4 Server. |
|
510 |
* |
|
511 |
* We take note that the server has sent a CB_NULL for later processing |
|
512 |
* in the recovery logic. It is noted so we may pause slightly after the |
|
513 |
* setclientid and before reopening files. The pause is to allow the |
|
514 |
* NFSv4 Server time to receive the CB_NULL reply and adjust any of |
|
515 |
* its internal structures such that it has the opportunity to grant |
|
516 |
* delegations to reopened files. |
|
517 |
* |
|
518 |
*/ |
|
519 |
||
520 |
/* ARGSUSED */ |
|
521 |
static void |
|
522 |
cb_null(CB_COMPOUND4args *args, CB_COMPOUND4res *resp, struct svc_req *req, |
|
523 |
struct nfs4_callback_globals *ncg) |
|
524 |
{ |
|
525 |
struct nfs4_server *sp; |
|
526 |
||
527 |
ncg->nfs4_callback_stats.cb_null.value.ui64++; |
|
528 |
||
529 |
ASSERT(req->rq_prog >= NFS4_CALLBACK); |
|
530 |
ASSERT(req->rq_prog < NFS4_CALLBACK+nfs4_num_prognums); |
|
531 |
||
532 |
mutex_enter(&ncg->nfs4_cb_lock); |
|
533 |
sp = ncg->nfs4prog2server[req->rq_prog - NFS4_CALLBACK]; |
|
534 |
mutex_exit(&ncg->nfs4_cb_lock); |
|
535 |
||
536 |
if (nfs4_server_vlock(sp, 0) != FALSE) { |
|
537 |
sp->s_flags |= N4S_CB_PINGED; |
|
538 |
cv_broadcast(&sp->wait_cb_null); |
|
539 |
mutex_exit(&sp->s_lock); |
|
540 |
nfs4_server_rele(sp); |
|
541 |
} |
|
542 |
} |
|
543 |
||
544 |
/* |
|
545 |
* cb_illegal args: void |
|
546 |
* res : status (NFS4ERR_OP_CB_ILLEGAL) |
|
547 |
*/ |
|
548 |
/* ARGSUSED */ |
|
549 |
static void |
|
550 |
cb_illegal(nfs_cb_argop4 *argop, nfs_cb_resop4 *resop, struct svc_req *req, |
|
551 |
struct compound_state *cs, struct nfs4_callback_globals *ncg) |
|
552 |
{ |
|
553 |
CB_ILLEGAL4res *resp = &resop->nfs_cb_resop4_u.opcbillegal; |
|
554 |
||
555 |
ncg->nfs4_callback_stats.cb_illegal.value.ui64++; |
|
556 |
resop->resop = OP_CB_ILLEGAL; |
|
557 |
*cs->statusp = resp->status = NFS4ERR_OP_ILLEGAL; |
|
558 |
} |
|
559 |
||
560 |
static void |
|
561 |
cb_compound(CB_COMPOUND4args *args, CB_COMPOUND4res *resp, struct svc_req *req, |
|
562 |
struct nfs4_callback_globals *ncg) |
|
563 |
{ |
|
564 |
uint_t i; |
|
565 |
struct compound_state cs; |
|
566 |
nfs_cb_argop4 *argop; |
|
567 |
nfs_cb_resop4 *resop, *new_res; |
|
568 |
uint_t op; |
|
569 |
||
570 |
bzero(&cs, sizeof (cs)); |
|
571 |
cs.statusp = &resp->status; |
|
572 |
cs.cont = TRUE; |
|
573 |
||
574 |
/* |
|
575 |
* Form a reply tag by copying over the reqeuest tag. |
|
576 |
*/ |
|
577 |
resp->tag.utf8string_len = args->tag.utf8string_len; |
|
578 |
resp->tag.utf8string_val = kmem_alloc(resp->tag.utf8string_len, |
|
579 |
KM_SLEEP); |
|
580 |
bcopy(args->tag.utf8string_val, resp->tag.utf8string_val, |
|
581 |
args->tag.utf8string_len); |
|
582 |
||
583 |
/* |
|
584 |
* XXX for now, minorversion should be zero |
|
585 |
*/ |
|
586 |
if (args->minorversion != CB4_MINORVERSION) { |
|
587 |
resp->array_len = 0; |
|
588 |
resp->array = NULL; |
|
589 |
resp->status = NFS4ERR_MINOR_VERS_MISMATCH; |
|
590 |
return; |
|
591 |
} |
|
592 |
||
593 |
#ifdef DEBUG |
|
594 |
/* |
|
595 |
* Verify callback_ident. It doesn't really matter if it's wrong |
|
596 |
* because we don't really use callback_ident -- we use prog number |
|
597 |
* of the RPC request instead. In this case, just print a DEBUG |
|
598 |
* console message to reveal brokenness of cbclient (at bkoff/cthon). |
|
599 |
*/ |
|
600 |
if (args->callback_ident != req->rq_prog) |
|
601 |
zcmn_err(getzoneid(), CE_WARN, |
|
602 |
"cb_compound: cb_client using wrong " |
|
603 |
"callback_ident(%d), should be %d", |
|
604 |
args->callback_ident, req->rq_prog); |
|
605 |
#endif |
|
606 |
||
607 |
resp->array_len = args->array_len; |
|
608 |
resp->array = kmem_zalloc(args->array_len * sizeof (nfs_cb_resop4), |
|
609 |
KM_SLEEP); |
|
610 |
||
611 |
for (i = 0; i < args->array_len && cs.cont; i++) { |
|
612 |
||
613 |
argop = &args->array[i]; |
|
614 |
resop = &resp->array[i]; |
|
615 |
resop->resop = argop->argop; |
|
616 |
op = (uint_t)resop->resop; |
|
617 |
||
618 |
switch (op) { |
|
619 |
||
620 |
case OP_CB_GETATTR: |
|
621 |
||
622 |
cb_getattr(argop, resop, req, &cs, ncg); |
|
623 |
break; |
|
624 |
||
625 |
case OP_CB_RECALL: |
|
626 |
||
627 |
cb_recall(argop, resop, req, &cs, ncg); |
|
628 |
break; |
|
629 |
||
630 |
case OP_CB_ILLEGAL: |
|
631 |
||
632 |
/* fall through */ |
|
633 |
||
634 |
default: |
|
635 |
/* |
|
636 |
* Handle OP_CB_ILLEGAL and any undefined opcode. |
|
637 |
* Currently, the XDR code will return BADXDR |
|
638 |
* if cb op doesn't decode to legal value, so |
|
639 |
* it really only handles OP_CB_ILLEGAL. |
|
640 |
*/ |
|
641 |
op = OP_CB_ILLEGAL; |
|
642 |
cb_illegal(argop, resop, req, &cs, ncg); |
|
643 |
} |
|
644 |
||
645 |
if (*cs.statusp != NFS4_OK) |
|
646 |
cs.cont = FALSE; |
|
647 |
||
648 |
/* |
|
649 |
* If not at last op, and if we are to stop, then |
|
650 |
* compact the results array. |
|
651 |
*/ |
|
652 |
if ((i + 1) < args->array_len && !cs.cont) { |
|
653 |
||
654 |
new_res = kmem_alloc( |
|
655 |
(i+1) * sizeof (nfs_cb_resop4), KM_SLEEP); |
|
656 |
bcopy(resp->array, |
|
657 |
new_res, (i+1) * sizeof (nfs_cb_resop4)); |
|
658 |
kmem_free(resp->array, |
|
659 |
args->array_len * sizeof (nfs_cb_resop4)); |
|
660 |
||
661 |
resp->array_len = i + 1; |
|
662 |
resp->array = new_res; |
|
663 |
} |
|
664 |
} |
|
665 |
||
666 |
} |
|
667 |
||
668 |
static void |
|
669 |
cb_compound_free(CB_COMPOUND4res *resp) |
|
670 |
{ |
|
671 |
uint_t i, op; |
|
672 |
nfs_cb_resop4 *resop; |
|
673 |
||
674 |
if (resp->tag.utf8string_val) { |
|
675 |
UTF8STRING_FREE(resp->tag) |
|
676 |
} |
|
677 |
||
678 |
for (i = 0; i < resp->array_len; i++) { |
|
679 |
||
680 |
resop = &resp->array[i]; |
|
681 |
op = (uint_t)resop->resop; |
|
682 |
||
683 |
switch (op) { |
|
684 |
||
685 |
case OP_CB_GETATTR: |
|
686 |
||
687 |
cb_getattr_free(resop); |
|
688 |
break; |
|
689 |
||
690 |
case OP_CB_RECALL: |
|
691 |
||
692 |
cb_recall_free(resop); |
|
693 |
break; |
|
694 |
||
695 |
default: |
|
696 |
break; |
|
697 |
} |
|
698 |
} |
|
699 |
||
700 |
if (resp->array != NULL) { |
|
701 |
kmem_free(resp->array, |
|
702 |
resp->array_len * sizeof (nfs_cb_resop4)); |
|
703 |
} |
|
704 |
} |
|
705 |
||
706 |
static void |
|
707 |
cb_dispatch(struct svc_req *req, SVCXPRT *xprt) |
|
708 |
{ |
|
709 |
CB_COMPOUND4args args; |
|
710 |
CB_COMPOUND4res res; |
|
711 |
struct nfs4_callback_globals *ncg; |
|
712 |
||
713 |
bool_t (*xdr_args)(), (*xdr_res)(); |
|
714 |
void (*proc)(CB_COMPOUND4args *, CB_COMPOUND4res *, struct svc_req *, |
|
715 |
struct nfs4_callback_globals *); |
|
716 |
void (*freeproc)(CB_COMPOUND4res *); |
|
717 |
||
766 | 718 |
ncg = zone_getspecific(nfs4_callback_zone_key, nfs_zone()); |
0 | 719 |
ASSERT(ncg != NULL); |
720 |
||
721 |
ncg->nfs4_callback_stats.cb_dispatch.value.ui64++; |
|
722 |
||
723 |
switch (req->rq_proc) { |
|
724 |
case CB_NULL: |
|
725 |
xdr_args = xdr_void; |
|
726 |
xdr_res = xdr_void; |
|
727 |
proc = cb_null; |
|
728 |
freeproc = NULL; |
|
729 |
break; |
|
730 |
||
731 |
case CB_COMPOUND: |
|
1232 | 732 |
xdr_args = xdr_CB_COMPOUND4args_clnt; |
0 | 733 |
xdr_res = xdr_CB_COMPOUND4res; |
734 |
proc = cb_compound; |
|
735 |
freeproc = cb_compound_free; |
|
736 |
break; |
|
737 |
||
738 |
default: |
|
739 |
CB_WARN("cb_dispatch: no proc\n"); |
|
740 |
svcerr_noproc(xprt); |
|
741 |
return; |
|
742 |
} |
|
743 |
||
744 |
args.tag.utf8string_val = NULL; |
|
745 |
args.array = NULL; |
|
746 |
||
747 |
if (!SVC_GETARGS(xprt, xdr_args, (caddr_t)&args)) { |
|
748 |
||
749 |
CB_WARN("cb_dispatch: cannot getargs\n"); |
|
750 |
svcerr_decode(xprt); |
|
751 |
return; |
|
752 |
} |
|
753 |
||
754 |
(*proc)(&args, &res, req, ncg); |
|
755 |
||
756 |
if (svc_sendreply(xprt, xdr_res, (caddr_t)&res) == FALSE) { |
|
757 |
||
758 |
CB_WARN("cb_dispatch: bad sendreply\n"); |
|
759 |
||
760 |
/* |
|
761 |
* svcerr_systemerr(xprt); |
|
762 |
*/ |
|
763 |
} |
|
764 |
||
765 |
if (freeproc) |
|
766 |
(*freeproc)(&res); |
|
767 |
||
768 |
if (!SVC_FREEARGS(xprt, xdr_args, (caddr_t)&args)) { |
|
769 |
||
770 |
CB_WARN("cb_dispatch: bad freeargs\n"); |
|
771 |
} |
|
772 |
} |
|
773 |
||
774 |
static rpcprog_t |
|
775 |
nfs4_getnextprogram(struct nfs4_callback_globals *ncg) |
|
776 |
{ |
|
777 |
int i, j; |
|
778 |
||
779 |
j = ncg->nfs4_program_hint; |
|
780 |
for (i = 0; i < nfs4_num_prognums; i++, j++) { |
|
781 |
||
782 |
if (j >= nfs4_num_prognums) |
|
783 |
j = 0; |
|
784 |
||
785 |
if (ncg->nfs4prog2server[j] == NULL) { |
|
786 |
ncg->nfs4_program_hint = j+1; |
|
787 |
return (j+NFS4_CALLBACK); |
|
788 |
} |
|
789 |
} |
|
790 |
||
791 |
return (0); |
|
792 |
} |
|
793 |
||
794 |
void |
|
795 |
nfs4callback_destroy(nfs4_server_t *np) |
|
796 |
{ |
|
797 |
struct nfs4_callback_globals *ncg; |
|
798 |
int i; |
|
799 |
||
800 |
if (np->s_program == 0) |
|
801 |
return; |
|
802 |
||
803 |
ncg = np->zone_globals; |
|
804 |
i = np->s_program - NFS4_CALLBACK; |
|
805 |
||
806 |
mutex_enter(&ncg->nfs4_cb_lock); |
|
807 |
||
808 |
ASSERT(ncg->nfs4prog2server[i] == np); |
|
809 |
||
810 |
ncg->nfs4prog2server[i] = NULL; |
|
811 |
||
812 |
if (i < ncg->nfs4_program_hint) |
|
813 |
ncg->nfs4_program_hint = i; |
|
814 |
||
815 |
mutex_exit(&ncg->nfs4_cb_lock); |
|
816 |
} |
|
817 |
||
818 |
/* |
|
819 |
* nfs4_setport - This function saves a netid and univeral address for |
|
820 |
* the callback program. These values will be used during setclientid. |
|
821 |
*/ |
|
822 |
static void |
|
823 |
nfs4_setport(char *netid, char *uaddr, char *protofmly, char *proto, |
|
824 |
struct nfs4_callback_globals *ncg) |
|
825 |
{ |
|
826 |
struct nfs4_cb_port *p; |
|
827 |
bool_t found = FALSE; |
|
828 |
||
829 |
ASSERT(MUTEX_HELD(&ncg->nfs4_cb_lock)); |
|
830 |
||
831 |
p = list_head(&ncg->nfs4_cb_ports); |
|
832 |
for (; p != NULL; p = list_next(&ncg->nfs4_cb_ports, p)) { |
|
833 |
if (strcmp(p->netid, netid) == 0) { |
|
834 |
found = TRUE; |
|
835 |
break; |
|
836 |
} |
|
837 |
} |
|
838 |
if (found == TRUE) |
|
839 |
(void) strcpy(p->uaddr, uaddr); |
|
840 |
else { |
|
841 |
p = kmem_alloc(sizeof (*p), KM_SLEEP); |
|
842 |
||
843 |
(void) strcpy(p->uaddr, uaddr); |
|
844 |
(void) strcpy(p->netid, netid); |
|
845 |
(void) strcpy(p->protofmly, protofmly); |
|
846 |
(void) strcpy(p->proto, proto); |
|
847 |
list_insert_head(&ncg->nfs4_cb_ports, p); |
|
848 |
} |
|
849 |
} |
|
850 |
||
851 |
/* |
|
852 |
* nfs4_cb_args - This function is used to construct the callback |
|
853 |
* portion of the arguments needed for setclientid. |
|
854 |
*/ |
|
855 |
||
856 |
void |
|
857 |
nfs4_cb_args(nfs4_server_t *np, struct knetconfig *knc, SETCLIENTID4args *args) |
|
858 |
{ |
|
859 |
struct nfs4_cb_port *p; |
|
860 |
bool_t found = FALSE; |
|
861 |
rpcprog_t pgm; |
|
862 |
struct nfs4_callback_globals *ncg = np->zone_globals; |
|
863 |
||
864 |
/* |
|
865 |
* This server structure may already have a program number |
|
866 |
* assigned to it. This happens when the client has to |
|
867 |
* re-issue SETCLIENTID. Just re-use the information. |
|
868 |
*/ |
|
869 |
if (np->s_program >= NFS4_CALLBACK && |
|
870 |
np->s_program < NFS4_CALLBACK + nfs4_num_prognums) |
|
871 |
nfs4callback_destroy(np); |
|
872 |
||
873 |
mutex_enter(&ncg->nfs4_cb_lock); |
|
874 |
||
875 |
p = list_head(&ncg->nfs4_cb_ports); |
|
876 |
for (; p != NULL; p = list_next(&ncg->nfs4_cb_ports, p)) { |
|
877 |
if (strcmp(p->protofmly, knc->knc_protofmly) == 0 && |
|
878 |
strcmp(p->proto, knc->knc_proto) == 0) { |
|
879 |
found = TRUE; |
|
880 |
break; |
|
881 |
} |
|
882 |
} |
|
883 |
||
884 |
if (found == FALSE) { |
|
885 |
||
886 |
NFS4_DEBUG(nfs4_callback_debug, |
|
887 |
(CE_WARN, "nfs4_cb_args: could not find netid for %s/%s\n", |
|
888 |
knc->knc_protofmly, knc->knc_proto)); |
|
889 |
||
890 |
args->callback.cb_program = 0; |
|
891 |
args->callback.cb_location.r_netid = NULL; |
|
892 |
args->callback.cb_location.r_addr = NULL; |
|
893 |
args->callback_ident = 0; |
|
894 |
mutex_exit(&ncg->nfs4_cb_lock); |
|
895 |
return; |
|
896 |
} |
|
897 |
||
898 |
if ((pgm = nfs4_getnextprogram(ncg)) == 0) { |
|
899 |
CB_WARN("nfs4_cb_args: out of program numbers\n"); |
|
900 |
||
901 |
args->callback.cb_program = 0; |
|
902 |
args->callback.cb_location.r_netid = NULL; |
|
903 |
args->callback.cb_location.r_addr = NULL; |
|
904 |
args->callback_ident = 0; |
|
905 |
mutex_exit(&ncg->nfs4_cb_lock); |
|
906 |
return; |
|
907 |
} |
|
908 |
||
909 |
ncg->nfs4prog2server[pgm-NFS4_CALLBACK] = np; |
|
910 |
args->callback.cb_program = pgm; |
|
911 |
args->callback.cb_location.r_netid = p->netid; |
|
912 |
args->callback.cb_location.r_addr = p->uaddr; |
|
913 |
args->callback_ident = pgm; |
|
914 |
||
915 |
np->s_program = pgm; |
|
916 |
||
917 |
mutex_exit(&ncg->nfs4_cb_lock); |
|
918 |
} |
|
919 |
||
920 |
static int |
|
921 |
nfs4_dquery(struct nfs4_svc_args *arg, model_t model) |
|
922 |
{ |
|
923 |
file_t *fp; |
|
924 |
vnode_t *vp; |
|
925 |
rnode4_t *rp; |
|
926 |
int error; |
|
927 |
STRUCT_HANDLE(nfs4_svc_args, uap); |
|
928 |
||
929 |
STRUCT_SET_HANDLE(uap, model, arg); |
|
930 |
||
931 |
if ((fp = getf(STRUCT_FGET(uap, fd))) == NULL) |
|
932 |
return (EBADF); |
|
933 |
||
934 |
vp = fp->f_vnode; |
|
935 |
||
936 |
if (vp == NULL || vp->v_type != VREG || |
|
937 |
!vn_matchops(vp, nfs4_vnodeops)) { |
|
938 |
releasef(STRUCT_FGET(uap, fd)); |
|
939 |
return (EBADF); |
|
940 |
} |
|
941 |
||
942 |
rp = VTOR4(vp); |
|
943 |
||
944 |
/* |
|
945 |
* I can't convince myself that we need locking here. The |
|
946 |
* rnode cannot disappear and the value returned is instantly |
|
947 |
* stale anway, so why bother? |
|
948 |
*/ |
|
949 |
||
950 |
error = suword32(STRUCT_FGETP(uap, netid), rp->r_deleg_type); |
|
951 |
releasef(STRUCT_FGET(uap, fd)); |
|
952 |
return (error); |
|
953 |
} |
|
954 |
||
955 |
||
956 |
/* |
|
957 |
* NFS4 client system call. This service does the |
|
958 |
* necessary initialization for the callback program. |
|
959 |
* This is fashioned after the server side interaction |
|
960 |
* between nfsd and the kernel. On the client, the |
|
961 |
* mount command forks and the child process does the |
|
962 |
* necessary interaction with the kernel. |
|
963 |
* |
|
964 |
* uap->fd is the fd of an open transport provider |
|
965 |
*/ |
|
966 |
int |
|
967 |
nfs4_svc(struct nfs4_svc_args *arg, model_t model) |
|
968 |
{ |
|
969 |
file_t *fp; |
|
970 |
int error; |
|
971 |
int readsize; |
|
972 |
char buf[KNC_STRSIZE], uaddr[KNC_STRSIZE]; |
|
973 |
char protofmly[KNC_STRSIZE], proto[KNC_STRSIZE]; |
|
974 |
size_t len; |
|
975 |
STRUCT_HANDLE(nfs4_svc_args, uap); |
|
976 |
struct netbuf addrmask; |
|
977 |
int cmd; |
|
978 |
SVCMASTERXPRT *cb_xprt; |
|
979 |
struct nfs4_callback_globals *ncg; |
|
980 |
||
981 |
#ifdef lint |
|
982 |
model = model; /* STRUCT macros don't always refer to it */ |
|
983 |
#endif |
|
984 |
||
985 |
STRUCT_SET_HANDLE(uap, model, arg); |
|
986 |
||
987 |
if (STRUCT_FGET(uap, cmd) == NFS4_DQUERY) |
|
988 |
return (nfs4_dquery(arg, model)); |
|
989 |
||
990 |
if (secpolicy_nfs(CRED()) != 0) |
|
991 |
return (EPERM); |
|
992 |
||
993 |
if ((fp = getf(STRUCT_FGET(uap, fd))) == NULL) |
|
994 |
return (EBADF); |
|
995 |
||
996 |
/* |
|
997 |
* Set read buffer size to rsize |
|
998 |
* and add room for RPC headers. |
|
999 |
*/ |
|
1000 |
readsize = nfs3tsize() + (RPC_MAXDATASIZE - NFS_MAXDATA); |
|
1001 |
if (readsize < RPC_MAXDATASIZE) |
|
1002 |
readsize = RPC_MAXDATASIZE; |
|
1003 |
||
1004 |
error = copyinstr((const char *)STRUCT_FGETP(uap, netid), buf, |
|
1005 |
KNC_STRSIZE, &len); |
|
1006 |
if (error) { |
|
1007 |
releasef(STRUCT_FGET(uap, fd)); |
|
1008 |
return (error); |
|
1009 |
} |
|
1010 |
||
1011 |
cmd = STRUCT_FGET(uap, cmd); |
|
1012 |
||
1013 |
if (cmd & NFS4_KRPC_START) { |
|
1014 |
addrmask.len = STRUCT_FGET(uap, addrmask.len); |
|
1015 |
addrmask.maxlen = STRUCT_FGET(uap, addrmask.maxlen); |
|
1016 |
addrmask.buf = kmem_alloc(addrmask.maxlen, KM_SLEEP); |
|
1017 |
error = copyin(STRUCT_FGETP(uap, addrmask.buf), addrmask.buf, |
|
1018 |
addrmask.len); |
|
1019 |
if (error) { |
|
1020 |
releasef(STRUCT_FGET(uap, fd)); |
|
1021 |
kmem_free(addrmask.buf, addrmask.maxlen); |
|
1022 |
return (error); |
|
1023 |
} |
|
1024 |
} |
|
1025 |
else |
|
1026 |
addrmask.buf = NULL; |
|
1027 |
||
1028 |
error = copyinstr((const char *)STRUCT_FGETP(uap, addr), uaddr, |
|
1029 |
sizeof (uaddr), &len); |
|
1030 |
if (error) { |
|
1031 |
releasef(STRUCT_FGET(uap, fd)); |
|
1032 |
if (addrmask.buf) |
|
1033 |
kmem_free(addrmask.buf, addrmask.maxlen); |
|
1034 |
return (error); |
|
1035 |
} |
|
1036 |
||
1037 |
error = copyinstr((const char *)STRUCT_FGETP(uap, protofmly), protofmly, |
|
1038 |
sizeof (protofmly), &len); |
|
1039 |
if (error) { |
|
1040 |
releasef(STRUCT_FGET(uap, fd)); |
|
1041 |
if (addrmask.buf) |
|
1042 |
kmem_free(addrmask.buf, addrmask.maxlen); |
|
1043 |
return (error); |
|
1044 |
} |
|
1045 |
||
1046 |
error = copyinstr((const char *)STRUCT_FGETP(uap, proto), proto, |
|
1047 |
sizeof (proto), &len); |
|
1048 |
if (error) { |
|
1049 |
releasef(STRUCT_FGET(uap, fd)); |
|
1050 |
if (addrmask.buf) |
|
1051 |
kmem_free(addrmask.buf, addrmask.maxlen); |
|
1052 |
return (error); |
|
1053 |
} |
|
1054 |
||
766 | 1055 |
ncg = zone_getspecific(nfs4_callback_zone_key, nfs_zone()); |
0 | 1056 |
ASSERT(ncg != NULL); |
1057 |
||
1058 |
mutex_enter(&ncg->nfs4_cb_lock); |
|
1059 |
if (cmd & NFS4_SETPORT) |
|
1060 |
nfs4_setport(buf, uaddr, protofmly, proto, ncg); |
|
1061 |
||
1062 |
if (cmd & NFS4_KRPC_START) { |
|
1063 |
error = svc_tli_kcreate(fp, readsize, buf, &addrmask, &cb_xprt, |
|
1064 |
&nfs4_cb_sct, NULL, NFS_CB_SVCPOOL_ID, FALSE); |
|
1065 |
if (error) { |
|
1066 |
CB_WARN1("nfs4_svc: svc_tli_kcreate failed %d\n", |
|
1067 |
error); |
|
1068 |
kmem_free(addrmask.buf, addrmask.maxlen); |
|
1069 |
} |
|
1070 |
} |
|
1071 |
||
1072 |
mutex_exit(&ncg->nfs4_cb_lock); |
|
1073 |
releasef(STRUCT_FGET(uap, fd)); |
|
1074 |
return (error); |
|
1075 |
} |
|
1076 |
||
1077 |
struct nfs4_callback_globals * |
|
1078 |
nfs4_get_callback_globals(void) |
|
1079 |
{ |
|
766 | 1080 |
return (zone_getspecific(nfs4_callback_zone_key, nfs_zone())); |
0 | 1081 |
} |
1082 |
||
1083 |
static void * |
|
1084 |
nfs4_callback_init_zone(zoneid_t zoneid) |
|
1085 |
{ |
|
1086 |
kstat_t *nfs4_callback_kstat; |
|
1087 |
struct nfs4_callback_globals *ncg; |
|
1088 |
||
1089 |
ncg = kmem_zalloc(sizeof (*ncg), KM_SLEEP); |
|
1090 |
||
1091 |
ncg->nfs4prog2server = kmem_zalloc(nfs4_num_prognums * |
|
1092 |
sizeof (struct nfs4_server *), KM_SLEEP); |
|
1093 |
||
1094 |
/* initialize the dlist */ |
|
1095 |
mutex_init(&ncg->nfs4_dlist_lock, NULL, MUTEX_DEFAULT, NULL); |
|
1096 |
list_create(&ncg->nfs4_dlist, sizeof (struct nfs4_dnode), |
|
1097 |
offsetof(struct nfs4_dnode, linkage)); |
|
1098 |
||
1099 |
/* initialize cb_port list */ |
|
1100 |
mutex_init(&ncg->nfs4_cb_lock, NULL, MUTEX_DEFAULT, NULL); |
|
1101 |
list_create(&ncg->nfs4_cb_ports, sizeof (struct nfs4_cb_port), |
|
1102 |
offsetof(struct nfs4_cb_port, linkage)); |
|
1103 |
||
1104 |
/* get our own copy of the kstats */ |
|
1105 |
bcopy(&nfs4_callback_stats_tmpl, &ncg->nfs4_callback_stats, |
|
1106 |
sizeof (nfs4_callback_stats_tmpl)); |
|
1107 |
/* register "nfs:0:nfs4_callback_stats" for this zone */ |
|
1108 |
if ((nfs4_callback_kstat = |
|
1109 |
kstat_create_zone("nfs", 0, "nfs4_callback_stats", "misc", |
|
1110 |
KSTAT_TYPE_NAMED, |
|
1111 |
sizeof (ncg->nfs4_callback_stats) / sizeof (kstat_named_t), |
|
1112 |
KSTAT_FLAG_VIRTUAL | KSTAT_FLAG_WRITABLE, |
|
1113 |
zoneid)) != NULL) { |
|
1114 |
nfs4_callback_kstat->ks_data = &ncg->nfs4_callback_stats; |
|
1115 |
kstat_install(nfs4_callback_kstat); |
|
1116 |
} |
|
1117 |
return (ncg); |
|
1118 |
} |
|
1119 |
||
1120 |
static void |
|
1121 |
nfs4_discard_delegations(struct nfs4_callback_globals *ncg) |
|
1122 |
{ |
|
1123 |
nfs4_server_t *sp; |
|
1124 |
int i, num_removed; |
|
1125 |
||
1126 |
/* |
|
1127 |
* It's OK here to just run through the registered "programs", as |
|
1128 |
* servers without programs won't have any delegations to handle. |
|
1129 |
*/ |
|
1130 |
for (i = 0; i < nfs4_num_prognums; i++) { |
|
1131 |
rnode4_t *rp; |
|
1132 |
||
1133 |
mutex_enter(&ncg->nfs4_cb_lock); |
|
1134 |
sp = ncg->nfs4prog2server[i]; |
|
1135 |
mutex_exit(&ncg->nfs4_cb_lock); |
|
1136 |
||
1137 |
if (nfs4_server_vlock(sp, 1) == FALSE) |
|
1138 |
continue; |
|
1139 |
num_removed = 0; |
|
1140 |
while ((rp = list_head(&sp->s_deleg_list)) != NULL) { |
|
1141 |
mutex_enter(&rp->r_statev4_lock); |
|
1142 |
if (rp->r_deleg_type == OPEN_DELEGATE_NONE) { |
|
1143 |
/* |
|
1144 |
* We need to take matters into our own hands, |
|
1145 |
* as nfs4delegreturn_cleanup_impl() won't |
|
1146 |
* remove this from the list. |
|
1147 |
*/ |
|
1148 |
list_remove(&sp->s_deleg_list, rp); |
|
1149 |
mutex_exit(&rp->r_statev4_lock); |
|
1150 |
nfs4_dec_state_ref_count_nolock(sp, |
|
1151 |
VTOMI4(RTOV4(rp))); |
|
1152 |
num_removed++; |
|
1153 |
continue; |
|
1154 |
} |
|
1155 |
mutex_exit(&rp->r_statev4_lock); |
|
1156 |
VN_HOLD(RTOV4(rp)); |
|
1157 |
mutex_exit(&sp->s_lock); |
|
1158 |
/* |
|
1159 |
* The following will remove the node from the list. |
|
1160 |
*/ |
|
1161 |
nfs4delegreturn_cleanup_impl(rp, sp, ncg); |
|
1162 |
VN_RELE(RTOV4(rp)); |
|
1163 |
mutex_enter(&sp->s_lock); |
|
1164 |
} |
|
1165 |
mutex_exit(&sp->s_lock); |
|
1166 |
/* each removed list node reles a reference */ |
|
1167 |
while (num_removed-- > 0) |
|
1168 |
nfs4_server_rele(sp); |
|
1169 |
/* remove our reference for nfs4_server_vlock */ |
|
1170 |
nfs4_server_rele(sp); |
|
1171 |
} |
|
1172 |
} |
|
1173 |
||
1174 |
/* ARGSUSED */ |
|
1175 |
static void |
|
1176 |
nfs4_callback_shutdown_zone(zoneid_t zoneid, void *data) |
|
1177 |
{ |
|
1178 |
struct nfs4_callback_globals *ncg = data; |
|
1179 |
||
1180 |
/* |
|
1181 |
* Clean pending delegation return list. |
|
1182 |
*/ |
|
1183 |
nfs4_dlistclean_impl(ncg, NFS4_DR_DISCARD); |
|
1184 |
||
1185 |
/* |
|
1186 |
* Discard all delegations. |
|
1187 |
*/ |
|
1188 |
nfs4_discard_delegations(ncg); |
|
1189 |
} |
|
1190 |
||
1191 |
static void |
|
1192 |
nfs4_callback_fini_zone(zoneid_t zoneid, void *data) |
|
1193 |
{ |
|
1194 |
struct nfs4_callback_globals *ncg = data; |
|
1195 |
struct nfs4_cb_port *p; |
|
1196 |
nfs4_server_t *sp, *next; |
|
1197 |
nfs4_server_t freelist; |
|
1198 |
int i; |
|
1199 |
||
1200 |
kstat_delete_byname_zone("nfs", 0, "nfs4_callback_stats", zoneid); |
|
1201 |
||
1202 |
/* |
|
1203 |
* Discard all delegations that may have crept in since we did the |
|
1204 |
* _shutdown. |
|
1205 |
*/ |
|
1206 |
nfs4_discard_delegations(ncg); |
|
1207 |
/* |
|
1208 |
* We're completely done with this zone and all associated |
|
1209 |
* nfs4_server_t's. Any remaining nfs4_server_ts should only have one |
|
1210 |
* more reference outstanding -- the reference we didn't release in |
|
1211 |
* nfs4_renew_lease_thread(). |
|
1212 |
* |
|
1213 |
* Here we need to run through the global nfs4_server_lst as we need to |
|
1214 |
* deal with nfs4_server_ts without programs, as they also have threads |
|
1215 |
* created for them, and so have outstanding references that we need to |
|
1216 |
* release. |
|
1217 |
*/ |
|
1218 |
freelist.forw = &freelist; |
|
1219 |
freelist.back = &freelist; |
|
1220 |
mutex_enter(&nfs4_server_lst_lock); |
|
1221 |
sp = nfs4_server_lst.forw; |
|
1222 |
while (sp != &nfs4_server_lst) { |
|
1223 |
next = sp->forw; |
|
1224 |
if (sp->zoneid == zoneid) { |
|
1225 |
remque(sp); |
|
1226 |
insque(sp, &freelist); |
|
1227 |
} |
|
1228 |
sp = next; |
|
1229 |
} |
|
1230 |
mutex_exit(&nfs4_server_lst_lock); |
|
1231 |
||
1232 |
sp = freelist.forw; |
|
1233 |
while (sp != &freelist) { |
|
1234 |
next = sp->forw; |
|
1235 |
nfs4_server_rele(sp); /* free the list's reference */ |
|
1236 |
sp = next; |
|
1237 |
} |
|
1238 |
||
1239 |
#ifdef DEBUG |
|
1240 |
for (i = 0; i < nfs4_num_prognums; i++) { |
|
1241 |
ASSERT(ncg->nfs4prog2server[i] == NULL); |
|
1242 |
} |
|
1243 |
#endif |
|
1244 |
kmem_free(ncg->nfs4prog2server, nfs4_num_prognums * |
|
1245 |
sizeof (struct nfs4_server *)); |
|
1246 |
||
1247 |
mutex_enter(&ncg->nfs4_cb_lock); |
|
1248 |
while ((p = list_head(&ncg->nfs4_cb_ports)) != NULL) { |
|
1249 |
list_remove(&ncg->nfs4_cb_ports, p); |
|
1250 |
kmem_free(p, sizeof (*p)); |
|
1251 |
} |
|
1252 |
list_destroy(&ncg->nfs4_cb_ports); |
|
1253 |
mutex_destroy(&ncg->nfs4_cb_lock); |
|
1254 |
list_destroy(&ncg->nfs4_dlist); |
|
1255 |
mutex_destroy(&ncg->nfs4_dlist_lock); |
|
1256 |
kmem_free(ncg, sizeof (*ncg)); |
|
1257 |
} |
|
1258 |
||
1259 |
void |
|
1260 |
nfs4_callback_init(void) |
|
1261 |
{ |
|
1262 |
int i; |
|
1263 |
SVC_CALLOUT *nfs4_cb_sc; |
|
1264 |
||
1265 |
/* initialize the callback table */ |
|
1266 |
nfs4_cb_sc = kmem_alloc(nfs4_num_prognums * |
|
1267 |
sizeof (SVC_CALLOUT), KM_SLEEP); |
|
1268 |
||
1269 |
for (i = 0; i < nfs4_num_prognums; i++) { |
|
1270 |
nfs4_cb_sc[i].sc_prog = NFS4_CALLBACK+i; |
|
1271 |
nfs4_cb_sc[i].sc_versmin = NFS_CB; |
|
1272 |
nfs4_cb_sc[i].sc_versmax = NFS_CB; |
|
1273 |
nfs4_cb_sc[i].sc_dispatch = cb_dispatch; |
|
1274 |
} |
|
1275 |
||
1276 |
nfs4_cb_sct.sct_size = nfs4_num_prognums; |
|
1277 |
nfs4_cb_sct.sct_free = FALSE; |
|
1278 |
nfs4_cb_sct.sct_sc = nfs4_cb_sc; |
|
1279 |
||
1280 |
/* |
|
1281 |
* Compute max bytes required for dyamically allocated parts |
|
1282 |
* of cb_getattr reply. Only size and change are supported now. |
|
1283 |
* If CB_GETATTR is changed to reply with additional attrs, |
|
1284 |
* additional sizes must be added below. |
|
1285 |
* |
|
1286 |
* fattr4_change + fattr4_size == uint64_t + uint64_t |
|
1287 |
*/ |
|
1288 |
cb_getattr_bytes = 2 * BYTES_PER_XDR_UNIT + 2 * BYTES_PER_XDR_UNIT; |
|
1289 |
||
1290 |
zone_key_create(&nfs4_callback_zone_key, nfs4_callback_init_zone, |
|
1291 |
nfs4_callback_shutdown_zone, nfs4_callback_fini_zone); |
|
1292 |
} |
|
1293 |
||
1294 |
void |
|
1295 |
nfs4_callback_fini(void) |
|
1296 |
{ |
|
1297 |
} |
|
1298 |
||
1299 |
/* |
|
1300 |
* NB: This function can be called from the *wrong* zone (ie, the zone that |
|
1301 |
* 'rp' belongs to and the caller's zone may not be the same). This can happen |
|
1302 |
* if the zone is going away and we get called from nfs4_async_inactive(). In |
|
1303 |
* this case the globals will be NULL and we won't update the counters, which |
|
1304 |
* doesn't matter as the zone is going away anyhow. |
|
1305 |
*/ |
|
1306 |
static void |
|
1307 |
nfs4delegreturn_cleanup_impl(rnode4_t *rp, nfs4_server_t *np, |
|
1308 |
struct nfs4_callback_globals *ncg) |
|
1309 |
{ |
|
1310 |
mntinfo4_t *mi = VTOMI4(RTOV4(rp)); |
|
1311 |
boolean_t need_rele = B_FALSE; |
|
1312 |
||
1313 |
mutex_enter(&rp->r_statev4_lock); |
|
1314 |
||
1315 |
if (rp->r_deleg_type == OPEN_DELEGATE_NONE) { |
|
1316 |
mutex_exit(&rp->r_statev4_lock); |
|
1317 |
return; |
|
1318 |
} |
|
1319 |
||
1320 |
/* |
|
1321 |
* Free the cred originally held when |
|
1322 |
* the delegation was granted. Caller must |
|
1323 |
* hold this cred if it wants to use it after |
|
1324 |
* this call. |
|
1325 |
*/ |
|
1326 |
crfree(rp->r_deleg_cred); |
|
1327 |
rp->r_deleg_cred = NULL; |
|
1328 |
rp->r_deleg_type = OPEN_DELEGATE_NONE; |
|
1329 |
rp->r_deleg_needs_recovery = OPEN_DELEGATE_NONE; |
|
1330 |
rp->r_deleg_needs_recall = FALSE; |
|
1331 |
rp->r_deleg_return_pending = FALSE; |
|
1332 |
mutex_exit(&rp->r_statev4_lock); |
|
1333 |
||
1334 |
/* |
|
1335 |
* Caller must be holding mi_recovlock in read mode |
|
1336 |
* to call here. This is provided by start_op. |
|
1337 |
*/ |
|
1338 |
||
1339 |
if (np == NULL) { |
|
1340 |
np = find_nfs4_server_all(mi, 1); |
|
1341 |
ASSERT(np != NULL); |
|
1342 |
need_rele = B_TRUE; |
|
1343 |
} else { |
|
1344 |
mutex_enter(&np->s_lock); |
|
1345 |
} |
|
1346 |
||
1347 |
/* |
|
1348 |
* Remove the rnode from the server's list and |
|
1349 |
* update the ref counts. |
|
1350 |
*/ |
|
1351 |
list_remove(&np->s_deleg_list, rp); |
|
1352 |
nfs4_dec_state_ref_count_nolock(np, mi); |
|
1353 |
mutex_exit(&np->s_lock); |
|
1354 |
/* removed list node removes a reference */ |
|
1355 |
nfs4_server_rele(np); |
|
1356 |
if (need_rele) |
|
1357 |
nfs4_server_rele(np); |
|
1358 |
if (ncg != NULL) |
|
1359 |
ncg->nfs4_callback_stats.delegations.value.ui64--; |
|
1360 |
} |
|
1361 |
||
1362 |
void |
|
1363 |
nfs4delegreturn_cleanup(rnode4_t *rp, nfs4_server_t *np) |
|
1364 |
{ |
|
1365 |
struct nfs4_callback_globals *ncg; |
|
1366 |
||
1367 |
if (np != NULL) { |
|
1368 |
ncg = np->zone_globals; |
|
766 | 1369 |
} else if (nfs_zone() == VTOMI4(RTOV4(rp))->mi_zone) { |
1370 |
ncg = zone_getspecific(nfs4_callback_zone_key, nfs_zone()); |
|
0 | 1371 |
ASSERT(ncg != NULL); |
1372 |
} else { |
|
1373 |
/* |
|
1374 |
* Request coming from the wrong zone. |
|
1375 |
*/ |
|
1376 |
ASSERT(getzoneid() == GLOBAL_ZONEID); |
|
1377 |
ncg = NULL; |
|
1378 |
} |
|
1379 |
||
1380 |
nfs4delegreturn_cleanup_impl(rp, np, ncg); |
|
1381 |
} |
|
1382 |
||
1383 |
static void |
|
1384 |
nfs4delegreturn_save_lost_rqst(int error, nfs4_lost_rqst_t *lost_rqstp, |
|
1385 |
cred_t *cr, vnode_t *vp) |
|
1386 |
{ |
|
1387 |
if (error != ETIMEDOUT && error != EINTR && |
|
1388 |
!NFS4_FRC_UNMT_ERR(error, vp->v_vfsp)) { |
|
1389 |
lost_rqstp->lr_op = 0; |
|
1390 |
return; |
|
1391 |
} |
|
1392 |
||
1393 |
NFS4_DEBUG(nfs4_lost_rqst_debug, (CE_NOTE, |
|
1394 |
"nfs4close_save_lost_rqst: error %d", error)); |
|
1395 |
||
1396 |
lost_rqstp->lr_op = OP_DELEGRETURN; |
|
1397 |
/* |
|
1398 |
* The vp is held and rele'd via the recovery code. |
|
1399 |
* See nfs4_save_lost_rqst. |
|
1400 |
*/ |
|
1401 |
lost_rqstp->lr_vp = vp; |
|
1402 |
lost_rqstp->lr_dvp = NULL; |
|
1403 |
lost_rqstp->lr_oop = NULL; |
|
1404 |
lost_rqstp->lr_osp = NULL; |
|
1405 |
lost_rqstp->lr_lop = NULL; |
|
1406 |
lost_rqstp->lr_cr = cr; |
|
1407 |
lost_rqstp->lr_flk = NULL; |
|
1408 |
lost_rqstp->lr_putfirst = FALSE; |
|
1409 |
} |
|
1410 |
||
1411 |
static void |
|
1412 |
nfs4delegreturn_otw(rnode4_t *rp, cred_t *cr, nfs4_error_t *ep) |
|
1413 |
{ |
|
1414 |
COMPOUND4args_clnt args; |
|
1415 |
COMPOUND4res_clnt res; |
|
1416 |
nfs_argop4 argops[3]; |
|
1417 |
nfs4_ga_res_t *garp = NULL; |
|
1418 |
hrtime_t t; |
|
1419 |
int numops; |
|
1420 |
int doqueue = 1; |
|
1421 |
||
1422 |
args.ctag = TAG_DELEGRETURN; |
|
1423 |
||
1424 |
numops = 3; /* PUTFH, GETATTR, DELEGRETURN */ |
|
1425 |
||
1426 |
args.array = argops; |
|
1427 |
args.array_len = numops; |
|
1428 |
||
1429 |
argops[0].argop = OP_CPUTFH; |
|
1430 |
argops[0].nfs_argop4_u.opcputfh.sfh = rp->r_fh; |
|
1431 |
||
1432 |
argops[1].argop = OP_GETATTR; |
|
1433 |
argops[1].nfs_argop4_u.opgetattr.attr_request = NFS4_VATTR_MASK; |
|
1434 |
argops[1].nfs_argop4_u.opgetattr.mi = VTOMI4(RTOV4(rp)); |
|
1435 |
||
1436 |
argops[2].argop = OP_DELEGRETURN; |
|
1437 |
argops[2].nfs_argop4_u.opdelegreturn.deleg_stateid = |
|
1438 |
rp->r_deleg_stateid; |
|
1439 |
||
1440 |
t = gethrtime(); |
|
1441 |
rfs4call(VTOMI4(RTOV4(rp)), &args, &res, cr, &doqueue, 0, ep); |
|
1442 |
||
1443 |
if (ep->error) |
|
1444 |
return; |
|
1445 |
||
1446 |
if (res.status == NFS4_OK) { |
|
1447 |
garp = &res.array[1].nfs_resop4_u.opgetattr.ga_res; |
|
1448 |
nfs4_attr_cache(RTOV4(rp), garp, t, cr, TRUE, NULL); |
|
1449 |
||
1450 |
} |
|
1451 |
(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res); |
|
1452 |
} |
|
1453 |
||
1454 |
int |
|
1455 |
nfs4_do_delegreturn(rnode4_t *rp, int flags, cred_t *cr, |
|
1456 |
struct nfs4_callback_globals *ncg) |
|
1457 |
{ |
|
1458 |
vnode_t *vp = RTOV4(rp); |
|
1459 |
mntinfo4_t *mi = VTOMI4(vp); |
|
1460 |
nfs4_lost_rqst_t lost_rqst; |
|
1461 |
nfs4_recov_state_t recov_state; |
|
1462 |
bool_t needrecov = FALSE, recovonly, done = FALSE; |
|
1463 |
nfs4_error_t e = { 0, NFS4_OK, RPC_SUCCESS }; |
|
1464 |
||
1465 |
ncg->nfs4_callback_stats.delegreturn.value.ui64++; |
|
1466 |
||
1467 |
while (!done) { |
|
1468 |
e.error = nfs4_start_fop(mi, vp, NULL, OH_DELEGRETURN, |
|
1469 |
&recov_state, &recovonly); |
|
1470 |
||
1471 |
if (e.error) { |
|
1472 |
if (flags & NFS4_DR_FORCE) { |
|
1473 |
(void) nfs_rw_enter_sig(&mi->mi_recovlock, |
|
1474 |
RW_READER, 0); |
|
1475 |
nfs4delegreturn_cleanup_impl(rp, NULL, ncg); |
|
1476 |
nfs_rw_exit(&mi->mi_recovlock); |
|
1477 |
} |
|
1478 |
break; |
|
1479 |
} |
|
1480 |
||
1481 |
/* |
|
1482 |
* Check to see if the delegation has already been |
|
1483 |
* returned by the recovery thread. The state of |
|
1484 |
* the delegation cannot change at this point due |
|
1485 |
* to start_fop and the r_deleg_recall_lock. |
|
1486 |
*/ |
|
1487 |
if (rp->r_deleg_type == OPEN_DELEGATE_NONE) { |
|
1488 |
e.error = 0; |
|
1489 |
nfs4_end_op(mi, vp, NULL, &recov_state, needrecov); |
|
1490 |
break; |
|
1491 |
} |
|
1492 |
||
1493 |
if (recovonly) { |
|
1494 |
/* |
|
1495 |
* Delegation will be returned via the |
|
1496 |
* recovery framework. Build a lost request |
|
1497 |
* structure, start recovery and get out. |
|
1498 |
*/ |
|
1499 |
nfs4_error_init(&e, EINTR); |
|
1500 |
nfs4delegreturn_save_lost_rqst(e.error, &lost_rqst, |
|
1501 |
cr, vp); |
|
1502 |
(void) nfs4_start_recovery(&e, mi, vp, |
|
1503 |
NULL, &rp->r_deleg_stateid, |
|
1504 |
lost_rqst.lr_op == OP_DELEGRETURN ? |
|
1505 |
&lost_rqst : NULL, OP_DELEGRETURN, NULL); |
|
1506 |
nfs4_end_op(mi, vp, NULL, &recov_state, needrecov); |
|
1507 |
break; |
|
1508 |
} |
|
1509 |
||
1510 |
nfs4delegreturn_otw(rp, cr, &e); |
|
1511 |
||
1512 |
/* |
|
1513 |
* Ignore some errors on delegreturn; no point in marking |
|
1514 |
* the file dead on a state destroying operation. |
|
1515 |
*/ |
|
1516 |
if (e.error == 0 && (nfs4_recov_marks_dead(e.stat) || |
|
1517 |
e.stat == NFS4ERR_BADHANDLE || |
|
1518 |
e.stat == NFS4ERR_STALE)) |
|
1519 |
needrecov = FALSE; |
|
1520 |
else |
|
1521 |
needrecov = nfs4_needs_recovery(&e, TRUE, vp->v_vfsp); |
|
1522 |
||
1523 |
if (needrecov) { |
|
1524 |
nfs4delegreturn_save_lost_rqst(e.error, &lost_rqst, |
|
1525 |
cr, vp); |
|
1526 |
(void) nfs4_start_recovery(&e, mi, vp, |
|
1527 |
NULL, &rp->r_deleg_stateid, |
|
1528 |
lost_rqst.lr_op == OP_DELEGRETURN ? |
|
1529 |
&lost_rqst : NULL, OP_DELEGRETURN, NULL); |
|
1530 |
} else { |
|
1531 |
nfs4delegreturn_cleanup_impl(rp, NULL, ncg); |
|
1532 |
done = TRUE; |
|
1533 |
} |
|
1534 |
||
1535 |
nfs4_end_op(mi, vp, NULL, &recov_state, needrecov); |
|
1536 |
} |
|
1537 |
return (e.error); |
|
1538 |
} |
|
1539 |
||
1540 |
/* |
|
1541 |
* nfs4_resend_delegreturn - used to drive the delegreturn |
|
1542 |
* operation via the recovery thread. |
|
1543 |
*/ |
|
1544 |
void |
|
1545 |
nfs4_resend_delegreturn(nfs4_lost_rqst_t *lorp, nfs4_error_t *ep, |
|
1546 |
nfs4_server_t *np) |
|
1547 |
{ |
|
1548 |
rnode4_t *rp = VTOR4(lorp->lr_vp); |
|
1549 |
||
1550 |
/* If the file failed recovery, just quit. */ |
|
1551 |
mutex_enter(&rp->r_statelock); |
|
1552 |
if (rp->r_flags & R4RECOVERR) { |
|
1553 |
ep->error = EIO; |
|
1554 |
} |
|
1555 |
mutex_exit(&rp->r_statelock); |
|
1556 |
||
1557 |
if (!ep->error) |
|
1558 |
nfs4delegreturn_otw(rp, lorp->lr_cr, ep); |
|
1559 |
||
1560 |
/* |
|
1561 |
* If recovery is now needed, then return the error |
|
1562 |
* and status and let the recovery thread handle it, |
|
1563 |
* including re-driving another delegreturn. Otherwise, |
|
1564 |
* just give up and clean up the delegation. |
|
1565 |
*/ |
|
1566 |
if (nfs4_needs_recovery(ep, TRUE, lorp->lr_vp->v_vfsp)) |
|
1567 |
return; |
|
1568 |
||
1569 |
if (rp->r_deleg_type != OPEN_DELEGATE_NONE) |
|
1570 |
nfs4delegreturn_cleanup(rp, np); |
|
1571 |
||
1572 |
nfs4_error_zinit(ep); |
|
1573 |
} |
|
1574 |
||
1575 |
/* |
|
1576 |
* nfs4delegreturn - general function to return a delegation. |
|
1577 |
* |
|
1578 |
* NFS4_DR_FORCE - return the delegation even if start_op fails |
|
1579 |
* NFS4_DR_PUSH - push modified data back to the server via VOP_PUTPAGE |
|
1580 |
* NFS4_DR_DISCARD - discard the delegation w/o delegreturn |
|
1581 |
* NFS4_DR_DID_OP - calling function already did nfs4_start_op |
|
1582 |
* NFS4_DR_RECALL - delegreturned initiated via CB_RECALL |
|
1583 |
* NFS4_DR_REOPEN - do file reopens, if applicable |
|
1584 |
*/ |
|
1585 |
static int |
|
1586 |
nfs4delegreturn_impl(rnode4_t *rp, int flags, struct nfs4_callback_globals *ncg) |
|
1587 |
{ |
|
1588 |
int error = 0; |
|
1589 |
cred_t *cr = NULL; |
|
1590 |
vnode_t *vp; |
|
1591 |
bool_t needrecov = FALSE; |
|
1592 |
bool_t rw_entered = FALSE; |
|
1593 |
bool_t do_reopen; |
|
1594 |
||
1595 |
vp = RTOV4(rp); |
|
1596 |
||
1597 |
/* |
|
1598 |
* If NFS4_DR_DISCARD is set by itself, take a short-cut and |
|
1599 |
* discard without doing an otw DELEGRETURN. This may only be used |
|
1600 |
* by the recovery thread because it bypasses the synchronization |
|
1601 |
* with r_deleg_recall_lock and mi->mi_recovlock. |
|
1602 |
*/ |
|
1603 |
if (flags == NFS4_DR_DISCARD) { |
|
1604 |
nfs4delegreturn_cleanup_impl(rp, NULL, ncg); |
|
1605 |
return (0); |
|
1606 |
} |
|
1607 |
||
1608 |
if (flags & NFS4_DR_DID_OP) { |
|
1609 |
/* |
|
1610 |
* Caller had already done start_op, which means the |
|
1611 |
* r_deleg_recall_lock is already held in READ mode |
|
1612 |
* so we cannot take it in write mode. Return the |
|
1613 |
* delegation asynchronously. |
|
1614 |
* |
|
1615 |
* Remove the NFS4_DR_DID_OP flag so we don't |
|
1616 |
* get stuck looping through here. |
|
1617 |
*/ |
|
1618 |
VN_HOLD(vp); |
|
1619 |
nfs4delegreturn_async(rp, (flags & ~NFS4_DR_DID_OP), FALSE); |
|
1620 |
return (0); |
|
1621 |
} |
|
1622 |
||
1623 |
/* |
|
1624 |
* Take r_deleg_recall_lock to verify we still have a delegation |
|
1625 |
* and to crhold the credential. We have to release the lock |
|
1626 |
* before we call VOP_PUTPAGE or else we'll deadlock. |
|
1627 |
*/ |
|
1628 |
(void) nfs_rw_enter_sig(&rp->r_deleg_recall_lock, RW_WRITER, FALSE); |
|
1629 |
rw_entered = TRUE; |
|
1630 |
if (rp->r_deleg_type == OPEN_DELEGATE_NONE) |
|
1631 |
goto out; |
|
1632 |
cr = rp->r_deleg_cred; |
|
1633 |
crhold(cr); |
|
1634 |
nfs_rw_exit(&rp->r_deleg_recall_lock); |
|
1635 |
rw_entered = FALSE; |
|
1636 |
||
1637 |
/* |
|
1638 |
* Push the modified data back to the server synchronously |
|
1639 |
* before doing DELEGRETURN. |
|
1640 |
*/ |
|
1641 |
if (flags & NFS4_DR_PUSH) |
|
1642 |
(void) VOP_PUTPAGE(vp, 0, 0, 0, cr); |
|
1643 |
||
1644 |
/* |
|
1645 |
* Take r_deleg_recall_lock in WRITE mode, this will prevent |
|
1646 |
* nfs4_is_otw_open_necessary from trying to use the delegation |
|
1647 |
* while the DELEGRETURN is in progress. |
|
1648 |
*/ |
|
1649 |
(void) nfs_rw_enter_sig(&rp->r_deleg_recall_lock, RW_WRITER, FALSE); |
|
1650 |
||
1651 |
rw_entered = TRUE; |
|
1652 |
||
1653 |
if (rp->r_deleg_type == OPEN_DELEGATE_NONE) |
|
1654 |
goto out; |
|
1655 |
||
1656 |
if (flags & NFS4_DR_REOPEN) { |
|
1657 |
/* |
|
1658 |
* If R4RECOVERRP is already set, then skip re-opening |
|
1659 |
* the delegation open streams and go straight to doing |
|
1660 |
* delegreturn. (XXX if the file has failed recovery, then the |
|
1661 |
* delegreturn attempt is likely to be futile.) |
|
1662 |
*/ |
|
1663 |
mutex_enter(&rp->r_statelock); |
|
1664 |
do_reopen = !(rp->r_flags & R4RECOVERRP); |
|
1665 |
mutex_exit(&rp->r_statelock); |
|
1666 |
||
1667 |
if (do_reopen) { |
|
1668 |
error = deleg_reopen(vp, &needrecov, ncg, flags); |
|
1669 |
if (error != 0) { |
|
1670 |
if ((flags & (NFS4_DR_FORCE | NFS4_DR_RECALL)) |
|
1671 |
== 0) |
|
1672 |
goto out; |
|
1673 |
} else if (needrecov) { |
|
1674 |
if ((flags & NFS4_DR_FORCE) == 0) |
|
1675 |
goto out; |
|
1676 |
} |
|
1677 |
} |
|
1678 |
} |
|
1679 |
||
1680 |
if (flags & NFS4_DR_DISCARD) { |
|
1681 |
mntinfo4_t *mi = VTOMI4(RTOV4(rp)); |
|
1682 |
||
1683 |
mutex_enter(&rp->r_statelock); |
|
1684 |
/* |
|
1685 |
* deleg_return_pending is cleared inside of delegation_accept |
|
1686 |
* when a delegation is accepted. if this flag has been |
|
1687 |
* cleared, then a new delegation has overwritten the one we |
|
1688 |
* were about to throw away. |
|
1689 |
*/ |
|
1690 |
if (!rp->r_deleg_return_pending) { |
|
1691 |
mutex_exit(&rp->r_statelock); |
|
1692 |
goto out; |
|
1693 |
} |
|
1694 |
mutex_exit(&rp->r_statelock); |
|
1695 |
(void) nfs_rw_enter_sig(&mi->mi_recovlock, RW_READER, FALSE); |
|
1696 |
nfs4delegreturn_cleanup_impl(rp, NULL, ncg); |
|
1697 |
nfs_rw_exit(&mi->mi_recovlock); |
|
1698 |
} else { |
|
1699 |
error = nfs4_do_delegreturn(rp, flags, cr, ncg); |
|
1700 |
} |
|
1701 |
||
1702 |
out: |
|
1703 |
if (cr) |
|
1704 |
crfree(cr); |
|
1705 |
if (rw_entered) |
|
1706 |
nfs_rw_exit(&rp->r_deleg_recall_lock); |
|
1707 |
return (error); |
|
1708 |
} |
|
1709 |
||
1710 |
int |
|
1711 |
nfs4delegreturn(rnode4_t *rp, int flags) |
|
1712 |
{ |
|
1713 |
struct nfs4_callback_globals *ncg; |
|
1714 |
||
766 | 1715 |
ncg = zone_getspecific(nfs4_callback_zone_key, nfs_zone()); |
0 | 1716 |
ASSERT(ncg != NULL); |
1717 |
||
1718 |
return (nfs4delegreturn_impl(rp, flags, ncg)); |
|
1719 |
} |
|
1720 |
||
1721 |
void |
|
1722 |
nfs4delegreturn_async(rnode4_t *rp, int flags, bool_t trunc) |
|
1723 |
{ |
|
1724 |
struct cb_recall_pass *pp; |
|
1725 |
||
1726 |
pp = kmem_alloc(sizeof (struct cb_recall_pass), KM_SLEEP); |
|
1727 |
pp->rp = rp; |
|
1728 |
pp->flags = flags; |
|
1729 |
pp->truncate = trunc; |
|
1730 |
||
1731 |
/* |
|
1732 |
* Fire up a thread to do the actual delegreturn |
|
1733 |
* Caller must guarantee that the rnode doesn't |
|
1734 |
* vanish (by calling VN_HOLD). |
|
1735 |
*/ |
|
1736 |
||
1737 |
(void) zthread_create(NULL, 0, nfs4delegreturn_thread, pp, 0, |
|
1738 |
minclsyspri); |
|
1739 |
} |
|
1740 |
||
1741 |
static void |
|
1742 |
delegreturn_all_thread(rpcprog_t *pp) |
|
1743 |
{ |
|
1744 |
nfs4_server_t *np; |
|
1745 |
bool_t found = FALSE; |
|
1746 |
rpcprog_t prog; |
|
1747 |
rnode4_t *rp; |
|
1748 |
vnode_t *vp; |
|
1749 |
zoneid_t zoneid = getzoneid(); |
|
1750 |
struct nfs4_callback_globals *ncg; |
|
1751 |
||
1752 |
NFS4_DEBUG(nfs4_drat_debug, |
|
1753 |
(CE_NOTE, "delereturn_all_thread: prog %d\n", *pp)); |
|
1754 |
||
1755 |
prog = *pp; |
|
1756 |
kmem_free(pp, sizeof (*pp)); |
|
1757 |
pp = NULL; |
|
1758 |
||
1759 |
mutex_enter(&nfs4_server_lst_lock); |
|
1760 |
for (np = nfs4_server_lst.forw; np != &nfs4_server_lst; np = np->forw) { |
|
1761 |
if (np->zoneid == zoneid && np->s_program == prog) { |
|
1762 |
mutex_enter(&np->s_lock); |
|
1763 |
found = TRUE; |
|
1764 |
break; |
|
1765 |
} |
|
1766 |
} |
|
1767 |
mutex_exit(&nfs4_server_lst_lock); |
|
1768 |
||
1769 |
/* |
|
1770 |
* It's possible that the nfs4_server which was using this |
|
1771 |
* program number has vanished since this thread is async. |
|
1772 |
* If so, just return. Your work here is finished, my friend. |
|
1773 |
*/ |
|
1774 |
if (!found) |
|
1775 |
goto out; |
|
1776 |
||
1777 |
ncg = np->zone_globals; |
|
1778 |
while ((rp = list_head(&np->s_deleg_list)) != NULL) { |
|
1779 |
vp = RTOV4(rp); |
|
1780 |
VN_HOLD(vp); |
|
1781 |
mutex_exit(&np->s_lock); |
|
1782 |
(void) nfs4delegreturn_impl(rp, NFS4_DR_PUSH|NFS4_DR_REOPEN, |
|
1783 |
ncg); |
|
1784 |
VN_RELE(vp); |
|
1785 |
||
1786 |
/* retake the s_lock for next trip through the loop */ |
|
1787 |
mutex_enter(&np->s_lock); |
|
1788 |
} |
|
1789 |
mutex_exit(&np->s_lock); |
|
1790 |
out: |
|
1791 |
NFS4_DEBUG(nfs4_drat_debug, |
|
1792 |
(CE_NOTE, "delereturn_all_thread: complete\n")); |
|
1793 |
zthread_exit(); |
|
1794 |
} |
|
1795 |
||
1796 |
void |
|
1797 |
nfs4_delegreturn_all(nfs4_server_t *sp) |
|
1798 |
{ |
|
1799 |
rpcprog_t pro, *pp; |
|
1800 |
||
1801 |
mutex_enter(&sp->s_lock); |
|
1802 |
||
1803 |
/* Check to see if the delegation list is empty */ |
|
1804 |
||
1805 |
if (list_head(&sp->s_deleg_list) == NULL) { |
|
1806 |
mutex_exit(&sp->s_lock); |
|
1807 |
return; |
|
1808 |
} |
|
1809 |
/* |
|
1810 |
* Grab the program number; the async thread will use this |
|
1811 |
* to find the nfs4_server. |
|
1812 |
*/ |
|
1813 |
pro = sp->s_program; |
|
1814 |
mutex_exit(&sp->s_lock); |
|
1815 |
pp = kmem_alloc(sizeof (rpcprog_t), KM_SLEEP); |
|
1816 |
*pp = pro; |
|
1817 |
(void) zthread_create(NULL, 0, delegreturn_all_thread, pp, 0, |
|
1818 |
minclsyspri); |
|
1819 |
} |
|
1820 |
||
1821 |
||
1822 |
/* |
|
1823 |
* Discard any delegations |
|
1824 |
* |
|
1825 |
* Iterate over the servers s_deleg_list and |
|
1826 |
* for matching mount-point rnodes discard |
|
1827 |
* the delegation. |
|
1828 |
*/ |
|
1829 |
void |
|
1830 |
nfs4_deleg_discard(mntinfo4_t *mi, nfs4_server_t *sp) |
|
1831 |
{ |
|
1832 |
rnode4_t *rp, *next; |
|
1833 |
mntinfo4_t *r_mi; |
|
1834 |
struct nfs4_callback_globals *ncg; |
|
1835 |
||
1836 |
ASSERT(mutex_owned(&sp->s_lock)); |
|
1837 |
ncg = sp->zone_globals; |
|
1838 |
||
1839 |
for (rp = list_head(&sp->s_deleg_list); rp != NULL; rp = next) { |
|
1840 |
r_mi = VTOMI4(RTOV4(rp)); |
|
1841 |
next = list_next(&sp->s_deleg_list, rp); |
|
1842 |
||
1843 |
if (r_mi != mi) { |
|
1844 |
/* |
|
1845 |
* Skip if this rnode is in not on the |
|
1846 |
* same mount-point |
|
1847 |
*/ |
|
1848 |
continue; |
|
1849 |
} |
|
1850 |
||
1851 |
ASSERT(rp->r_deleg_type == OPEN_DELEGATE_READ); |
|
1852 |
||
1853 |
#ifdef DEBUG |
|
1854 |
if (nfs4_client_recov_debug) { |
|
1855 |
zprintf(getzoneid(), |
|
1856 |
"nfs4_deleg_discard: matched rnode %p " |
|
1857 |
"-- discarding delegation\n", (void *)rp); |
|
1858 |
} |
|
1859 |
#endif |
|
1860 |
mutex_enter(&rp->r_statev4_lock); |
|
1861 |
/* |
|
1862 |
* Free the cred originally held when the delegation |
|
1863 |
* was granted. Also need to decrement the refcnt |
|
1864 |
* on this server for each delegation we discard |
|
1865 |
*/ |
|
1866 |
if (rp->r_deleg_cred) |
|
1867 |
crfree(rp->r_deleg_cred); |
|
1868 |
rp->r_deleg_cred = NULL; |
|
1869 |
rp->r_deleg_type = OPEN_DELEGATE_NONE; |
|
1870 |
rp->r_deleg_needs_recovery = OPEN_DELEGATE_NONE; |
|
1871 |
rp->r_deleg_needs_recall = FALSE; |
|
1872 |
ASSERT(sp->s_refcnt > 1); |
|
1873 |
sp->s_refcnt--; |
|
1874 |
list_remove(&sp->s_deleg_list, rp); |
|
1875 |
mutex_exit(&rp->r_statev4_lock); |
|
1876 |
nfs4_dec_state_ref_count_nolock(sp, mi); |
|
1877 |
ncg->nfs4_callback_stats.delegations.value.ui64--; |
|
1878 |
} |
|
1879 |
} |
|
1880 |
||
1881 |
/* |
|
1882 |
* Reopen any open streams that were covered by the given file's |
|
1883 |
* delegation. |
|
1884 |
* Returns zero or an errno value. If there was no error, *recovp |
|
1885 |
* indicates whether recovery was initiated. |
|
1886 |
*/ |
|
1887 |
||
1888 |
static int |
|
1889 |
deleg_reopen(vnode_t *vp, bool_t *recovp, struct nfs4_callback_globals *ncg, |
|
1890 |
int flags) |
|
1891 |
{ |
|
1892 |
nfs4_open_stream_t *osp; |
|
1893 |
nfs4_recov_state_t recov_state; |
|
1894 |
bool_t needrecov = FALSE; |
|
1895 |
mntinfo4_t *mi; |
|
1896 |
rnode4_t *rp; |
|
1897 |
nfs4_error_t e = { 0, NFS4_OK, RPC_SUCCESS }; |
|
1898 |
int claimnull; |
|
1899 |
||
1900 |
mi = VTOMI4(vp); |
|
1901 |
rp = VTOR4(vp); |
|
1902 |
||
1903 |
recov_state.rs_flags = 0; |
|
1904 |
recov_state.rs_num_retry_despite_err = 0; |
|
1905 |
||
1906 |
retry: |
|
1907 |
if ((e.error = nfs4_start_op(mi, vp, NULL, &recov_state)) != 0) { |
|
1908 |
return (e.error); |
|
1909 |
} |
|
1910 |
||
1911 |
/* |
|
1912 |
* if we mean to discard the delegation, it must be BAD, so don't |
|
1913 |
* use it when doing the reopen or it will fail too. |
|
1914 |
*/ |
|
1915 |
claimnull = (flags & NFS4_DR_DISCARD); |
|
1916 |
/* |
|
1917 |
* Loop through the open streams for this rnode to find |
|
1918 |
* all of the ones created using the delegation state ID. |
|
1919 |
* Each of these needs to be re-opened. |
|
1920 |
*/ |
|
1921 |
||
1922 |
while ((osp = get_next_deleg_stream(rp, claimnull)) != NULL) { |
|
1923 |
||
1924 |
if (claimnull) { |
|
1925 |
nfs4_reopen(vp, osp, &e, CLAIM_NULL, FALSE, FALSE); |
|
1926 |
} else { |
|
1927 |
ncg->nfs4_callback_stats.claim_cur.value.ui64++; |
|
1928 |
||
1929 |
nfs4_reopen(vp, osp, &e, CLAIM_DELEGATE_CUR, FALSE, |
|
1930 |
FALSE); |
|
1931 |
if (e.error == 0 && e.stat == NFS4_OK) |
|
1932 |
ncg->nfs4_callback_stats. |
|
1933 |
claim_cur_ok.value.ui64++; |
|
1934 |
} |
|
1935 |
||
1936 |
if (e.error == EAGAIN) { |
|
1937 |
nfs4_end_op(mi, vp, NULL, &recov_state, TRUE); |
|
1938 |
goto retry; |
|
1939 |
} |
|
1940 |
||
1941 |
/* |
|
1942 |
* if error is EINTR, ETIMEDOUT, or NFS4_FRC_UNMT_ERR, then |
|
1943 |
* recovery has already been started inside of nfs4_reopen. |
|
1944 |
*/ |
|
1945 |
if (e.error == EINTR || e.error == ETIMEDOUT || |
|
1946 |
NFS4_FRC_UNMT_ERR(e.error, vp->v_vfsp)) { |
|
1947 |
open_stream_rele(osp, rp); |
|
1948 |
break; |
|
1949 |
} |
|
1950 |
||
1951 |
needrecov = nfs4_needs_recovery(&e, TRUE, vp->v_vfsp); |
|
1952 |
||
1953 |
if (e.error != 0 && !needrecov) { |
|
1954 |
/* |
|
1955 |
* Recovery is not possible, but don't give up yet; |
|
1956 |
* we'd still like to do delegreturn after |
|
1957 |
* reopening as many streams as possible. |
|
1958 |
* Continue processing the open streams. |
|
1959 |
*/ |
|
1960 |
||
1961 |
ncg->nfs4_callback_stats.recall_failed.value.ui64++; |
|
1962 |
||
1963 |
} else if (needrecov) { |
|
1964 |
/* |
|
1965 |
* Start recovery and bail out. The recovery |
|
1966 |
* thread will take it from here. |
|
1967 |
*/ |
|
1968 |
(void) nfs4_start_recovery(&e, mi, vp, NULL, NULL, |
|
1969 |
NULL, OP_OPEN, NULL); |
|
1970 |
open_stream_rele(osp, rp); |
|
1971 |
*recovp = TRUE; |
|
1972 |
break; |
|
1973 |
} |
|
1974 |
||
1975 |
open_stream_rele(osp, rp); |
|
1976 |
} |
|
1977 |
||
1978 |
nfs4_end_op(mi, vp, NULL, &recov_state, needrecov); |
|
1979 |
||
1980 |
return (e.error); |
|
1981 |
} |
|
1982 |
||
1983 |
/* |
|
1984 |
* get_next_deleg_stream - returns the next open stream which |
|
1985 |
* represents a delegation for this rnode. In order to assure |
|
1986 |
* forward progress, the caller must guarantee that each open |
|
1987 |
* stream returned is changed so that a future call won't return |
|
1988 |
* it again. |
|
1989 |
* |
|
1990 |
* There are several ways for the open stream to change. If the open |
|
1991 |
* stream is !os_delegation, then we aren't interested in it. Also, if |
|
1992 |
* either os_failed_reopen or !os_valid, then don't return the osp. |
|
1993 |
* |
|
1994 |
* If claimnull is false (doing reopen CLAIM_DELEGATE_CUR) then return |
|
1995 |
* the osp if it is an os_delegation open stream. Also, if the rnode still |
|
1996 |
* has r_deleg_return_pending, then return the os_delegation osp. Lastly, |
|
1997 |
* if the rnode's r_deleg_stateid is different from the osp's open_stateid, |
|
1998 |
* then return the osp. |
|
1999 |
* |
|
2000 |
* We have already taken the 'r_deleg_recall_lock' as WRITER, which |
|
2001 |
* prevents new OPENs from going OTW (as start_fop takes this |
|
2002 |
* lock in READ mode); thus, no new open streams can be created |
|
2003 |
* (which inheretly means no new delegation open streams are |
|
2004 |
* being created). |
|
2005 |
*/ |
|
2006 |
||
2007 |
static nfs4_open_stream_t * |
|
2008 |
get_next_deleg_stream(rnode4_t *rp, int claimnull) |
|
2009 |
{ |
|
2010 |
nfs4_open_stream_t *osp; |
|
2011 |
||
2012 |
ASSERT(nfs_rw_lock_held(&rp->r_deleg_recall_lock, RW_WRITER)); |
|
2013 |
||
2014 |
/* |
|
2015 |
* Search through the list of open streams looking for |
|
2016 |
* one that was created while holding the delegation. |
|
2017 |
*/ |
|
2018 |
mutex_enter(&rp->r_os_lock); |
|
2019 |
for (osp = list_head(&rp->r_open_streams); osp != NULL; |
|
2020 |
osp = list_next(&rp->r_open_streams, osp)) { |
|
2021 |
mutex_enter(&osp->os_sync_lock); |
|
2022 |
if (!osp->os_delegation || osp->os_failed_reopen || |
|
2023 |
!osp->os_valid) { |
|
2024 |
mutex_exit(&osp->os_sync_lock); |
|
2025 |
continue; |
|
2026 |
} |
|
2027 |
if (!claimnull || rp->r_deleg_return_pending || |
|
2028 |
!stateid4_cmp(&osp->open_stateid, &rp->r_deleg_stateid)) { |
|
2029 |
osp->os_ref_count++; |
|
2030 |
mutex_exit(&osp->os_sync_lock); |
|
2031 |
mutex_exit(&rp->r_os_lock); |
|
2032 |
return (osp); |
|
2033 |
} |
|
2034 |
mutex_exit(&osp->os_sync_lock); |
|
2035 |
} |
|
2036 |
mutex_exit(&rp->r_os_lock); |
|
2037 |
||
2038 |
return (NULL); |
|
2039 |
} |
|
2040 |
||
2041 |
static void |
|
2042 |
nfs4delegreturn_thread(struct cb_recall_pass *args) |
|
2043 |
{ |
|
2044 |
rnode4_t *rp; |
|
2045 |
vnode_t *vp; |
|
2046 |
cred_t *cr; |
|
2047 |
int dtype, error, flags; |
|
2048 |
bool_t rdirty, rip; |
|
2049 |
kmutex_t cpr_lock; |
|
2050 |
callb_cpr_t cpr_info; |
|
2051 |
struct nfs4_callback_globals *ncg; |
|
2052 |
||
766 | 2053 |
ncg = zone_getspecific(nfs4_callback_zone_key, nfs_zone()); |
0 | 2054 |
ASSERT(ncg != NULL); |
2055 |
||
2056 |
mutex_init(&cpr_lock, NULL, MUTEX_DEFAULT, NULL); |
|
2057 |
||
2058 |
CALLB_CPR_INIT(&cpr_info, &cpr_lock, callb_generic_cpr, |
|
2059 |
"nfsv4delegRtn"); |
|
2060 |
||
2061 |
rp = args->rp; |
|
2062 |
vp = RTOV4(rp); |
|
2063 |
||
2064 |
mutex_enter(&rp->r_statev4_lock); |
|
2065 |
if (rp->r_deleg_type == OPEN_DELEGATE_NONE) { |
|
2066 |
mutex_exit(&rp->r_statev4_lock); |
|
2067 |
goto out; |
|
2068 |
} |
|
2069 |
mutex_exit(&rp->r_statev4_lock); |
|
2070 |
||
2071 |
/* |
|
2072 |
* Take the read-write lock in read mode to prevent other |
|
2073 |
* threads from modifying the data during the recall. This |
|
2074 |
* doesn't affect mmappers. |
|
2075 |
*/ |
|
2076 |
(void) nfs_rw_enter_sig(&rp->r_rwlock, RW_READER, FALSE); |
|
2077 |
||
2078 |
/* Proceed with delegreturn */ |
|
2079 |
||
2080 |
mutex_enter(&rp->r_statev4_lock); |
|
2081 |
if (rp->r_deleg_type == OPEN_DELEGATE_NONE) { |
|
2082 |
mutex_exit(&rp->r_statev4_lock); |
|
2083 |
nfs_rw_exit(&rp->r_rwlock); |
|
2084 |
goto out; |
|
2085 |
} |
|
2086 |
dtype = rp->r_deleg_type; |
|
2087 |
cr = rp->r_deleg_cred; |
|
2088 |
ASSERT(cr != NULL); |
|
2089 |
crhold(cr); |
|
2090 |
mutex_exit(&rp->r_statev4_lock); |
|
2091 |
||
2092 |
flags = args->flags; |
|
2093 |
||
2094 |
/* |
|
2095 |
* If the file is being truncated at the server, then throw |
|
2096 |
* away all of the pages, it doesn't matter what flavor of |
|
2097 |
* delegation we have. |
|
2098 |
*/ |
|
2099 |
||
2100 |
if (args->truncate) { |
|
2101 |
ncg->nfs4_callback_stats.recall_trunc.value.ui64++; |
|
2102 |
nfs4_invalidate_pages(vp, 0, cr); |
|
2103 |
} else if (dtype == OPEN_DELEGATE_WRITE) { |
|
2104 |
||
2105 |
mutex_enter(&rp->r_statelock); |
|
2106 |
rdirty = rp->r_flags & R4DIRTY; |
|
2107 |
mutex_exit(&rp->r_statelock); |
|
2108 |
||
2109 |
if (rdirty) { |
|
2110 |
error = VOP_PUTPAGE(vp, 0, 0, 0, cr); |
|
2111 |
||
2112 |
if (error) |
|
2113 |
CB_WARN1("nfs4delegreturn_thread:" |
|
2114 |
" VOP_PUTPAGE: %d\n", error); |
|
2115 |
} |
|
2116 |
/* turn off NFS4_DR_PUSH because we just did that above. */ |
|
2117 |
flags &= ~NFS4_DR_PUSH; |
|
2118 |
} |
|
2119 |
||
2120 |
mutex_enter(&rp->r_statelock); |
|
2121 |
rip = rp->r_flags & R4RECOVERRP; |
|
2122 |
mutex_exit(&rp->r_statelock); |
|
2123 |
||
2124 |
/* If a failed recovery is indicated, discard the pages */ |
|
2125 |
||
2126 |
if (rip) { |
|
2127 |
||
2128 |
error = VOP_PUTPAGE(vp, 0, 0, B_INVAL, cr); |
|
2129 |
||
2130 |
if (error) |
|
2131 |
CB_WARN1("nfs4delegreturn_thread: VOP_PUTPAGE: %d\n", |
|
2132 |
error); |
|
2133 |
} |
|
2134 |
||
2135 |
/* |
|
2136 |
* Pass the flags to nfs4delegreturn_impl, but be sure not to pass |
|
2137 |
* NFS4_DR_DID_OP, which just calls nfs4delegreturn_async again. |
|
2138 |
*/ |
|
2139 |
flags &= ~NFS4_DR_DID_OP; |
|
2140 |
||
2141 |
(void) nfs4delegreturn_impl(rp, flags, ncg); |
|
2142 |
||
2143 |
nfs_rw_exit(&rp->r_rwlock); |
|
2144 |
crfree(cr); |
|
2145 |
out: |
|
2146 |
kmem_free(args, sizeof (struct cb_recall_pass)); |
|
2147 |
VN_RELE(vp); |
|
2148 |
mutex_enter(&cpr_lock); |
|
2149 |
CALLB_CPR_EXIT(&cpr_info); |
|
2150 |
mutex_destroy(&cpr_lock); |
|
2151 |
zthread_exit(); |
|
2152 |
} |
|
2153 |
||
2154 |
/* |
|
2155 |
* This function has one assumption that the caller of this function is |
|
2156 |
* either doing recovery (therefore cannot call nfs4_start_op) or has |
|
2157 |
* already called nfs4_start_op(). |
|
2158 |
*/ |
|
2159 |
void |
|
2160 |
nfs4_delegation_accept(rnode4_t *rp, open_claim_type4 claim, OPEN4res *res, |
|
2161 |
nfs4_ga_res_t *garp, cred_t *cr) |
|
2162 |
{ |
|
2163 |
open_read_delegation4 *orp; |
|
2164 |
open_write_delegation4 *owp; |
|
2165 |
nfs4_server_t *np; |
|
2166 |
bool_t already = FALSE; |
|
2167 |
bool_t recall = FALSE; |
|
2168 |
bool_t valid_garp = TRUE; |
|
2169 |
long mapcnt; |
|
2170 |
uint_t rflag; |
|
2171 |
mntinfo4_t *mi; |
|
2172 |
bool_t recov; |
|
2173 |
struct nfs4_callback_globals *ncg; |
|
536
ece52d2759f6
6300246 nfs4_delegation_accept(): Deadlock: cycle in blocking chain
rmesta
parents:
0
diff
changeset
|
2174 |
open_delegation_type4 odt; |
0 | 2175 |
|
766 | 2176 |
ncg = zone_getspecific(nfs4_callback_zone_key, nfs_zone()); |
0 | 2177 |
ASSERT(ncg != NULL); |
2178 |
||
2179 |
mutex_enter(&rp->r_statev4_lock); |
|
2180 |
||
2181 |
if (rp->r_deleg_type == OPEN_DELEGATE_READ || |
|
2182 |
rp->r_deleg_type == OPEN_DELEGATE_WRITE) |
|
2183 |
already = TRUE; |
|
2184 |
||
536
ece52d2759f6
6300246 nfs4_delegation_accept(): Deadlock: cycle in blocking chain
rmesta
parents:
0
diff
changeset
|
2185 |
odt = res->delegation.delegation_type; |
ece52d2759f6
6300246 nfs4_delegation_accept(): Deadlock: cycle in blocking chain
rmesta
parents:
0
diff
changeset
|
2186 |
mutex_exit(&rp->r_statev4_lock); |
ece52d2759f6
6300246 nfs4_delegation_accept(): Deadlock: cycle in blocking chain
rmesta
parents:
0
diff
changeset
|
2187 |
|
ece52d2759f6
6300246 nfs4_delegation_accept(): Deadlock: cycle in blocking chain
rmesta
parents:
0
diff
changeset
|
2188 |
if (odt == OPEN_DELEGATE_READ) { |
ece52d2759f6
6300246 nfs4_delegation_accept(): Deadlock: cycle in blocking chain
rmesta
parents:
0
diff
changeset
|
2189 |
|
ece52d2759f6
6300246 nfs4_delegation_accept(): Deadlock: cycle in blocking chain
rmesta
parents:
0
diff
changeset
|
2190 |
mutex_enter(&rp->r_statev4_lock); |
0 | 2191 |
rp->r_deleg_type = res->delegation.delegation_type; |
2192 |
orp = &res->delegation.open_delegation4_u.read; |
|
2193 |
rp->r_deleg_stateid = orp->stateid; |
|
2194 |
rp->r_deleg_perms = orp->permissions; |
|
2195 |
recall = orp->recall; |
|
536
ece52d2759f6
6300246 nfs4_delegation_accept(): Deadlock: cycle in blocking chain
rmesta
parents:
0
diff
changeset
|
2196 |
mutex_exit(&rp->r_statev4_lock); |
0 | 2197 |
|
2198 |
ncg->nfs4_callback_stats.delegations.value.ui64++; |
|
2199 |
ncg->nfs4_callback_stats.delegaccept_r.value.ui64++; |
|
2200 |
||
536
ece52d2759f6
6300246 nfs4_delegation_accept(): Deadlock: cycle in blocking chain
rmesta
parents:
0
diff
changeset
|
2201 |
} else if (odt == OPEN_DELEGATE_WRITE) { |
ece52d2759f6
6300246 nfs4_delegation_accept(): Deadlock: cycle in blocking chain
rmesta
parents:
0
diff
changeset
|
2202 |
|
ece52d2759f6
6300246 nfs4_delegation_accept(): Deadlock: cycle in blocking chain
rmesta
parents:
0
diff
changeset
|
2203 |
mutex_enter(&rp->r_statelock); |
ece52d2759f6
6300246 nfs4_delegation_accept(): Deadlock: cycle in blocking chain
rmesta
parents:
0
diff
changeset
|
2204 |
mutex_enter(&rp->r_statev4_lock); |
0 | 2205 |
rp->r_deleg_type = res->delegation.delegation_type; |
2206 |
owp = &res->delegation.open_delegation4_u.write; |
|
2207 |
rp->r_deleg_stateid = owp->stateid; |
|
2208 |
rp->r_deleg_perms = owp->permissions; |
|
2209 |
rp->r_deleg_limit = owp->space_limit; |
|
2210 |
recall = owp->recall; |
|
2211 |
||
2212 |
if (garp == NULL || !garp->n4g_change_valid) { |
|
2213 |
valid_garp = FALSE; |
|
2214 |
rp->r_deleg_change = 0; |
|
2215 |
rp->r_deleg_change_grant = 0; |
|
2216 |
} else { |
|
2217 |
rp->r_deleg_change = garp->n4g_change; |
|
2218 |
rp->r_deleg_change_grant = garp->n4g_change; |
|
2219 |
} |
|
2220 |
mapcnt = rp->r_mapcnt; |
|
2221 |
rflag = rp->r_flags; |
|
2222 |
||
2223 |
/* |
|
2224 |
* Update the delegation change attribute if |
|
2225 |
* there are mappers for the file is dirty. This |
|
2226 |
* might be the case during recovery after server |
|
2227 |
* reboot. |
|
2228 |
*/ |
|
2229 |
if (mapcnt > 0 || rflag & R4DIRTY) |
|
2230 |
rp->r_deleg_change++; |
|
2231 |
||
2232 |
NFS4_DEBUG(nfs4_callback_debug, (CE_NOTE, |
|
2233 |
"nfs4_delegation_accept: r_deleg_change: 0x%x\n", |
|
2234 |
(int)(rp->r_deleg_change >> 32))); |
|
2235 |
NFS4_DEBUG(nfs4_callback_debug, (CE_NOTE, |
|
2236 |
"nfs4_delegation_accept: r_delg_change_grant: 0x%x\n", |
|
2237 |
(int)(rp->r_deleg_change_grant >> 32))); |
|
2238 |
||
2239 |
#ifdef DEBUG |
|
2240 |
if (nfs4_use_phony_limit == 1) |
|
2241 |
rp->r_deleg_limit = nfs4_deleg_space_phony; |
|
2242 |
if (nfs4_use_phony_limit == 2) { |
|
2243 |
rp->r_deleg_limit = nfs4_deleg_space_phony2; |
|
2244 |
rp->r_deleg_limit.nfs_space_limit4_u.mod_blocks = |
|
2245 |
nfs4_deleg_space_phonyl; |
|
2246 |
} |
|
2247 |
#endif |
|
536
ece52d2759f6
6300246 nfs4_delegation_accept(): Deadlock: cycle in blocking chain
rmesta
parents:
0
diff
changeset
|
2248 |
mutex_exit(&rp->r_statev4_lock); |
ece52d2759f6
6300246 nfs4_delegation_accept(): Deadlock: cycle in blocking chain
rmesta
parents:
0
diff
changeset
|
2249 |
mutex_exit(&rp->r_statelock); |
ece52d2759f6
6300246 nfs4_delegation_accept(): Deadlock: cycle in blocking chain
rmesta
parents:
0
diff
changeset
|
2250 |
|
ece52d2759f6
6300246 nfs4_delegation_accept(): Deadlock: cycle in blocking chain
rmesta
parents:
0
diff
changeset
|
2251 |
ncg->nfs4_callback_stats.delegations.value.ui64++; |
ece52d2759f6
6300246 nfs4_delegation_accept(): Deadlock: cycle in blocking chain
rmesta
parents:
0
diff
changeset
|
2252 |
ncg->nfs4_callback_stats.delegaccept_rw.value.ui64++; |
0 | 2253 |
|
2254 |
#ifdef DEBUG |
|
2255 |
||
2256 |
} else if (nfs4_deleg_accept_phony == OPEN_DELEGATE_READ) { |
|
2257 |
||
536
ece52d2759f6
6300246 nfs4_delegation_accept(): Deadlock: cycle in blocking chain
rmesta
parents:
0
diff
changeset
|
2258 |
mutex_enter(&rp->r_statev4_lock); |
0 | 2259 |
rp->r_deleg_type = OPEN_DELEGATE_READ; |
2260 |
rp->r_deleg_stateid = nfs4_deleg_any; |
|
2261 |
rp->r_deleg_perms = nfs4_deleg_ace_phony; |
|
2262 |
rp->r_deleg_change = nfs4_deleg_change_phony; |
|
2263 |
rp->r_deleg_change_grant = rp->r_deleg_change; |
|
536
ece52d2759f6
6300246 nfs4_delegation_accept(): Deadlock: cycle in blocking chain
rmesta
parents:
0
diff
changeset
|
2264 |
mutex_exit(&rp->r_statev4_lock); |
0 | 2265 |
|
2266 |
} else if (nfs4_deleg_accept_phony == OPEN_DELEGATE_WRITE) { |
|
2267 |
||
536
ece52d2759f6
6300246 nfs4_delegation_accept(): Deadlock: cycle in blocking chain
rmesta
parents:
0
diff
changeset
|
2268 |
mutex_enter(&rp->r_statev4_lock); |
0 | 2269 |
rp->r_deleg_type = OPEN_DELEGATE_WRITE; |
2270 |
rp->r_deleg_stateid = nfs4_deleg_any; |
|
2271 |
rp->r_deleg_perms = nfs4_deleg_ace_phony; |
|
2272 |
rp->r_deleg_limit = nfs4_deleg_space_phony; |
|
2273 |
rp->r_deleg_change = nfs4_deleg_change_phony; |
|
2274 |
rp->r_deleg_change_grant = rp->r_deleg_change; |
|
536
ece52d2759f6
6300246 nfs4_delegation_accept(): Deadlock: cycle in blocking chain
rmesta
parents:
0
diff
changeset
|
2275 |
mutex_exit(&rp->r_statev4_lock); |
0 | 2276 |
|
2277 |
#endif |
|
2278 |
} else { |
|
2279 |
||
2280 |
if (already) { |
|
2281 |
switch (claim) { |
|
2282 |
||
2283 |
case CLAIM_NULL: |
|
2284 |
case CLAIM_PREVIOUS: |
|
2285 |
/* |
|
2286 |
* The file may already have a delegation when |
|
2287 |
* it is reopened during recovery. In this |
|
2288 |
* case, we consider the delegation to no longer |
|
2289 |
* be valid. As a courtesy, attempt to return |
|
2290 |
* the delegation. |
|
2291 |
*/ |
|
2292 |
mi = VTOMI4(RTOV4(rp)); |
|
2293 |
mutex_enter(&mi->mi_lock); |
|
2294 |
recov = mi->mi_recovflags & MI4R_REOPEN_FILES; |
|
2295 |
mutex_exit(&mi->mi_lock); |
|
2296 |
||
2297 |
/* |
|
2298 |
* We need to hold rp->r_statev4_lock while |
|
2299 |
* checking rp->r_deleg_return_pending and |
|
2300 |
* when calling nfs4_dlistadd() if we're in |
|
2301 |
* recovery. |
|
2302 |
*/ |
|
2303 |
mutex_enter(&rp->r_statev4_lock); |
|
2304 |
if (rp->r_deleg_return_pending == TRUE) { |
|
2305 |
/* |
|
2306 |
* We're alreading in the throes of |
|
2307 |
* returning a delegation. Drop |
|
2308 |
* the lock and head for the return. |
|
2309 |
*/ |
|
2310 |
mutex_exit(&rp->r_statev4_lock); |
|
2311 |
} else if (recov) { |
|
2312 |
/* |
|
2313 |
* Cannot call delegreturn from inside |
|
2314 |
* of recovery or VOP_PUTPAGE will hang |
|
2315 |
* due to nfs4_start_fop call in |
|
2316 |
* nfs4write. Use dlistadd to add the |
|
2317 |
* rnode to the list of rnodes needing |
|
2318 |
* cleaning. |
|
2319 |
* |
|
2320 |
* NB: We're in recover so don't reopen |
|
2321 |
*/ |
|
2322 |
nfs4_dlistadd(rp, ncg, |
|
2323 |
NFS4_DR_PUSH|NFS4_DR_DISCARD); |
|
2324 |
mutex_exit(&rp->r_statev4_lock); |
|
2325 |
} else { |
|
2326 |
mutex_exit(&rp->r_statev4_lock); |
|
2327 |
/* XXX - Do we need to reopen? */ |
|
2328 |
(void) nfs4delegreturn_impl(rp, |
|
2329 |
(NFS4_DR_PUSH | |
|
2330 |
NFS4_DR_DID_OP | |
|
2331 |
NFS4_DR_REOPEN), |
|
2332 |
ncg); |
|
2333 |
} |
|
2334 |
break; |
|
2335 |
||
2336 |
default: |
|
2337 |
/* |
|
2338 |
* CLAIM_DELEGATE_CUR, CLAIM_DELEGATE_PREV |
|
2339 |
* fall through here |
|
2340 |
*/ |
|
2341 |
break; |
|
2342 |
} |
|
2343 |
} |
|
2344 |
||
2345 |
/* No delegation granted, get out. */ |
|
2346 |
return; |
|
2347 |
} |
|
2348 |
||
536
ece52d2759f6
6300246 nfs4_delegation_accept(): Deadlock: cycle in blocking chain
rmesta
parents:
0
diff
changeset
|
2349 |
mutex_enter(&rp->r_statev4_lock); |
0 | 2350 |
rp->r_deleg_return_pending = FALSE; |
2351 |
rp->r_deleg_needs_recovery = OPEN_DELEGATE_NONE; |
|
2352 |
if (claim == CLAIM_PREVIOUS) |
|
2353 |
rp->r_deleg_needs_recall = recall; |
|
2354 |
||
2355 |
#ifdef DEBUG |
|
2356 |
if (nfs4_use_phony_recall) |
|
2357 |
rp->r_deleg_needs_recall = nfs4_phony_recall_v; |
|
2358 |
#endif |
|
2359 |
||
2360 |
/* |
|
2361 |
* If the server has requested a recall, then put the |
|
2362 |
* vnode on a list of files which need to be cleaned. |
|
2363 |
* This will be done later by the recovery thread to |
|
2364 |
* avoid a deadlock. If this were a CLAIM_NULL open |
|
2365 |
* and the server set recall, then the server is just |
|
2366 |
* confused; the delegation will be returned eventually. |
|
2367 |
*/ |
|
2368 |
if (rp->r_deleg_needs_recall) |
|
2369 |
nfs4_dlistadd(rp, ncg, NFS4_DR_PUSH|NFS4_DR_REOPEN); |
|
2370 |
||
2371 |
if (already == FALSE) { |
|
2372 |
rp->r_deleg_cred = cr; |
|
2373 |
crhold(cr); |
|
2374 |
} |
|
2375 |
mutex_exit(&rp->r_statev4_lock); |
|
2376 |
||
2377 |
if (already == FALSE) { |
|
2378 |
||
2379 |
/* |
|
2380 |
* Add this rnode to the list of rnodes with delegations |
|
2381 |
* for this nfs4_server. find_nfs4_server returns with |
|
2382 |
* the mutex locked, so don't forget to mutex exit. |
|
2383 |
*/ |
|
2384 |
||
2385 |
if ((np = find_nfs4_server(VTOMI4(RTOV4(rp)))) == NULL) { |
|
2386 |
||
2387 |
mutex_enter(&rp->r_statev4_lock); |
|
2388 |
rp->r_deleg_type = OPEN_DELEGATE_NONE; |
|
2389 |
mutex_exit(&rp->r_statev4_lock); |
|
2390 |
return; |
|
2391 |
} |
|
2392 |
||
2393 |
list_insert_head(&np->s_deleg_list, rp); |
|
2394 |
/* added list node gets a reference */ |
|
2395 |
np->s_refcnt++; |
|
2396 |
nfs4_inc_state_ref_count_nolock(np, VTOMI4(RTOV4(rp))); |
|
2397 |
mutex_exit(&np->s_lock); |
|
2398 |
nfs4_server_rele(np); |
|
2399 |
} |
|
2400 |
||
2401 |
/* |
|
2402 |
* This call to nfs4delegreturn assumes that nfs4_start_op MUST |
|
2403 |
* not be called by nfs4delegreturn. |
|
2404 |
*/ |
|
2405 |
if (nfs4_delegreturn_policy == IMMEDIATE || !valid_garp) |
|
2406 |
(void) nfs4delegreturn_impl(rp, |
|
2407 |
NFS4_DR_PUSH|NFS4_DR_DID_OP|NFS4_DR_REOPEN, ncg); |
|
2408 |
} |
|
2409 |
||
2410 |
/* |
|
2411 |
* nfs4delegabandon - Abandon the delegation on an rnode4. This code |
|
2412 |
* is called when the client receives EXPIRED, BAD_STATEID, OLD_STATEID |
|
2413 |
* or BADSEQID and the recovery code is unable to recover. Push any |
|
2414 |
* dirty data back to the server and return the delegation (if any). |
|
2415 |
*/ |
|
2416 |
||
2417 |
void |
|
2418 |
nfs4delegabandon(rnode4_t *rp) |
|
2419 |
{ |
|
2420 |
vnode_t *vp; |
|
2421 |
struct cb_recall_pass *pp; |
|
2422 |
open_delegation_type4 dt; |
|
2423 |
||
2424 |
mutex_enter(&rp->r_statev4_lock); |
|
2425 |
dt = rp->r_deleg_type; |
|
2426 |
mutex_exit(&rp->r_statev4_lock); |
|
2427 |
||
2428 |
if (dt == OPEN_DELEGATE_NONE) |
|
2429 |
return; |
|
2430 |
||
2431 |
vp = RTOV4(rp); |
|
2432 |
VN_HOLD(vp); |
|
2433 |
||
2434 |
pp = kmem_alloc(sizeof (struct cb_recall_pass), KM_SLEEP); |
|
2435 |
pp->rp = rp; |
|
2436 |
/* |
|
2437 |
* Recovery on the file has failed and we want to return |
|
2438 |
* the delegation. We don't want to reopen files and |
|
2439 |
* nfs4delegreturn_thread() figures out what to do about |
|
2440 |
* the data. The only thing to do is attempt to return |
|
2441 |
* the delegation. |
|
2442 |
*/ |
|
2443 |
pp->flags = 0; |
|
2444 |
pp->truncate = FALSE; |
|
2445 |
||
2446 |
/* |
|
2447 |
* Fire up a thread to do the delegreturn; this is |
|
2448 |
* necessary because we could be inside a GETPAGE or |
|
2449 |
* PUTPAGE and we cannot do another one. |
|
2450 |
*/ |
|
2451 |
||
2452 |
(void) zthread_create(NULL, 0, nfs4delegreturn_thread, pp, 0, |
|
2453 |
minclsyspri); |
|
2454 |
} |
|
2455 |
||
2456 |
static int |
|
2457 |
wait_for_recall1(vnode_t *vp, nfs4_op_hint_t op, nfs4_recov_state_t *rsp, |
|
2458 |
int flg) |
|
2459 |
{ |
|
2460 |
rnode4_t *rp; |
|
2461 |
int error = 0; |
|
2462 |
||
2463 |
#ifdef lint |
|
2464 |
op = op; |
|
2465 |
#endif |
|
2466 |
||
2467 |
if (vp && vp->v_type == VREG) { |
|
2468 |
rp = VTOR4(vp); |
|
2469 |
||
2470 |
/* |
|
2471 |
* Take r_deleg_recall_lock in read mode to synchronize |
|
2472 |
* with delegreturn. |
|
2473 |
*/ |
|
2474 |
error = nfs_rw_enter_sig(&rp->r_deleg_recall_lock, |
|
2475 |
RW_READER, INTR4(vp)); |
|
2476 |
||
2477 |
if (error == 0) |
|
2478 |
rsp->rs_flags |= flg; |
|
2479 |
||
2480 |
} |
|
2481 |
return (error); |
|
2482 |
} |
|
2483 |
||
2484 |
void |
|
2485 |
nfs4_end_op_recall(vnode_t *vp1, vnode_t *vp2, nfs4_recov_state_t *rsp) |
|
2486 |
{ |
|
2487 |
NFS4_DEBUG(nfs4_recall_debug, |
|
2488 |
(CE_NOTE, "nfs4_end_op_recall: 0x%p, 0x%p\n", |
|
2489 |
(void *)vp1, (void *)vp2)); |
|
2490 |
||
2491 |
if (vp2 && rsp->rs_flags & NFS4_RS_RECALL_HELD2) |
|
2492 |
nfs_rw_exit(&VTOR4(vp2)->r_deleg_recall_lock); |
|
2493 |
if (vp1 && rsp->rs_flags & NFS4_RS_RECALL_HELD1) |
|
2494 |
nfs_rw_exit(&VTOR4(vp1)->r_deleg_recall_lock); |
|
2495 |
} |
|
2496 |
||
2497 |
int |
|
2498 |
wait_for_recall(vnode_t *vp1, vnode_t *vp2, nfs4_op_hint_t op, |
|
2499 |
nfs4_recov_state_t *rsp) |
|
2500 |
{ |
|
2501 |
int error; |
|
2502 |
||
2503 |
NFS4_DEBUG(nfs4_recall_debug, |
|
2504 |
(CE_NOTE, "wait_for_recall: 0x%p, 0x%p\n", |
|
2505 |
(void *)vp1, (void *) vp2)); |
|
2506 |
||
2507 |
rsp->rs_flags &= ~(NFS4_RS_RECALL_HELD1|NFS4_RS_RECALL_HELD2); |
|
2508 |
||
2509 |
if ((error = wait_for_recall1(vp1, op, rsp, NFS4_RS_RECALL_HELD1)) != 0) |
|
2510 |
return (error); |
|
2511 |
||
2512 |
if ((error = wait_for_recall1(vp2, op, rsp, NFS4_RS_RECALL_HELD2)) |
|
2513 |
!= 0) { |
|
2514 |
if (rsp->rs_flags & NFS4_RS_RECALL_HELD1) { |
|
2515 |
nfs_rw_exit(&VTOR4(vp1)->r_deleg_recall_lock); |
|
2516 |
rsp->rs_flags &= ~NFS4_RS_RECALL_HELD1; |
|
2517 |
} |
|
2518 |
||
2519 |
return (error); |
|
2520 |
} |
|
2521 |
||
2522 |
return (0); |
|
2523 |
} |
|
2524 |
||
2525 |
/* |
|
2526 |
* nfs4_dlistadd - Add this rnode to a list of rnodes to be |
|
2527 |
* DELEGRETURN'd at the end of recovery. |
|
2528 |
*/ |
|
2529 |
||
2530 |
static void |
|
2531 |
nfs4_dlistadd(rnode4_t *rp, struct nfs4_callback_globals *ncg, int flags) |
|
2532 |
{ |
|
2533 |
struct nfs4_dnode *dp; |
|
2534 |
||
2535 |
ASSERT(mutex_owned(&rp->r_statev4_lock)); |
|
2536 |
/* |
|
2537 |
* Mark the delegation as having a return pending. |
|
2538 |
* This will prevent the use of the delegation stateID |
|
2539 |
* by read, write, setattr and open. |
|
2540 |
*/ |
|
2541 |
rp->r_deleg_return_pending = TRUE; |
|
2542 |
dp = kmem_alloc(sizeof (*dp), KM_SLEEP); |
|
2543 |
VN_HOLD(RTOV4(rp)); |
|
2544 |
dp->rnodep = rp; |
|
2545 |
dp->flags = flags; |
|
2546 |
mutex_enter(&ncg->nfs4_dlist_lock); |
|
2547 |
list_insert_head(&ncg->nfs4_dlist, dp); |
|
2548 |
#ifdef DEBUG |
|
2549 |
ncg->nfs4_dlistadd_c++; |
|
2550 |
#endif |
|
2551 |
mutex_exit(&ncg->nfs4_dlist_lock); |
|
2552 |
} |
|
2553 |
||
2554 |
/* |
|
2555 |
* nfs4_dlistclean_impl - Do DELEGRETURN for each rnode on the list. |
|
2556 |
* of files awaiting cleaning. If the override_flags are non-zero |
|
2557 |
* then use them rather than the flags that were set when the rnode |
|
2558 |
* was added to the dlist. |
|
2559 |
*/ |
|
2560 |
static void |
|
2561 |
nfs4_dlistclean_impl(struct nfs4_callback_globals *ncg, int override_flags) |
|
2562 |
{ |
|
2563 |
rnode4_t *rp; |
|
2564 |
struct nfs4_dnode *dp; |
|
2565 |
int flags; |
|
2566 |
||
2567 |
ASSERT(override_flags == 0 || override_flags == NFS4_DR_DISCARD); |
|
2568 |
||
2569 |
mutex_enter(&ncg->nfs4_dlist_lock); |
|
2570 |
while ((dp = list_head(&ncg->nfs4_dlist)) != NULL) { |
|
2571 |
#ifdef DEBUG |
|
2572 |
ncg->nfs4_dlistclean_c++; |
|
2573 |
#endif |
|
2574 |
list_remove(&ncg->nfs4_dlist, dp); |
|
2575 |
mutex_exit(&ncg->nfs4_dlist_lock); |
|
2576 |
rp = dp->rnodep; |
|
2577 |
flags = (override_flags != 0) ? override_flags : dp->flags; |
|
2578 |
kmem_free(dp, sizeof (*dp)); |
|
2579 |
(void) nfs4delegreturn_impl(rp, flags, ncg); |
|
2580 |
VN_RELE(RTOV4(rp)); |
|
2581 |
mutex_enter(&ncg->nfs4_dlist_lock); |
|
2582 |
} |
|
2583 |
mutex_exit(&ncg->nfs4_dlist_lock); |
|
2584 |
} |
|
2585 |
||
2586 |
void |
|
2587 |
nfs4_dlistclean(void) |
|
2588 |
{ |
|
2589 |
struct nfs4_callback_globals *ncg; |
|
2590 |
||
766 | 2591 |
ncg = zone_getspecific(nfs4_callback_zone_key, nfs_zone()); |
0 | 2592 |
ASSERT(ncg != NULL); |
2593 |
||
2594 |
nfs4_dlistclean_impl(ncg, 0); |
|
2595 |
} |