Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/fs/nfsclient/nfs_clstate.c
39536 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2009 Rick Macklem, University of Guelph
5
* All rights reserved.
6
*
7
* Redistribution and use in source and binary forms, with or without
8
* modification, are permitted provided that the following conditions
9
* are met:
10
* 1. Redistributions of source code must retain the above copyright
11
* notice, this list of conditions and the following disclaimer.
12
* 2. Redistributions in binary form must reproduce the above copyright
13
* notice, this list of conditions and the following disclaimer in the
14
* documentation and/or other materials provided with the distribution.
15
*
16
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26
* SUCH DAMAGE.
27
*
28
*/
29
30
#include <sys/cdefs.h>
31
/*
32
* These functions implement the client side state handling for NFSv4.
33
* NFSv4 state handling:
34
* - A lockowner is used to determine lock contention, so it
35
* corresponds directly to a Posix pid. (1 to 1 mapping)
36
* - The correct granularity of an OpenOwner is not nearly so
37
* obvious. An OpenOwner does the following:
38
* - provides a serial sequencing of Open/Close/Lock-with-new-lockowner
39
* - is used to check for Open/Share contention (not applicable to
40
* this client, since all Opens are Deny_None)
41
* As such, I considered both extreme.
42
* 1 OpenOwner per ClientID - Simple to manage, but fully serializes
43
* all Open, Close and Lock (with a new lockowner) Ops.
44
* 1 OpenOwner for each Open - This one results in an OpenConfirm for
45
* every Open, for most servers.
46
* So, I chose to use the same mapping as I did for LockOwnwers.
47
* The main concern here is that you can end up with multiple Opens
48
* for the same File Handle, but on different OpenOwners (opens
49
* inherited from parents, grandparents...) and you do not know
50
* which of these the vnodeop close applies to. This is handled by
51
* delaying the Close Op(s) until all of the Opens have been closed.
52
* (It is not yet obvious if this is the correct granularity.)
53
* - How the code handles serialization:
54
* - For the ClientId, it uses an exclusive lock while getting its
55
* SetClientId and during recovery. Otherwise, it uses a shared
56
* lock via a reference count.
57
* - For the rest of the data structures, it uses an SMP mutex
58
* (once the nfs client is SMP safe) and doesn't sleep while
59
* manipulating the linked lists.
60
* - The serialization of Open/Close/Lock/LockU falls out in the
61
* "wash", since OpenOwners and LockOwners are both mapped from
62
* Posix pid. In other words, there is only one Posix pid using
63
* any given owner, so that owner is serialized. (If you change
64
* the granularity of the OpenOwner, then code must be added to
65
* serialize Ops on the OpenOwner.)
66
* - When to get rid of OpenOwners and LockOwners.
67
* - The function nfscl_cleanup_common() is executed after a process exits.
68
* It goes through the client list looking for all Open and Lock Owners.
69
* When one is found, it is marked "defunct" or in the case of
70
* an OpenOwner without any Opens, freed.
71
* The renew thread scans for defunct Owners and gets rid of them,
72
* if it can. The LockOwners will also be deleted when the
73
* associated Open is closed.
74
* - If the LockU or Close Op(s) fail during close in a way
75
* that could be recovered upon retry, they are relinked to the
76
* ClientId's defunct open list and retried by the renew thread
77
* until they succeed or an unmount/recovery occurs.
78
* (Since we are done with them, they do not need to be recovered.)
79
*/
80
81
#include <fs/nfs/nfsport.h>
82
83
/*
84
* Global variables
85
*/
86
extern struct nfsstatsv1 nfsstatsv1;
87
extern struct nfsreqhead nfsd_reqq;
88
extern u_int32_t newnfs_false, newnfs_true;
89
extern int nfscl_debuglevel;
90
extern int nfscl_enablecallb;
91
extern int nfs_numnfscbd;
92
NFSREQSPINLOCK;
93
NFSCLSTATEMUTEX;
94
int nfscl_inited = 0;
95
struct nfsclhead nfsclhead; /* Head of clientid list */
96
97
static int nfscl_getopen(struct nfsclownerhead *, struct nfsclopenhash *,
98
u_int8_t *, int, u_int8_t *, u_int8_t *, u_int32_t,
99
struct nfscllockowner **, struct nfsclopen **);
100
static bool nfscl_checkown(struct nfsclowner *, struct nfsclopen *, uint8_t *,
101
uint8_t *, struct nfscllockowner **, struct nfsclopen **,
102
struct nfsclopen **);
103
static void nfscl_clrelease(struct nfsclclient *);
104
static void nfscl_unlinkopen(struct nfsclopen *);
105
static void nfscl_cleanclient(struct nfsclclient *);
106
static void nfscl_expireclient(struct nfsclclient *, struct nfsmount *,
107
struct ucred *, NFSPROC_T *);
108
static int nfscl_expireopen(struct nfsclclient *, struct nfsclopen *,
109
struct nfsmount *, struct ucred *, NFSPROC_T *);
110
static void nfscl_recover(struct nfsclclient *, bool *, struct ucred *,
111
NFSPROC_T *);
112
static void nfscl_insertlock(struct nfscllockowner *, struct nfscllock *,
113
struct nfscllock *, int);
114
static int nfscl_updatelock(struct nfscllockowner *, struct nfscllock **,
115
struct nfscllock **, int);
116
static void nfscl_delegreturnall(struct nfsclclient *, NFSPROC_T *,
117
struct nfscldeleghead *);
118
static u_int32_t nfscl_nextcbident(void);
119
static mount_t nfscl_getmnt(int, uint8_t *, u_int32_t, struct nfsclclient **);
120
static struct nfsclclient *nfscl_getclnt(u_int32_t);
121
static struct nfsclclient *nfscl_getclntsess(uint8_t *);
122
static struct nfscldeleg *nfscl_finddeleg(struct nfsclclient *, u_int8_t *,
123
int);
124
static void nfscl_retoncloselayout(vnode_t, struct nfsclclient *, uint8_t *,
125
int, struct nfsclrecalllayout **, struct nfscllayout **);
126
static void nfscl_reldevinfo_locked(struct nfscldevinfo *);
127
static struct nfscllayout *nfscl_findlayout(struct nfsclclient *, u_int8_t *,
128
int);
129
static struct nfscldevinfo *nfscl_finddevinfo(struct nfsclclient *, uint8_t *);
130
static int nfscl_checkconflict(struct nfscllockownerhead *, struct nfscllock *,
131
u_int8_t *, struct nfscllock **);
132
static void nfscl_freealllocks(struct nfscllockownerhead *, int);
133
static int nfscl_localconflict(struct nfsclclient *, u_int8_t *, int,
134
struct nfscllock *, u_int8_t *, struct nfscldeleg *, struct nfscllock **);
135
static void nfscl_newopen(struct nfsclclient *, struct nfscldeleg *,
136
struct nfsclowner **, struct nfsclowner **, struct nfsclopen **,
137
struct nfsclopen **, u_int8_t *, u_int8_t *, int, struct ucred *, int *);
138
static int nfscl_moveopen(vnode_t , struct nfsclclient *,
139
struct nfsmount *, struct nfsclopen *, struct nfsclowner *,
140
struct nfscldeleg *, struct ucred *, NFSPROC_T *);
141
static void nfscl_totalrecall(struct nfsclclient *);
142
static int nfscl_relock(vnode_t , struct nfsclclient *, struct nfsmount *,
143
struct nfscllockowner *, struct nfscllock *, struct ucred *, NFSPROC_T *);
144
static int nfscl_tryopen(struct nfsmount *, vnode_t , u_int8_t *, int,
145
u_int8_t *, int, u_int32_t, struct nfsclopen *, u_int8_t *, int,
146
struct nfscldeleg **, int, u_int32_t, struct ucred *, NFSPROC_T *);
147
static int nfscl_trylock(struct nfsmount *, vnode_t , u_int8_t *,
148
int, struct nfscllockowner *, int, int, u_int64_t, u_int64_t, short,
149
struct ucred *, NFSPROC_T *);
150
static int nfsrpc_reopen(struct nfsmount *, u_int8_t *, int, u_int32_t,
151
struct nfsclopen *, struct nfscldeleg **, struct ucred *, NFSPROC_T *);
152
static void nfscl_freedeleg(struct nfscldeleghead *, struct nfscldeleg *,
153
bool);
154
static int nfscl_errmap(struct nfsrv_descript *, u_int32_t);
155
static void nfscl_cleanup_common(struct nfsclclient *, u_int8_t *);
156
static int nfscl_recalldeleg(struct nfsclclient *, struct nfsmount *,
157
struct nfscldeleg *, vnode_t, struct ucred *, NFSPROC_T *, int,
158
vnode_t *);
159
static void nfscl_freeopenowner(struct nfsclowner *, int);
160
static void nfscl_cleandeleg(struct nfscldeleg *);
161
static void nfscl_emptylockowner(struct nfscllockowner *,
162
struct nfscllockownerfhhead *);
163
static void nfscl_mergeflayouts(struct nfsclflayouthead *,
164
struct nfsclflayouthead *);
165
static int nfscl_layoutrecall(int, struct nfscllayout *, uint32_t, uint64_t,
166
uint64_t, uint32_t, uint32_t, uint32_t, char *, struct nfsclrecalllayout *);
167
static int nfscl_seq(uint32_t, uint32_t);
168
static void nfscl_layoutreturn(struct nfsmount *, struct nfscllayout *,
169
struct ucred *, NFSPROC_T *);
170
static void nfscl_dolayoutcommit(struct nfsmount *, struct nfscllayout *,
171
struct ucred *, NFSPROC_T *);
172
173
static short nfscberr_null[] = {
174
0,
175
0,
176
};
177
178
static short nfscberr_getattr[] = {
179
NFSERR_RESOURCE,
180
NFSERR_BADHANDLE,
181
NFSERR_BADXDR,
182
NFSERR_RESOURCE,
183
NFSERR_SERVERFAULT,
184
0,
185
};
186
187
static short nfscberr_recall[] = {
188
NFSERR_RESOURCE,
189
NFSERR_BADHANDLE,
190
NFSERR_BADSTATEID,
191
NFSERR_BADXDR,
192
NFSERR_RESOURCE,
193
NFSERR_SERVERFAULT,
194
0,
195
};
196
197
static short *nfscl_cberrmap[] = {
198
nfscberr_null,
199
nfscberr_null,
200
nfscberr_null,
201
nfscberr_getattr,
202
nfscberr_recall
203
};
204
205
#define NETFAMILY(clp) \
206
(((clp)->nfsc_flags & NFSCLFLAGS_AFINET6) ? AF_INET6 : AF_INET)
207
208
/*
209
* Called for an open operation.
210
* If the nfhp argument is NULL, just get an openowner.
211
*/
212
int
213
nfscl_open(vnode_t vp, u_int8_t *nfhp, int fhlen, u_int32_t amode, int usedeleg,
214
struct ucred *cred, NFSPROC_T *p, struct nfsclowner **owpp,
215
struct nfsclopen **opp, int *newonep, int *retp, int lockit, bool firstref)
216
{
217
struct nfsclclient *clp;
218
struct nfsclowner *owp, *nowp;
219
struct nfsclopen *op = NULL, *nop = NULL;
220
struct nfscldeleg *dp;
221
struct nfsclownerhead *ohp;
222
u_int8_t own[NFSV4CL_LOCKNAMELEN];
223
int ret;
224
225
if (newonep != NULL)
226
*newonep = 0;
227
if (opp != NULL)
228
*opp = NULL;
229
if (owpp != NULL)
230
*owpp = NULL;
231
232
/*
233
* Might need one or both of these, so MALLOC them now, to
234
* avoid a tsleep() in MALLOC later.
235
*/
236
nowp = malloc(sizeof (struct nfsclowner),
237
M_NFSCLOWNER, M_WAITOK);
238
if (nfhp != NULL) {
239
nop = malloc(sizeof (struct nfsclopen) +
240
fhlen - 1, M_NFSCLOPEN, M_WAITOK);
241
nop->nfso_hash.le_prev = NULL;
242
}
243
ret = nfscl_getcl(vp->v_mount, cred, p, false, firstref, &clp);
244
if (ret != 0) {
245
free(nowp, M_NFSCLOWNER);
246
if (nop != NULL)
247
free(nop, M_NFSCLOPEN);
248
return (ret);
249
}
250
251
/*
252
* Get the Open iff it already exists.
253
* If none found, add the new one or return error, depending upon
254
* "create".
255
*/
256
NFSLOCKCLSTATE();
257
dp = NULL;
258
/* First check the delegation list */
259
if (nfhp != NULL && usedeleg) {
260
LIST_FOREACH(dp, NFSCLDELEGHASH(clp, nfhp, fhlen), nfsdl_hash) {
261
if (dp->nfsdl_fhlen == fhlen &&
262
!NFSBCMP(nfhp, dp->nfsdl_fh, fhlen)) {
263
if (!(amode & NFSV4OPEN_ACCESSWRITE) ||
264
(dp->nfsdl_flags & NFSCLDL_WRITE))
265
break;
266
dp = NULL;
267
break;
268
}
269
}
270
}
271
272
/* For NFSv4.1/4.2 and this option, use a single open_owner. */
273
if (NFSHASONEOPENOWN(VFSTONFS(vp->v_mount)))
274
nfscl_filllockowner(NULL, own, F_POSIX);
275
else
276
nfscl_filllockowner(p->td_proc, own, F_POSIX);
277
if (dp != NULL)
278
ohp = &dp->nfsdl_owner;
279
else
280
ohp = &clp->nfsc_owner;
281
/* Now, search for an openowner */
282
LIST_FOREACH(owp, ohp, nfsow_list) {
283
if (!NFSBCMP(owp->nfsow_owner, own, NFSV4CL_LOCKNAMELEN))
284
break;
285
}
286
287
/*
288
* Create a new open, as required.
289
*/
290
nfscl_newopen(clp, dp, &owp, &nowp, &op, &nop, own, nfhp, fhlen,
291
cred, newonep);
292
293
/*
294
* Now, check the mode on the open and return the appropriate
295
* value.
296
*/
297
if (retp != NULL) {
298
if (nfhp != NULL && dp != NULL && nop == NULL)
299
/* new local open on delegation */
300
*retp = NFSCLOPEN_SETCRED;
301
else
302
*retp = NFSCLOPEN_OK;
303
}
304
if (op != NULL && (amode & ~(op->nfso_mode))) {
305
op->nfso_mode |= amode;
306
if (retp != NULL && dp == NULL)
307
*retp = NFSCLOPEN_DOOPEN;
308
}
309
310
/*
311
* Serialize modifications to the open owner for multiple threads
312
* within the same process using a read/write sleep lock.
313
* For NFSv4.1 and a single OpenOwner, allow concurrent open operations
314
* by acquiring a shared lock. The close operations still use an
315
* exclusive lock for this case.
316
*/
317
if (lockit != 0) {
318
if (NFSHASONEOPENOWN(VFSTONFS(vp->v_mount))) {
319
/*
320
* Get a shared lock on the OpenOwner, but first
321
* wait for any pending exclusive lock, so that the
322
* exclusive locker gets priority.
323
*/
324
nfsv4_lock(&owp->nfsow_rwlock, 0, NULL,
325
NFSCLSTATEMUTEXPTR, NULL);
326
nfsv4_getref(&owp->nfsow_rwlock, NULL,
327
NFSCLSTATEMUTEXPTR, NULL);
328
} else
329
nfscl_lockexcl(&owp->nfsow_rwlock, NFSCLSTATEMUTEXPTR);
330
}
331
NFSUNLOCKCLSTATE();
332
if (nowp != NULL)
333
free(nowp, M_NFSCLOWNER);
334
if (nop != NULL)
335
free(nop, M_NFSCLOPEN);
336
if (owpp != NULL)
337
*owpp = owp;
338
if (opp != NULL)
339
*opp = op;
340
return (0);
341
}
342
343
/*
344
* Create a new open, as required.
345
*/
346
static void
347
nfscl_newopen(struct nfsclclient *clp, struct nfscldeleg *dp,
348
struct nfsclowner **owpp, struct nfsclowner **nowpp, struct nfsclopen **opp,
349
struct nfsclopen **nopp, u_int8_t *own, u_int8_t *fhp, int fhlen,
350
struct ucred *cred, int *newonep)
351
{
352
struct nfsclowner *owp = *owpp, *nowp;
353
struct nfsclopen *op, *nop;
354
355
if (nowpp != NULL)
356
nowp = *nowpp;
357
else
358
nowp = NULL;
359
if (nopp != NULL)
360
nop = *nopp;
361
else
362
nop = NULL;
363
if (owp == NULL && nowp != NULL) {
364
NFSBCOPY(own, nowp->nfsow_owner, NFSV4CL_LOCKNAMELEN);
365
LIST_INIT(&nowp->nfsow_open);
366
nowp->nfsow_clp = clp;
367
nowp->nfsow_seqid = 0;
368
nowp->nfsow_defunct = 0;
369
nfscl_lockinit(&nowp->nfsow_rwlock);
370
if (dp != NULL) {
371
nfsstatsv1.cllocalopenowners++;
372
LIST_INSERT_HEAD(&dp->nfsdl_owner, nowp, nfsow_list);
373
} else {
374
nfsstatsv1.clopenowners++;
375
LIST_INSERT_HEAD(&clp->nfsc_owner, nowp, nfsow_list);
376
}
377
owp = *owpp = nowp;
378
*nowpp = NULL;
379
if (newonep != NULL)
380
*newonep = 1;
381
}
382
383
/* If an fhp has been specified, create an Open as well. */
384
if (fhp != NULL) {
385
/* and look for the correct open, based upon FH */
386
LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
387
if (op->nfso_fhlen == fhlen &&
388
!NFSBCMP(op->nfso_fh, fhp, fhlen))
389
break;
390
}
391
if (op == NULL && nop != NULL) {
392
nop->nfso_own = owp;
393
nop->nfso_mode = 0;
394
nop->nfso_opencnt = 0;
395
nop->nfso_posixlock = 1;
396
nop->nfso_fhlen = fhlen;
397
NFSBCOPY(fhp, nop->nfso_fh, fhlen);
398
LIST_INIT(&nop->nfso_lock);
399
nop->nfso_stateid.seqid = 0;
400
nop->nfso_stateid.other[0] = 0;
401
nop->nfso_stateid.other[1] = 0;
402
nop->nfso_stateid.other[2] = 0;
403
KASSERT(cred != NULL, ("%s: cred NULL\n", __func__));
404
newnfs_copyincred(cred, &nop->nfso_cred);
405
if (dp != NULL) {
406
TAILQ_REMOVE(&clp->nfsc_deleg, dp, nfsdl_list);
407
TAILQ_INSERT_HEAD(&clp->nfsc_deleg, dp,
408
nfsdl_list);
409
dp->nfsdl_timestamp = NFSD_MONOSEC + 120;
410
nfsstatsv1.cllocalopens++;
411
} else {
412
LIST_INSERT_HEAD(NFSCLOPENHASH(clp, fhp, fhlen),
413
nop, nfso_hash);
414
nfsstatsv1.clopens++;
415
}
416
LIST_INSERT_HEAD(&owp->nfsow_open, nop, nfso_list);
417
*opp = nop;
418
*nopp = NULL;
419
if (newonep != NULL)
420
*newonep = 1;
421
} else {
422
*opp = op;
423
}
424
}
425
}
426
427
/*
428
* Called to find/add a delegation to a client.
429
*/
430
int
431
nfscl_deleg(mount_t mp, struct nfsclclient *clp, u_int8_t *nfhp,
432
int fhlen, struct ucred *cred, NFSPROC_T *p, struct nfscldeleg *dp)
433
{
434
struct nfscldeleg *tdp;
435
struct nfsmount *nmp;
436
437
KASSERT(mp != NULL, ("nfscl_deleg: mp NULL"));
438
nmp = VFSTONFS(mp);
439
440
/*
441
* Since a delegation might be added to the mount,
442
* set NFSMNTP_DELEGISSUED now. If a delegation already
443
* exagain ists, setting this flag is harmless.
444
*/
445
NFSLOCKMNT(nmp);
446
nmp->nm_privflag |= NFSMNTP_DELEGISSUED;
447
NFSUNLOCKMNT(nmp);
448
449
/* Look for the correct deleg, based upon FH */
450
NFSLOCKCLSTATE();
451
tdp = nfscl_finddeleg(clp, nfhp, fhlen);
452
if (tdp == NULL) {
453
if (dp == NULL) {
454
NFSUNLOCKCLSTATE();
455
return (NFSERR_BADSTATEID);
456
}
457
TAILQ_INSERT_HEAD(&clp->nfsc_deleg, dp, nfsdl_list);
458
LIST_INSERT_HEAD(NFSCLDELEGHASH(clp, nfhp, fhlen), dp,
459
nfsdl_hash);
460
dp->nfsdl_timestamp = NFSD_MONOSEC + 120;
461
nfsstatsv1.cldelegates++;
462
clp->nfsc_delegcnt++;
463
} else {
464
/*
465
* A delegation already exists. If the new one is a Write
466
* delegation and the old one a Read delegation, return the
467
* Read delegation. Otherwise, return the new delegation.
468
*/
469
if (dp != NULL) {
470
if ((dp->nfsdl_flags & NFSCLDL_WRITE) != 0 &&
471
(tdp->nfsdl_flags & NFSCLDL_READ) != 0) {
472
TAILQ_REMOVE(&clp->nfsc_deleg, tdp, nfsdl_list);
473
LIST_REMOVE(tdp, nfsdl_hash);
474
TAILQ_INSERT_HEAD(&clp->nfsc_deleg, dp,
475
nfsdl_list);
476
LIST_INSERT_HEAD(NFSCLDELEGHASH(clp, nfhp,
477
fhlen), dp, nfsdl_hash);
478
dp->nfsdl_timestamp = NFSD_MONOSEC + 120;
479
} else {
480
tdp = dp; /* Return this one. */
481
}
482
} else {
483
tdp = NULL;
484
}
485
}
486
NFSUNLOCKCLSTATE();
487
if (tdp != NULL) {
488
nfscl_trydelegreturn(tdp, cred, nmp, p);
489
free(tdp, M_NFSCLDELEG);
490
}
491
return (0);
492
}
493
494
/*
495
* Find a delegation for this file handle. Return NULL upon failure.
496
*/
497
static struct nfscldeleg *
498
nfscl_finddeleg(struct nfsclclient *clp, u_int8_t *fhp, int fhlen)
499
{
500
struct nfscldeleg *dp;
501
502
LIST_FOREACH(dp, NFSCLDELEGHASH(clp, fhp, fhlen), nfsdl_hash) {
503
if (dp->nfsdl_fhlen == fhlen &&
504
!NFSBCMP(dp->nfsdl_fh, fhp, fhlen))
505
break;
506
}
507
return (dp);
508
}
509
510
/*
511
* Get a stateid for an I/O operation. First, look for an open and iff
512
* found, return either a lockowner stateid or the open stateid.
513
* If no Open is found, just return error and the special stateid of all zeros.
514
*/
515
int
516
nfscl_getstateid(vnode_t vp, u_int8_t *nfhp, int fhlen, u_int32_t mode,
517
int fords, struct ucred *cred, NFSPROC_T *p, nfsv4stateid_t *stateidp,
518
void **lckpp)
519
{
520
struct nfsclclient *clp;
521
struct nfsclopen *op = NULL, *top;
522
struct nfsclopenhash *oph;
523
struct nfscllockowner *lp;
524
struct nfscldeleg *dp;
525
struct nfsnode *np;
526
struct nfsmount *nmp;
527
struct nfscred ncr;
528
u_int8_t own[NFSV4CL_LOCKNAMELEN], lockown[NFSV4CL_LOCKNAMELEN];
529
int error;
530
bool done;
531
532
*lckpp = NULL;
533
/*
534
* Initially, just set the special stateid of all zeros.
535
* (Don't do this for a DS, since the special stateid can't be used.)
536
*/
537
if (fords == 0) {
538
stateidp->seqid = 0;
539
stateidp->other[0] = 0;
540
stateidp->other[1] = 0;
541
stateidp->other[2] = 0;
542
}
543
if (vp->v_type != VREG)
544
return (EISDIR);
545
np = VTONFS(vp);
546
nmp = VFSTONFS(vp->v_mount);
547
548
/*
549
* For "oneopenown" mounts, first check for a cached open in the
550
* NFS vnode, that can be used as a stateid. This can only be
551
* done if no delegations have been issued to the mount and no
552
* byte range file locking has been done for the file.
553
*/
554
if (NFSHASNFSV4N(nmp) && NFSHASONEOPENOWN(nmp) && fords == 0) {
555
NFSLOCKMNT(nmp);
556
NFSLOCKNODE(np);
557
if ((nmp->nm_privflag & NFSMNTP_DELEGISSUED) == 0 &&
558
(np->n_flag & NMIGHTBELOCKED) == 0 &&
559
np->n_openstateid != NULL) {
560
stateidp->seqid = 0;
561
stateidp->other[0] =
562
np->n_openstateid->nfso_stateid.other[0];
563
stateidp->other[1] =
564
np->n_openstateid->nfso_stateid.other[1];
565
stateidp->other[2] =
566
np->n_openstateid->nfso_stateid.other[2];
567
NFSUNLOCKNODE(np);
568
NFSUNLOCKMNT(nmp);
569
return (0);
570
}
571
NFSUNLOCKNODE(np);
572
NFSUNLOCKMNT(nmp);
573
}
574
575
NFSLOCKCLSTATE();
576
clp = nfscl_findcl(nmp);
577
if (clp == NULL) {
578
NFSUNLOCKCLSTATE();
579
return (EACCES);
580
}
581
582
/*
583
* Wait for recovery to complete.
584
*/
585
while ((clp->nfsc_flags & NFSCLFLAGS_RECVRINPROG))
586
(void) nfsmsleep(&clp->nfsc_flags, NFSCLSTATEMUTEXPTR,
587
PZERO, "nfsrecvr", NULL);
588
589
/*
590
* First, look for a delegation.
591
*/
592
LIST_FOREACH(dp, NFSCLDELEGHASH(clp, nfhp, fhlen), nfsdl_hash) {
593
if (dp->nfsdl_fhlen == fhlen &&
594
!NFSBCMP(nfhp, dp->nfsdl_fh, fhlen)) {
595
if (!(mode & NFSV4OPEN_ACCESSWRITE) ||
596
(dp->nfsdl_flags & NFSCLDL_WRITE)) {
597
if (NFSHASNFSV4N(nmp))
598
stateidp->seqid = 0;
599
else
600
stateidp->seqid =
601
dp->nfsdl_stateid.seqid;
602
stateidp->other[0] = dp->nfsdl_stateid.other[0];
603
stateidp->other[1] = dp->nfsdl_stateid.other[1];
604
stateidp->other[2] = dp->nfsdl_stateid.other[2];
605
if (!(np->n_flag & NDELEGRECALL)) {
606
TAILQ_REMOVE(&clp->nfsc_deleg, dp,
607
nfsdl_list);
608
TAILQ_INSERT_HEAD(&clp->nfsc_deleg, dp,
609
nfsdl_list);
610
dp->nfsdl_timestamp = NFSD_MONOSEC +
611
120;
612
dp->nfsdl_rwlock.nfslock_usecnt++;
613
*lckpp = (void *)&dp->nfsdl_rwlock;
614
}
615
NFSUNLOCKCLSTATE();
616
return (0);
617
}
618
break;
619
}
620
}
621
622
if (p != NULL) {
623
/*
624
* If p != NULL, we want to search the parentage tree
625
* for a matching OpenOwner and use that.
626
*/
627
if (NFSHASONEOPENOWN(VFSTONFS(vp->v_mount)))
628
nfscl_filllockowner(NULL, own, F_POSIX);
629
else
630
nfscl_filllockowner(p->td_proc, own, F_POSIX);
631
nfscl_filllockowner(p->td_proc, lockown, F_POSIX);
632
lp = NULL;
633
error = nfscl_getopen(NULL, clp->nfsc_openhash, nfhp, fhlen,
634
own, lockown, mode, &lp, &op);
635
if (error == 0 && lp != NULL && fords == 0) {
636
/* Don't return a lock stateid for a DS. */
637
if (NFSHASNFSV4N(nmp))
638
stateidp->seqid = 0;
639
else
640
stateidp->seqid = lp->nfsl_stateid.seqid;
641
stateidp->other[0] =
642
lp->nfsl_stateid.other[0];
643
stateidp->other[1] =
644
lp->nfsl_stateid.other[1];
645
stateidp->other[2] =
646
lp->nfsl_stateid.other[2];
647
NFSUNLOCKCLSTATE();
648
return (0);
649
}
650
}
651
if (op == NULL) {
652
/* If not found, just look for any OpenOwner that will work. */
653
top = NULL;
654
done = false;
655
oph = NFSCLOPENHASH(clp, nfhp, fhlen);
656
LIST_FOREACH(op, oph, nfso_hash) {
657
if (op->nfso_fhlen == fhlen &&
658
!NFSBCMP(op->nfso_fh, nfhp, fhlen)) {
659
if (top == NULL && (op->nfso_mode &
660
NFSV4OPEN_ACCESSWRITE) != 0 &&
661
(mode & NFSV4OPEN_ACCESSREAD) != 0)
662
top = op;
663
if ((mode & op->nfso_mode) == mode) {
664
/* LRU order the hash list. */
665
LIST_REMOVE(op, nfso_hash);
666
LIST_INSERT_HEAD(oph, op, nfso_hash);
667
done = true;
668
break;
669
}
670
}
671
}
672
if (!done) {
673
NFSCL_DEBUG(2, "openmode top=%p\n", top);
674
if (top == NULL || NFSHASOPENMODE(nmp)) {
675
NFSUNLOCKCLSTATE();
676
return (ENOENT);
677
} else
678
op = top;
679
}
680
/*
681
* For read aheads or write behinds, use the open cred.
682
* A read ahead or write behind is indicated by p == NULL.
683
*/
684
if (p == NULL)
685
memcpy(&ncr, &op->nfso_cred, sizeof(ncr));
686
}
687
688
/*
689
* No lock stateid, so return the open stateid.
690
*/
691
if (NFSHASNFSV4N(nmp))
692
stateidp->seqid = 0;
693
else
694
stateidp->seqid = op->nfso_stateid.seqid;
695
stateidp->other[0] = op->nfso_stateid.other[0];
696
stateidp->other[1] = op->nfso_stateid.other[1];
697
stateidp->other[2] = op->nfso_stateid.other[2];
698
NFSUNLOCKCLSTATE();
699
if (p == NULL)
700
newnfs_copycred(&ncr, cred);
701
return (0);
702
}
703
704
/*
705
* Search for a matching file, mode and, optionally, lockowner.
706
*/
707
static int
708
nfscl_getopen(struct nfsclownerhead *ohp, struct nfsclopenhash *ohashp,
709
u_int8_t *nfhp, int fhlen, u_int8_t *openown, u_int8_t *lockown,
710
u_int32_t mode, struct nfscllockowner **lpp, struct nfsclopen **opp)
711
{
712
struct nfsclowner *owp;
713
struct nfsclopen *op, *rop, *rop2;
714
struct nfsclopenhash *oph;
715
bool keep_looping;
716
717
KASSERT(ohp == NULL || ohashp == NULL, ("nfscl_getopen: "
718
"only one of ohp and ohashp can be set"));
719
if (lpp != NULL)
720
*lpp = NULL;
721
/*
722
* rop will be set to the open to be returned. There are three
723
* variants of this, all for an open of the correct file:
724
* 1 - A match of lockown.
725
* 2 - A match of the openown, when no lockown match exists.
726
* 3 - A match for any open, if no openown or lockown match exists.
727
* Looking for #2 over #3 probably isn't necessary, but since
728
* RFC3530 is vague w.r.t. the relationship between openowners and
729
* lockowners, I think this is the safer way to go.
730
*/
731
rop = NULL;
732
rop2 = NULL;
733
keep_looping = true;
734
/* Search the client list */
735
if (ohashp == NULL) {
736
/* Search the local opens on the delegation. */
737
LIST_FOREACH(owp, ohp, nfsow_list) {
738
/* and look for the correct open */
739
LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
740
if (op->nfso_fhlen == fhlen &&
741
!NFSBCMP(op->nfso_fh, nfhp, fhlen)
742
&& (op->nfso_mode & mode) == mode)
743
keep_looping = nfscl_checkown(owp, op, openown,
744
lockown, lpp, &rop, &rop2);
745
if (!keep_looping)
746
break;
747
}
748
if (!keep_looping)
749
break;
750
}
751
} else {
752
/* Search for matching opens on the hash list. */
753
oph = &ohashp[NFSCLOPENHASHFUNC(nfhp, fhlen)];
754
LIST_FOREACH(op, oph, nfso_hash) {
755
if (op->nfso_fhlen == fhlen &&
756
!NFSBCMP(op->nfso_fh, nfhp, fhlen)
757
&& (op->nfso_mode & mode) == mode)
758
keep_looping = nfscl_checkown(op->nfso_own, op,
759
openown, lockown, lpp, &rop, &rop2);
760
if (!keep_looping) {
761
/* LRU order the hash list. */
762
LIST_REMOVE(op, nfso_hash);
763
LIST_INSERT_HEAD(oph, op, nfso_hash);
764
break;
765
}
766
}
767
}
768
if (rop == NULL)
769
rop = rop2;
770
if (rop == NULL)
771
return (EBADF);
772
*opp = rop;
773
return (0);
774
}
775
776
/* Check for an owner match. */
777
static bool
778
nfscl_checkown(struct nfsclowner *owp, struct nfsclopen *op, uint8_t *openown,
779
uint8_t *lockown, struct nfscllockowner **lpp, struct nfsclopen **ropp,
780
struct nfsclopen **ropp2)
781
{
782
struct nfscllockowner *lp;
783
bool keep_looping;
784
785
keep_looping = true;
786
if (lpp != NULL) {
787
/* Now look for a matching lockowner. */
788
LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
789
if (!NFSBCMP(lp->nfsl_owner, lockown,
790
NFSV4CL_LOCKNAMELEN)) {
791
*lpp = lp;
792
*ropp = op;
793
return (false);
794
}
795
}
796
}
797
if (*ropp == NULL && !NFSBCMP(owp->nfsow_owner, openown,
798
NFSV4CL_LOCKNAMELEN)) {
799
*ropp = op;
800
if (lpp == NULL)
801
keep_looping = false;
802
}
803
if (*ropp2 == NULL)
804
*ropp2 = op;
805
return (keep_looping);
806
}
807
808
/*
809
* Release use of an open owner. Called when open operations are done
810
* with the open owner.
811
*/
812
void
813
nfscl_ownerrelease(struct nfsmount *nmp, struct nfsclowner *owp,
814
__unused int error, __unused int candelete, int unlocked)
815
{
816
817
if (owp == NULL)
818
return;
819
NFSLOCKCLSTATE();
820
if (unlocked == 0) {
821
if (NFSHASONEOPENOWN(nmp))
822
nfsv4_relref(&owp->nfsow_rwlock);
823
else
824
nfscl_lockunlock(&owp->nfsow_rwlock);
825
}
826
nfscl_clrelease(owp->nfsow_clp);
827
NFSUNLOCKCLSTATE();
828
}
829
830
/*
831
* Release use of an open structure under an open owner.
832
*/
833
void
834
nfscl_openrelease(struct nfsmount *nmp, struct nfsclopen *op, int error,
835
int candelete)
836
{
837
struct nfsclclient *clp;
838
struct nfsclowner *owp;
839
840
if (op == NULL)
841
return;
842
NFSLOCKCLSTATE();
843
owp = op->nfso_own;
844
if (NFSHASONEOPENOWN(nmp))
845
nfsv4_relref(&owp->nfsow_rwlock);
846
else
847
nfscl_lockunlock(&owp->nfsow_rwlock);
848
clp = owp->nfsow_clp;
849
if (error && candelete && op->nfso_opencnt == 0)
850
nfscl_freeopen(op, 0, true);
851
nfscl_clrelease(clp);
852
NFSUNLOCKCLSTATE();
853
}
854
855
/*
856
* Called to get a clientid structure. It will optionally lock the
857
* client data structures to do the SetClientId/SetClientId_confirm,
858
* but will release that lock and return the clientid with a reference
859
* count on it.
860
* If the "cred" argument is NULL, a new clientid should not be created.
861
* If the "p" argument is NULL, a SetClientID/SetClientIDConfirm cannot
862
* be done.
863
* It always clpp with a reference count on it, unless returning an error.
864
*/
865
int
866
nfscl_getcl(struct mount *mp, struct ucred *cred, NFSPROC_T *p,
867
bool tryminvers, bool firstref, struct nfsclclient **clpp)
868
{
869
struct nfsclclient *clp;
870
struct nfsclclient *newclp = NULL;
871
struct nfsmount *nmp;
872
char uuid[HOSTUUIDLEN];
873
int igotlock = 0, error, trystalecnt, clidinusedelay, i;
874
u_int16_t idlen = 0;
875
876
nmp = VFSTONFS(mp);
877
if (cred != NULL) {
878
getcredhostuuid(cred, uuid, sizeof uuid);
879
idlen = strlen(uuid);
880
if (idlen > 0)
881
idlen += sizeof (u_int64_t);
882
else
883
idlen += sizeof (u_int64_t) + 16; /* 16 random bytes */
884
newclp = malloc(
885
sizeof (struct nfsclclient) + idlen - 1, M_NFSCLCLIENT,
886
M_WAITOK | M_ZERO);
887
}
888
NFSLOCKCLSTATE();
889
/*
890
* If a forced dismount is already in progress, don't
891
* allocate a new clientid and get out now. For the case where
892
* clp != NULL, this is a harmless optimization.
893
*/
894
if (NFSCL_FORCEDISM(mp)) {
895
NFSUNLOCKCLSTATE();
896
if (newclp != NULL)
897
free(newclp, M_NFSCLCLIENT);
898
return (EBADF);
899
}
900
clp = nmp->nm_clp;
901
if (clp == NULL) {
902
if (newclp == NULL) {
903
NFSUNLOCKCLSTATE();
904
return (EACCES);
905
}
906
clp = newclp;
907
clp->nfsc_idlen = idlen;
908
LIST_INIT(&clp->nfsc_owner);
909
TAILQ_INIT(&clp->nfsc_deleg);
910
TAILQ_INIT(&clp->nfsc_layout);
911
LIST_INIT(&clp->nfsc_devinfo);
912
for (i = 0; i < NFSCLDELEGHASHSIZE; i++)
913
LIST_INIT(&clp->nfsc_deleghash[i]);
914
for (i = 0; i < NFSCLOPENHASHSIZE; i++)
915
LIST_INIT(&clp->nfsc_openhash[i]);
916
for (i = 0; i < NFSCLLAYOUTHASHSIZE; i++)
917
LIST_INIT(&clp->nfsc_layouthash[i]);
918
clp->nfsc_flags = NFSCLFLAGS_INITED;
919
clp->nfsc_delegcnt = 0;
920
clp->nfsc_deleghighwater = NFSCLDELEGHIGHWATER;
921
clp->nfsc_layoutcnt = 0;
922
clp->nfsc_layouthighwater = NFSCLLAYOUTHIGHWATER;
923
clp->nfsc_clientidrev = 1;
924
clp->nfsc_cbident = nfscl_nextcbident();
925
nfscl_fillclid(nmp->nm_clval, uuid, clp->nfsc_id,
926
clp->nfsc_idlen);
927
LIST_INSERT_HEAD(&nfsclhead, clp, nfsc_list);
928
nmp->nm_clp = clp;
929
clp->nfsc_nmp = nmp;
930
} else {
931
if (newclp != NULL)
932
free(newclp, M_NFSCLCLIENT);
933
}
934
while ((clp->nfsc_flags & NFSCLFLAGS_HASCLIENTID) == 0 && !igotlock &&
935
!NFSCL_FORCEDISM(mp))
936
igotlock = nfsv4_lock(&clp->nfsc_lock, 1, NULL,
937
NFSCLSTATEMUTEXPTR, mp);
938
if (igotlock == 0) {
939
/*
940
* Call nfsv4_lock() with "iwantlock == 0" on the firstref so
941
* that it will wait for a pending exclusive lock request.
942
* This gives the exclusive lock request priority over this
943
* shared lock request.
944
* An exclusive lock on nfsc_lock is used mainly for server
945
* crash recoveries and delegation recalls.
946
*/
947
if (firstref)
948
nfsv4_lock(&clp->nfsc_lock, 0, NULL, NFSCLSTATEMUTEXPTR,
949
mp);
950
nfsv4_getref(&clp->nfsc_lock, NULL, NFSCLSTATEMUTEXPTR, mp);
951
}
952
if (igotlock == 0 && NFSCL_FORCEDISM(mp)) {
953
/*
954
* Both nfsv4_lock() and nfsv4_getref() know to check
955
* for NFSCL_FORCEDISM() and return without sleeping to
956
* wait for the exclusive lock to be released, since it
957
* might be held by nfscl_umount() and we need to get out
958
* now for that case and not wait until nfscl_umount()
959
* releases it.
960
*/
961
NFSUNLOCKCLSTATE();
962
return (EBADF);
963
}
964
NFSUNLOCKCLSTATE();
965
966
/*
967
* If it needs a clientid, do the setclientid now.
968
*/
969
if ((clp->nfsc_flags & NFSCLFLAGS_HASCLIENTID) == 0) {
970
if (!igotlock)
971
panic("nfscl_clget");
972
if (p == NULL || cred == NULL) {
973
NFSLOCKCLSTATE();
974
nfsv4_unlock(&clp->nfsc_lock, 0);
975
NFSUNLOCKCLSTATE();
976
return (EACCES);
977
}
978
/*
979
* If RFC3530 Sec. 14.2.33 is taken literally,
980
* NFSERR_CLIDINUSE will be returned persistently for the
981
* case where a new mount of the same file system is using
982
* a different principal. In practice, NFSERR_CLIDINUSE is
983
* only returned when there is outstanding unexpired state
984
* on the clientid. As such, try for twice the lease
985
* interval, if we know what that is. Otherwise, make a
986
* wild ass guess.
987
* The case of returning NFSERR_STALECLIENTID is far less
988
* likely, but might occur if there is a significant delay
989
* between doing the SetClientID and SetClientIDConfirm Ops,
990
* such that the server throws away the clientid before
991
* receiving the SetClientIDConfirm.
992
*/
993
if (clp->nfsc_renew > 0)
994
clidinusedelay = NFSCL_LEASE(clp->nfsc_renew) * 2;
995
else
996
clidinusedelay = 120;
997
trystalecnt = 3;
998
do {
999
error = nfsrpc_setclient(nmp, clp, 0, NULL, cred, p);
1000
if (error == NFSERR_STALECLIENTID ||
1001
error == NFSERR_STALEDONTRECOVER ||
1002
error == NFSERR_BADSESSION ||
1003
error == NFSERR_CLIDINUSE) {
1004
(void) nfs_catnap(PZERO, error, "nfs_setcl");
1005
} else if (error == NFSERR_MINORVERMISMATCH &&
1006
tryminvers) {
1007
if (nmp->nm_minorvers > 0)
1008
nmp->nm_minorvers--;
1009
else
1010
tryminvers = false;
1011
}
1012
} while (((error == NFSERR_STALECLIENTID ||
1013
error == NFSERR_BADSESSION ||
1014
error == NFSERR_STALEDONTRECOVER) && --trystalecnt > 0) ||
1015
(error == NFSERR_CLIDINUSE && --clidinusedelay > 0) ||
1016
(error == NFSERR_MINORVERMISMATCH && tryminvers));
1017
if (error) {
1018
NFSLOCKCLSTATE();
1019
nfsv4_unlock(&clp->nfsc_lock, 0);
1020
NFSUNLOCKCLSTATE();
1021
return (error);
1022
}
1023
clp->nfsc_flags |= NFSCLFLAGS_HASCLIENTID;
1024
}
1025
if (igotlock) {
1026
NFSLOCKCLSTATE();
1027
nfsv4_unlock(&clp->nfsc_lock, 1);
1028
NFSUNLOCKCLSTATE();
1029
}
1030
1031
*clpp = clp;
1032
return (0);
1033
}
1034
1035
/*
1036
* Get a reference to a clientid and return it, if valid.
1037
*/
1038
struct nfsclclient *
1039
nfscl_findcl(struct nfsmount *nmp)
1040
{
1041
struct nfsclclient *clp;
1042
1043
clp = nmp->nm_clp;
1044
if (clp == NULL || !(clp->nfsc_flags & NFSCLFLAGS_HASCLIENTID))
1045
return (NULL);
1046
return (clp);
1047
}
1048
1049
/*
1050
* Release the clientid structure. It may be locked or reference counted.
1051
*/
1052
static void
1053
nfscl_clrelease(struct nfsclclient *clp)
1054
{
1055
1056
if (clp->nfsc_lock.nfslock_lock & NFSV4LOCK_LOCK)
1057
nfsv4_unlock(&clp->nfsc_lock, 0);
1058
else
1059
nfsv4_relref(&clp->nfsc_lock);
1060
}
1061
1062
/*
1063
* External call for nfscl_clrelease.
1064
*/
1065
void
1066
nfscl_clientrelease(struct nfsclclient *clp)
1067
{
1068
1069
NFSLOCKCLSTATE();
1070
if (clp->nfsc_lock.nfslock_lock & NFSV4LOCK_LOCK)
1071
nfsv4_unlock(&clp->nfsc_lock, 0);
1072
else
1073
nfsv4_relref(&clp->nfsc_lock);
1074
NFSUNLOCKCLSTATE();
1075
}
1076
1077
/*
1078
* Called when wanting to lock a byte region.
1079
*/
1080
int
1081
nfscl_getbytelock(vnode_t vp, u_int64_t off, u_int64_t len,
1082
short type, struct ucred *cred, NFSPROC_T *p, struct nfsclclient *rclp,
1083
int recovery, void *id, int flags, u_int8_t *rownp, u_int8_t *ropenownp,
1084
struct nfscllockowner **lpp, int *newonep, int *donelocallyp)
1085
{
1086
struct nfscllockowner *lp;
1087
struct nfsclopen *op;
1088
struct nfsclclient *clp;
1089
struct nfscllockowner *nlp;
1090
struct nfscllock *nlop, *otherlop;
1091
struct nfscldeleg *dp = NULL, *ldp = NULL;
1092
struct nfscllockownerhead *lhp = NULL;
1093
struct nfsnode *np;
1094
u_int8_t own[NFSV4CL_LOCKNAMELEN], *ownp, openown[NFSV4CL_LOCKNAMELEN];
1095
u_int8_t *openownp;
1096
int error = 0, ret, donelocally = 0;
1097
u_int32_t mode;
1098
1099
/* For Lock Ops, the open mode doesn't matter, so use 0 to match any. */
1100
mode = 0;
1101
np = VTONFS(vp);
1102
*lpp = NULL;
1103
lp = NULL;
1104
*newonep = 0;
1105
*donelocallyp = 0;
1106
1107
/*
1108
* Might need these, so MALLOC them now, to
1109
* avoid a tsleep() in MALLOC later.
1110
*/
1111
nlp = malloc(
1112
sizeof (struct nfscllockowner), M_NFSCLLOCKOWNER, M_WAITOK);
1113
otherlop = malloc(
1114
sizeof (struct nfscllock), M_NFSCLLOCK, M_WAITOK);
1115
nlop = malloc(
1116
sizeof (struct nfscllock), M_NFSCLLOCK, M_WAITOK);
1117
nlop->nfslo_type = type;
1118
nlop->nfslo_first = off;
1119
if (len == NFS64BITSSET) {
1120
nlop->nfslo_end = NFS64BITSSET;
1121
} else {
1122
nlop->nfslo_end = off + len;
1123
if (nlop->nfslo_end <= nlop->nfslo_first)
1124
error = NFSERR_INVAL;
1125
}
1126
1127
if (!error) {
1128
if (recovery)
1129
clp = rclp;
1130
else
1131
error = nfscl_getcl(vp->v_mount, cred, p, false, true,
1132
&clp);
1133
}
1134
if (error) {
1135
free(nlp, M_NFSCLLOCKOWNER);
1136
free(otherlop, M_NFSCLLOCK);
1137
free(nlop, M_NFSCLLOCK);
1138
return (error);
1139
}
1140
1141
op = NULL;
1142
if (recovery) {
1143
ownp = rownp;
1144
openownp = ropenownp;
1145
} else {
1146
nfscl_filllockowner(id, own, flags);
1147
ownp = own;
1148
if (NFSHASONEOPENOWN(VFSTONFS(vp->v_mount)))
1149
nfscl_filllockowner(NULL, openown, F_POSIX);
1150
else
1151
nfscl_filllockowner(p->td_proc, openown, F_POSIX);
1152
openownp = openown;
1153
}
1154
if (!recovery) {
1155
NFSLOCKCLSTATE();
1156
/*
1157
* First, search for a delegation. If one exists for this file,
1158
* the lock can be done locally against it, so long as there
1159
* isn't a local lock conflict.
1160
*/
1161
ldp = dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh,
1162
np->n_fhp->nfh_len);
1163
/* Just sanity check for correct type of delegation */
1164
if (dp != NULL && ((dp->nfsdl_flags &
1165
(NFSCLDL_RECALL | NFSCLDL_DELEGRET)) != 0 ||
1166
(type == F_WRLCK &&
1167
(dp->nfsdl_flags & NFSCLDL_WRITE) == 0)))
1168
dp = NULL;
1169
}
1170
if (dp != NULL) {
1171
/* Now, find an open and maybe a lockowner. */
1172
ret = nfscl_getopen(&dp->nfsdl_owner, NULL, np->n_fhp->nfh_fh,
1173
np->n_fhp->nfh_len, openownp, ownp, mode, NULL, &op);
1174
if (ret)
1175
ret = nfscl_getopen(NULL, clp->nfsc_openhash,
1176
np->n_fhp->nfh_fh, np->n_fhp->nfh_len, openownp,
1177
ownp, mode, NULL, &op);
1178
if (!ret) {
1179
lhp = &dp->nfsdl_lock;
1180
TAILQ_REMOVE(&clp->nfsc_deleg, dp, nfsdl_list);
1181
TAILQ_INSERT_HEAD(&clp->nfsc_deleg, dp, nfsdl_list);
1182
dp->nfsdl_timestamp = NFSD_MONOSEC + 120;
1183
donelocally = 1;
1184
} else {
1185
dp = NULL;
1186
}
1187
}
1188
if (!donelocally) {
1189
/*
1190
* Get the related Open and maybe lockowner.
1191
*/
1192
error = nfscl_getopen(NULL, clp->nfsc_openhash,
1193
np->n_fhp->nfh_fh, np->n_fhp->nfh_len, openownp,
1194
ownp, mode, &lp, &op);
1195
if (!error)
1196
lhp = &op->nfso_lock;
1197
}
1198
if (!error && !recovery)
1199
error = nfscl_localconflict(clp, np->n_fhp->nfh_fh,
1200
np->n_fhp->nfh_len, nlop, ownp, ldp, NULL);
1201
if (error) {
1202
if (!recovery) {
1203
nfscl_clrelease(clp);
1204
NFSUNLOCKCLSTATE();
1205
}
1206
free(nlp, M_NFSCLLOCKOWNER);
1207
free(otherlop, M_NFSCLLOCK);
1208
free(nlop, M_NFSCLLOCK);
1209
return (error);
1210
}
1211
1212
/*
1213
* Ok, see if a lockowner exists and create one, as required.
1214
*/
1215
if (lp == NULL)
1216
LIST_FOREACH(lp, lhp, nfsl_list) {
1217
if (!NFSBCMP(lp->nfsl_owner, ownp, NFSV4CL_LOCKNAMELEN))
1218
break;
1219
}
1220
if (lp == NULL) {
1221
NFSBCOPY(ownp, nlp->nfsl_owner, NFSV4CL_LOCKNAMELEN);
1222
if (recovery)
1223
NFSBCOPY(ropenownp, nlp->nfsl_openowner,
1224
NFSV4CL_LOCKNAMELEN);
1225
else
1226
NFSBCOPY(op->nfso_own->nfsow_owner, nlp->nfsl_openowner,
1227
NFSV4CL_LOCKNAMELEN);
1228
nlp->nfsl_seqid = 0;
1229
nlp->nfsl_lockflags = flags;
1230
nlp->nfsl_inprog = NULL;
1231
nfscl_lockinit(&nlp->nfsl_rwlock);
1232
LIST_INIT(&nlp->nfsl_lock);
1233
if (donelocally) {
1234
nlp->nfsl_open = NULL;
1235
nfsstatsv1.cllocallockowners++;
1236
} else {
1237
nlp->nfsl_open = op;
1238
nfsstatsv1.cllockowners++;
1239
}
1240
LIST_INSERT_HEAD(lhp, nlp, nfsl_list);
1241
lp = nlp;
1242
nlp = NULL;
1243
*newonep = 1;
1244
}
1245
1246
/*
1247
* Now, update the byte ranges for locks.
1248
*/
1249
ret = nfscl_updatelock(lp, &nlop, &otherlop, donelocally);
1250
if (!ret)
1251
donelocally = 1;
1252
if (donelocally) {
1253
*donelocallyp = 1;
1254
if (!recovery)
1255
nfscl_clrelease(clp);
1256
} else {
1257
/*
1258
* Serial modifications on the lock owner for multiple threads
1259
* for the same process using a read/write lock.
1260
*/
1261
if (!recovery)
1262
nfscl_lockexcl(&lp->nfsl_rwlock, NFSCLSTATEMUTEXPTR);
1263
}
1264
if (!recovery)
1265
NFSUNLOCKCLSTATE();
1266
1267
if (nlp)
1268
free(nlp, M_NFSCLLOCKOWNER);
1269
if (nlop)
1270
free(nlop, M_NFSCLLOCK);
1271
if (otherlop)
1272
free(otherlop, M_NFSCLLOCK);
1273
1274
*lpp = lp;
1275
return (0);
1276
}
1277
1278
/*
1279
* Called to unlock a byte range, for LockU.
1280
*/
1281
int
1282
nfscl_relbytelock(vnode_t vp, u_int64_t off, u_int64_t len,
1283
__unused struct ucred *cred, NFSPROC_T *p, int callcnt,
1284
struct nfsclclient *clp, void *id, int flags,
1285
struct nfscllockowner **lpp, int *dorpcp)
1286
{
1287
struct nfscllockowner *lp;
1288
struct nfsclopen *op;
1289
struct nfscllock *nlop, *other_lop = NULL;
1290
struct nfscldeleg *dp;
1291
struct nfsnode *np;
1292
u_int8_t own[NFSV4CL_LOCKNAMELEN];
1293
int ret = 0, fnd;
1294
1295
np = VTONFS(vp);
1296
*lpp = NULL;
1297
*dorpcp = 0;
1298
1299
/*
1300
* Might need these, so MALLOC them now, to
1301
* avoid a tsleep() in MALLOC later.
1302
*/
1303
nlop = malloc(
1304
sizeof (struct nfscllock), M_NFSCLLOCK, M_WAITOK);
1305
nlop->nfslo_type = F_UNLCK;
1306
nlop->nfslo_first = off;
1307
if (len == NFS64BITSSET) {
1308
nlop->nfslo_end = NFS64BITSSET;
1309
} else {
1310
nlop->nfslo_end = off + len;
1311
if (nlop->nfslo_end <= nlop->nfslo_first) {
1312
free(nlop, M_NFSCLLOCK);
1313
return (NFSERR_INVAL);
1314
}
1315
}
1316
if (callcnt == 0) {
1317
other_lop = malloc(
1318
sizeof (struct nfscllock), M_NFSCLLOCK, M_WAITOK);
1319
*other_lop = *nlop;
1320
}
1321
nfscl_filllockowner(id, own, flags);
1322
dp = NULL;
1323
NFSLOCKCLSTATE();
1324
if (callcnt == 0)
1325
dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh,
1326
np->n_fhp->nfh_len);
1327
1328
/*
1329
* First, unlock any local regions on a delegation.
1330
*/
1331
if (dp != NULL) {
1332
/* Look for this lockowner. */
1333
LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
1334
if (!NFSBCMP(lp->nfsl_owner, own,
1335
NFSV4CL_LOCKNAMELEN))
1336
break;
1337
}
1338
if (lp != NULL)
1339
/* Use other_lop, so nlop is still available */
1340
(void)nfscl_updatelock(lp, &other_lop, NULL, 1);
1341
}
1342
1343
/*
1344
* Now, find a matching open/lockowner that hasn't already been done,
1345
* as marked by nfsl_inprog.
1346
*/
1347
lp = NULL;
1348
fnd = 0;
1349
LIST_FOREACH(op, NFSCLOPENHASH(clp, np->n_fhp->nfh_fh,
1350
np->n_fhp->nfh_len), nfso_hash) {
1351
if (op->nfso_fhlen == np->n_fhp->nfh_len &&
1352
!NFSBCMP(op->nfso_fh, np->n_fhp->nfh_fh, op->nfso_fhlen)) {
1353
LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
1354
if (lp->nfsl_inprog == NULL &&
1355
!NFSBCMP(lp->nfsl_owner, own,
1356
NFSV4CL_LOCKNAMELEN)) {
1357
fnd = 1;
1358
break;
1359
}
1360
}
1361
}
1362
if (fnd)
1363
break;
1364
}
1365
1366
if (lp != NULL) {
1367
ret = nfscl_updatelock(lp, &nlop, NULL, 0);
1368
if (ret)
1369
*dorpcp = 1;
1370
/*
1371
* Serial modifications on the lock owner for multiple
1372
* threads for the same process using a read/write lock.
1373
*/
1374
lp->nfsl_inprog = p;
1375
nfscl_lockexcl(&lp->nfsl_rwlock, NFSCLSTATEMUTEXPTR);
1376
*lpp = lp;
1377
}
1378
NFSUNLOCKCLSTATE();
1379
if (nlop)
1380
free(nlop, M_NFSCLLOCK);
1381
if (other_lop)
1382
free(other_lop, M_NFSCLLOCK);
1383
return (0);
1384
}
1385
1386
/*
1387
* Release all lockowners marked in progess for this process and file.
1388
*/
1389
void
1390
nfscl_releasealllocks(struct nfsclclient *clp, vnode_t vp, NFSPROC_T *p,
1391
void *id, int flags)
1392
{
1393
struct nfsclopen *op;
1394
struct nfscllockowner *lp;
1395
struct nfsnode *np;
1396
u_int8_t own[NFSV4CL_LOCKNAMELEN];
1397
1398
np = VTONFS(vp);
1399
nfscl_filllockowner(id, own, flags);
1400
NFSLOCKCLSTATE();
1401
LIST_FOREACH(op, NFSCLOPENHASH(clp, np->n_fhp->nfh_fh,
1402
np->n_fhp->nfh_len), nfso_hash) {
1403
if (op->nfso_fhlen == np->n_fhp->nfh_len &&
1404
!NFSBCMP(op->nfso_fh, np->n_fhp->nfh_fh, op->nfso_fhlen)) {
1405
LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
1406
if (lp->nfsl_inprog == p &&
1407
!NFSBCMP(lp->nfsl_owner, own,
1408
NFSV4CL_LOCKNAMELEN)) {
1409
lp->nfsl_inprog = NULL;
1410
nfscl_lockunlock(&lp->nfsl_rwlock);
1411
}
1412
}
1413
}
1414
}
1415
nfscl_clrelease(clp);
1416
NFSUNLOCKCLSTATE();
1417
}
1418
1419
/*
1420
* Called to find out if any bytes within the byte range specified are
1421
* write locked by the calling process. Used to determine if flushing
1422
* is required before a LockU.
1423
* If in doubt, return 1, so the flush will occur.
1424
*/
1425
int
1426
nfscl_checkwritelocked(vnode_t vp, struct flock *fl,
1427
struct ucred *cred, NFSPROC_T *p, void *id, int flags)
1428
{
1429
struct nfscllockowner *lp;
1430
struct nfsclopen *op;
1431
struct nfsclclient *clp;
1432
struct nfscllock *lop;
1433
struct nfscldeleg *dp;
1434
struct nfsnode *np;
1435
u_int64_t off, end;
1436
u_int8_t own[NFSV4CL_LOCKNAMELEN];
1437
int error = 0;
1438
1439
np = VTONFS(vp);
1440
switch (fl->l_whence) {
1441
case SEEK_SET:
1442
case SEEK_CUR:
1443
/*
1444
* Caller is responsible for adding any necessary offset
1445
* when SEEK_CUR is used.
1446
*/
1447
off = fl->l_start;
1448
break;
1449
case SEEK_END:
1450
off = np->n_size + fl->l_start;
1451
break;
1452
default:
1453
return (1);
1454
}
1455
if (fl->l_len != 0) {
1456
end = off + fl->l_len;
1457
if (end < off)
1458
return (1);
1459
} else {
1460
end = NFS64BITSSET;
1461
}
1462
1463
error = nfscl_getcl(vp->v_mount, cred, p, false, true, &clp);
1464
if (error)
1465
return (1);
1466
nfscl_filllockowner(id, own, flags);
1467
NFSLOCKCLSTATE();
1468
1469
/*
1470
* First check the delegation locks.
1471
*/
1472
dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
1473
if (dp != NULL) {
1474
/* No need to flush if it is a write delegation. */
1475
if ((dp->nfsdl_flags & NFSCLDL_WRITE) != 0) {
1476
nfscl_clrelease(clp);
1477
NFSUNLOCKCLSTATE();
1478
return (0);
1479
}
1480
LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
1481
if (!NFSBCMP(lp->nfsl_owner, own,
1482
NFSV4CL_LOCKNAMELEN))
1483
break;
1484
}
1485
if (lp != NULL) {
1486
LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) {
1487
if (lop->nfslo_first >= end)
1488
break;
1489
if (lop->nfslo_end <= off)
1490
continue;
1491
if (lop->nfslo_type == F_WRLCK) {
1492
nfscl_clrelease(clp);
1493
NFSUNLOCKCLSTATE();
1494
return (1);
1495
}
1496
}
1497
}
1498
}
1499
1500
/*
1501
* Now, check state against the server.
1502
*/
1503
LIST_FOREACH(op, NFSCLOPENHASH(clp, np->n_fhp->nfh_fh,
1504
np->n_fhp->nfh_len), nfso_hash) {
1505
if (op->nfso_fhlen == np->n_fhp->nfh_len &&
1506
!NFSBCMP(op->nfso_fh, np->n_fhp->nfh_fh, op->nfso_fhlen)) {
1507
LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
1508
if (!NFSBCMP(lp->nfsl_owner, own,
1509
NFSV4CL_LOCKNAMELEN))
1510
break;
1511
}
1512
if (lp != NULL) {
1513
LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) {
1514
if (lop->nfslo_first >= end)
1515
break;
1516
if (lop->nfslo_end <= off)
1517
continue;
1518
if (lop->nfslo_type == F_WRLCK) {
1519
nfscl_clrelease(clp);
1520
NFSUNLOCKCLSTATE();
1521
return (1);
1522
}
1523
}
1524
}
1525
}
1526
}
1527
nfscl_clrelease(clp);
1528
NFSUNLOCKCLSTATE();
1529
return (0);
1530
}
1531
1532
/*
1533
* Release a byte range lock owner structure.
1534
*/
1535
void
1536
nfscl_lockrelease(struct nfscllockowner *lp, int error, int candelete)
1537
{
1538
struct nfsclclient *clp;
1539
1540
if (lp == NULL)
1541
return;
1542
NFSLOCKCLSTATE();
1543
clp = lp->nfsl_open->nfso_own->nfsow_clp;
1544
if (error != 0 && candelete &&
1545
(lp->nfsl_rwlock.nfslock_lock & NFSV4LOCK_WANTED) == 0)
1546
nfscl_freelockowner(lp, 0);
1547
else
1548
nfscl_lockunlock(&lp->nfsl_rwlock);
1549
nfscl_clrelease(clp);
1550
NFSUNLOCKCLSTATE();
1551
}
1552
1553
/*
1554
* Unlink the open structure.
1555
*/
1556
static void
1557
nfscl_unlinkopen(struct nfsclopen *op)
1558
{
1559
1560
LIST_REMOVE(op, nfso_list);
1561
if (op->nfso_hash.le_prev != NULL)
1562
LIST_REMOVE(op, nfso_hash);
1563
}
1564
1565
/*
1566
* Free up an open structure and any associated byte range lock structures.
1567
*/
1568
void
1569
nfscl_freeopen(struct nfsclopen *op, int local, bool unlink)
1570
{
1571
1572
if (unlink)
1573
nfscl_unlinkopen(op);
1574
nfscl_freealllocks(&op->nfso_lock, local);
1575
free(op, M_NFSCLOPEN);
1576
if (local)
1577
nfsstatsv1.cllocalopens--;
1578
else
1579
nfsstatsv1.clopens--;
1580
}
1581
1582
/*
1583
* Free up all lock owners and associated locks.
1584
*/
1585
static void
1586
nfscl_freealllocks(struct nfscllockownerhead *lhp, int local)
1587
{
1588
struct nfscllockowner *lp, *nlp;
1589
1590
LIST_FOREACH_SAFE(lp, lhp, nfsl_list, nlp) {
1591
if ((lp->nfsl_rwlock.nfslock_lock & NFSV4LOCK_WANTED))
1592
panic("nfscllckw");
1593
nfscl_freelockowner(lp, local);
1594
}
1595
}
1596
1597
/*
1598
* Called for an Open when NFSERR_EXPIRED is received from the server.
1599
* If there are no byte range locks nor a Share Deny lost, try to do a
1600
* fresh Open. Otherwise, free the open.
1601
*/
1602
static int
1603
nfscl_expireopen(struct nfsclclient *clp, struct nfsclopen *op,
1604
struct nfsmount *nmp, struct ucred *cred, NFSPROC_T *p)
1605
{
1606
struct nfscllockowner *lp;
1607
struct nfscldeleg *dp;
1608
int mustdelete = 0, error;
1609
1610
/*
1611
* Look for any byte range lock(s).
1612
*/
1613
LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
1614
if (!LIST_EMPTY(&lp->nfsl_lock)) {
1615
mustdelete = 1;
1616
break;
1617
}
1618
}
1619
1620
/*
1621
* If no byte range lock(s) nor a Share deny, try to re-open.
1622
*/
1623
if (!mustdelete && (op->nfso_mode & NFSLCK_DENYBITS) == 0) {
1624
newnfs_copycred(&op->nfso_cred, cred);
1625
dp = NULL;
1626
error = nfsrpc_reopen(nmp, op->nfso_fh,
1627
op->nfso_fhlen, op->nfso_mode, op, &dp, cred, p);
1628
if (error) {
1629
mustdelete = 1;
1630
if (dp != NULL) {
1631
free(dp, M_NFSCLDELEG);
1632
dp = NULL;
1633
}
1634
}
1635
if (dp != NULL)
1636
nfscl_deleg(nmp->nm_mountp, clp, op->nfso_fh,
1637
op->nfso_fhlen, cred, p, dp);
1638
}
1639
1640
/*
1641
* If a byte range lock or Share deny or couldn't re-open, free it.
1642
*/
1643
if (mustdelete)
1644
nfscl_freeopen(op, 0, true);
1645
return (mustdelete);
1646
}
1647
1648
/*
1649
* Free up an open owner structure.
1650
*/
1651
static void
1652
nfscl_freeopenowner(struct nfsclowner *owp, int local)
1653
{
1654
int owned;
1655
1656
/*
1657
* Make sure the NFSCLSTATE mutex is held, to avoid races with
1658
* calls in nfscl_renewthread() that do not hold a reference
1659
* count on the nfsclclient and just the mutex.
1660
* The mutex will not be held for calls done with the exclusive
1661
* nfsclclient lock held, in particular, nfscl_hasexpired()
1662
* and nfscl_recalldeleg() might do this.
1663
*/
1664
owned = mtx_owned(NFSCLSTATEMUTEXPTR);
1665
if (owned == 0)
1666
NFSLOCKCLSTATE();
1667
LIST_REMOVE(owp, nfsow_list);
1668
if (owned == 0)
1669
NFSUNLOCKCLSTATE();
1670
free(owp, M_NFSCLOWNER);
1671
if (local)
1672
nfsstatsv1.cllocalopenowners--;
1673
else
1674
nfsstatsv1.clopenowners--;
1675
}
1676
1677
/*
1678
* Free up a byte range lock owner structure.
1679
*/
1680
void
1681
nfscl_freelockowner(struct nfscllockowner *lp, int local)
1682
{
1683
struct nfscllock *lop, *nlop;
1684
int owned;
1685
1686
/*
1687
* Make sure the NFSCLSTATE mutex is held, to avoid races with
1688
* calls in nfscl_renewthread() that do not hold a reference
1689
* count on the nfsclclient and just the mutex.
1690
* The mutex will not be held for calls done with the exclusive
1691
* nfsclclient lock held, in particular, nfscl_hasexpired()
1692
* and nfscl_recalldeleg() might do this.
1693
*/
1694
owned = mtx_owned(NFSCLSTATEMUTEXPTR);
1695
if (owned == 0)
1696
NFSLOCKCLSTATE();
1697
LIST_REMOVE(lp, nfsl_list);
1698
if (owned == 0)
1699
NFSUNLOCKCLSTATE();
1700
LIST_FOREACH_SAFE(lop, &lp->nfsl_lock, nfslo_list, nlop) {
1701
nfscl_freelock(lop, local);
1702
}
1703
free(lp, M_NFSCLLOCKOWNER);
1704
if (local)
1705
nfsstatsv1.cllocallockowners--;
1706
else
1707
nfsstatsv1.cllockowners--;
1708
}
1709
1710
/*
1711
* Free up a byte range lock structure.
1712
*/
1713
void
1714
nfscl_freelock(struct nfscllock *lop, int local)
1715
{
1716
1717
LIST_REMOVE(lop, nfslo_list);
1718
free(lop, M_NFSCLLOCK);
1719
if (local)
1720
nfsstatsv1.cllocallocks--;
1721
else
1722
nfsstatsv1.cllocks--;
1723
}
1724
1725
/*
1726
* Clean out the state related to a delegation.
1727
*/
1728
static void
1729
nfscl_cleandeleg(struct nfscldeleg *dp)
1730
{
1731
struct nfsclowner *owp, *nowp;
1732
struct nfsclopen *op;
1733
1734
LIST_FOREACH_SAFE(owp, &dp->nfsdl_owner, nfsow_list, nowp) {
1735
op = LIST_FIRST(&owp->nfsow_open);
1736
if (op != NULL) {
1737
if (LIST_NEXT(op, nfso_list) != NULL)
1738
panic("nfscleandel");
1739
nfscl_freeopen(op, 1, true);
1740
}
1741
nfscl_freeopenowner(owp, 1);
1742
}
1743
nfscl_freealllocks(&dp->nfsdl_lock, 1);
1744
}
1745
1746
/*
1747
* Free a delegation.
1748
*/
1749
static void
1750
nfscl_freedeleg(struct nfscldeleghead *hdp, struct nfscldeleg *dp, bool freeit)
1751
{
1752
1753
TAILQ_REMOVE(hdp, dp, nfsdl_list);
1754
LIST_REMOVE(dp, nfsdl_hash);
1755
dp->nfsdl_clp->nfsc_delegcnt--;
1756
if (freeit)
1757
free(dp, M_NFSCLDELEG);
1758
nfsstatsv1.cldelegates--;
1759
}
1760
1761
/*
1762
* Free up all state related to this client structure.
1763
*/
1764
static void
1765
nfscl_cleanclient(struct nfsclclient *clp)
1766
{
1767
struct nfsclowner *owp, *nowp;
1768
struct nfsclopen *op, *nop;
1769
struct nfscllayout *lyp, *nlyp;
1770
struct nfscldevinfo *dip, *ndip;
1771
1772
TAILQ_FOREACH_SAFE(lyp, &clp->nfsc_layout, nfsly_list, nlyp)
1773
nfscl_freelayout(lyp);
1774
1775
LIST_FOREACH_SAFE(dip, &clp->nfsc_devinfo, nfsdi_list, ndip)
1776
nfscl_freedevinfo(dip);
1777
1778
/* Now, all the OpenOwners, etc. */
1779
LIST_FOREACH_SAFE(owp, &clp->nfsc_owner, nfsow_list, nowp) {
1780
LIST_FOREACH_SAFE(op, &owp->nfsow_open, nfso_list, nop) {
1781
nfscl_freeopen(op, 0, true);
1782
}
1783
nfscl_freeopenowner(owp, 0);
1784
}
1785
}
1786
1787
/*
1788
* Called when an NFSERR_EXPIRED is received from the server.
1789
*/
1790
static void
1791
nfscl_expireclient(struct nfsclclient *clp, struct nfsmount *nmp,
1792
struct ucred *cred, NFSPROC_T *p)
1793
{
1794
struct nfsclowner *owp, *nowp, *towp;
1795
struct nfsclopen *op, *nop, *top;
1796
struct nfscldeleg *dp, *ndp;
1797
int ret, printed = 0;
1798
1799
/*
1800
* First, merge locally issued Opens into the list for the server.
1801
*/
1802
dp = TAILQ_FIRST(&clp->nfsc_deleg);
1803
while (dp != NULL) {
1804
ndp = TAILQ_NEXT(dp, nfsdl_list);
1805
owp = LIST_FIRST(&dp->nfsdl_owner);
1806
while (owp != NULL) {
1807
nowp = LIST_NEXT(owp, nfsow_list);
1808
op = LIST_FIRST(&owp->nfsow_open);
1809
if (op != NULL) {
1810
if (LIST_NEXT(op, nfso_list) != NULL)
1811
panic("nfsclexp");
1812
LIST_FOREACH(towp, &clp->nfsc_owner, nfsow_list) {
1813
if (!NFSBCMP(towp->nfsow_owner, owp->nfsow_owner,
1814
NFSV4CL_LOCKNAMELEN))
1815
break;
1816
}
1817
if (towp != NULL) {
1818
/* Merge opens in */
1819
LIST_FOREACH(top, &towp->nfsow_open, nfso_list) {
1820
if (top->nfso_fhlen == op->nfso_fhlen &&
1821
!NFSBCMP(top->nfso_fh, op->nfso_fh,
1822
op->nfso_fhlen)) {
1823
top->nfso_mode |= op->nfso_mode;
1824
top->nfso_opencnt += op->nfso_opencnt;
1825
break;
1826
}
1827
}
1828
if (top == NULL) {
1829
/* Just add the open to the owner list */
1830
LIST_REMOVE(op, nfso_list);
1831
op->nfso_own = towp;
1832
LIST_INSERT_HEAD(&towp->nfsow_open, op, nfso_list);
1833
LIST_INSERT_HEAD(NFSCLOPENHASH(clp, op->nfso_fh,
1834
op->nfso_fhlen), op, nfso_hash);
1835
nfsstatsv1.cllocalopens--;
1836
nfsstatsv1.clopens++;
1837
}
1838
} else {
1839
/* Just add the openowner to the client list */
1840
LIST_REMOVE(owp, nfsow_list);
1841
owp->nfsow_clp = clp;
1842
LIST_INSERT_HEAD(&clp->nfsc_owner, owp, nfsow_list);
1843
LIST_INSERT_HEAD(NFSCLOPENHASH(clp, op->nfso_fh,
1844
op->nfso_fhlen), op, nfso_hash);
1845
nfsstatsv1.cllocalopenowners--;
1846
nfsstatsv1.clopenowners++;
1847
nfsstatsv1.cllocalopens--;
1848
nfsstatsv1.clopens++;
1849
}
1850
}
1851
owp = nowp;
1852
}
1853
if (!printed && !LIST_EMPTY(&dp->nfsdl_lock)) {
1854
printed = 1;
1855
printf("nfsv4 expired locks lost\n");
1856
}
1857
nfscl_cleandeleg(dp);
1858
nfscl_freedeleg(&clp->nfsc_deleg, dp, true);
1859
dp = ndp;
1860
}
1861
if (!TAILQ_EMPTY(&clp->nfsc_deleg))
1862
panic("nfsclexp");
1863
1864
/*
1865
* Now, try and reopen against the server.
1866
*/
1867
LIST_FOREACH_SAFE(owp, &clp->nfsc_owner, nfsow_list, nowp) {
1868
owp->nfsow_seqid = 0;
1869
LIST_FOREACH_SAFE(op, &owp->nfsow_open, nfso_list, nop) {
1870
ret = nfscl_expireopen(clp, op, nmp, cred, p);
1871
if (ret && !printed) {
1872
printed = 1;
1873
printf("nfsv4 expired locks lost\n");
1874
}
1875
}
1876
if (LIST_EMPTY(&owp->nfsow_open))
1877
nfscl_freeopenowner(owp, 0);
1878
}
1879
}
1880
1881
/*
1882
* This function must be called after the process represented by "own" has
1883
* exited. Must be called with CLSTATE lock held.
1884
*/
1885
static void
1886
nfscl_cleanup_common(struct nfsclclient *clp, u_int8_t *own)
1887
{
1888
struct nfsclowner *owp, *nowp;
1889
struct nfscllockowner *lp;
1890
struct nfscldeleg *dp;
1891
1892
/* First, get rid of local locks on delegations. */
1893
TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) {
1894
LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
1895
if (!NFSBCMP(lp->nfsl_owner, own, NFSV4CL_LOCKNAMELEN)) {
1896
if ((lp->nfsl_rwlock.nfslock_lock & NFSV4LOCK_WANTED))
1897
panic("nfscllckw");
1898
nfscl_freelockowner(lp, 1);
1899
break;
1900
}
1901
}
1902
}
1903
owp = LIST_FIRST(&clp->nfsc_owner);
1904
while (owp != NULL) {
1905
nowp = LIST_NEXT(owp, nfsow_list);
1906
if (!NFSBCMP(owp->nfsow_owner, own,
1907
NFSV4CL_LOCKNAMELEN)) {
1908
/*
1909
* If there are children that haven't closed the
1910
* file descriptors yet, the opens will still be
1911
* here. For that case, let the renew thread clear
1912
* out the OpenOwner later.
1913
*/
1914
if (LIST_EMPTY(&owp->nfsow_open))
1915
nfscl_freeopenowner(owp, 0);
1916
else
1917
owp->nfsow_defunct = 1;
1918
break;
1919
}
1920
owp = nowp;
1921
}
1922
}
1923
1924
/*
1925
* Find open/lock owners for processes that have exited.
1926
*/
1927
static void
1928
nfscl_cleanupkext(struct nfsclclient *clp, struct nfscllockownerfhhead *lhp)
1929
{
1930
struct nfsclowner *owp, *nowp;
1931
struct nfsclopen *op;
1932
struct nfscllockowner *lp, *nlp;
1933
struct nfscldeleg *dp;
1934
uint8_t own[NFSV4CL_LOCKNAMELEN];
1935
1936
/*
1937
* All the pidhash locks must be acquired, since they are sx locks
1938
* and must be acquired before the mutexes. The pid(s) that will
1939
* be used aren't known yet, so all the locks need to be acquired.
1940
* Fortunately, this function is only performed once/sec.
1941
*/
1942
pidhash_slockall();
1943
NFSLOCKCLSTATE();
1944
LIST_FOREACH_SAFE(owp, &clp->nfsc_owner, nfsow_list, nowp) {
1945
LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
1946
LIST_FOREACH_SAFE(lp, &op->nfso_lock, nfsl_list, nlp) {
1947
if (LIST_EMPTY(&lp->nfsl_lock))
1948
nfscl_emptylockowner(lp, lhp);
1949
}
1950
}
1951
if (nfscl_procdoesntexist(owp->nfsow_owner)) {
1952
memcpy(own, owp->nfsow_owner, NFSV4CL_LOCKNAMELEN);
1953
nfscl_cleanup_common(clp, own);
1954
}
1955
}
1956
1957
/*
1958
* For the single open_owner case, these lock owners need to be
1959
* checked to see if they still exist separately.
1960
* This is because nfscl_procdoesntexist() never returns true for
1961
* the single open_owner so that the above doesn't ever call
1962
* nfscl_cleanup_common().
1963
*/
1964
TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) {
1965
LIST_FOREACH_SAFE(lp, &dp->nfsdl_lock, nfsl_list, nlp) {
1966
if (nfscl_procdoesntexist(lp->nfsl_owner)) {
1967
memcpy(own, lp->nfsl_owner,
1968
NFSV4CL_LOCKNAMELEN);
1969
nfscl_cleanup_common(clp, own);
1970
}
1971
}
1972
}
1973
NFSUNLOCKCLSTATE();
1974
pidhash_sunlockall();
1975
}
1976
1977
/*
1978
* Take the empty lock owner and move it to the local lhp list if the
1979
* associated process no longer exists.
1980
*/
1981
static void
1982
nfscl_emptylockowner(struct nfscllockowner *lp,
1983
struct nfscllockownerfhhead *lhp)
1984
{
1985
struct nfscllockownerfh *lfhp, *mylfhp;
1986
struct nfscllockowner *nlp;
1987
int fnd_it;
1988
1989
/* If not a Posix lock owner, just return. */
1990
if ((lp->nfsl_lockflags & F_POSIX) == 0)
1991
return;
1992
1993
fnd_it = 0;
1994
mylfhp = NULL;
1995
/*
1996
* First, search to see if this lock owner is already in the list.
1997
* If it is, then the associated process no longer exists.
1998
*/
1999
SLIST_FOREACH(lfhp, lhp, nfslfh_list) {
2000
if (lfhp->nfslfh_len == lp->nfsl_open->nfso_fhlen &&
2001
!NFSBCMP(lfhp->nfslfh_fh, lp->nfsl_open->nfso_fh,
2002
lfhp->nfslfh_len))
2003
mylfhp = lfhp;
2004
LIST_FOREACH(nlp, &lfhp->nfslfh_lock, nfsl_list)
2005
if (!NFSBCMP(nlp->nfsl_owner, lp->nfsl_owner,
2006
NFSV4CL_LOCKNAMELEN))
2007
fnd_it = 1;
2008
}
2009
/* If not found, check if process still exists. */
2010
if (fnd_it == 0 && nfscl_procdoesntexist(lp->nfsl_owner) == 0)
2011
return;
2012
2013
/* Move the lock owner over to the local list. */
2014
if (mylfhp == NULL) {
2015
mylfhp = malloc(sizeof(struct nfscllockownerfh), M_TEMP,
2016
M_NOWAIT);
2017
if (mylfhp == NULL)
2018
return;
2019
mylfhp->nfslfh_len = lp->nfsl_open->nfso_fhlen;
2020
NFSBCOPY(lp->nfsl_open->nfso_fh, mylfhp->nfslfh_fh,
2021
mylfhp->nfslfh_len);
2022
LIST_INIT(&mylfhp->nfslfh_lock);
2023
SLIST_INSERT_HEAD(lhp, mylfhp, nfslfh_list);
2024
}
2025
LIST_REMOVE(lp, nfsl_list);
2026
LIST_INSERT_HEAD(&mylfhp->nfslfh_lock, lp, nfsl_list);
2027
}
2028
2029
static int fake_global; /* Used to force visibility of MNTK_UNMOUNTF */
2030
/*
2031
* Called from nfs umount to free up the clientid.
2032
*/
2033
void
2034
nfscl_umount(struct nfsmount *nmp, NFSPROC_T *p, struct nfscldeleghead *dhp)
2035
{
2036
struct nfsclclient *clp;
2037
struct ucred *cred;
2038
int igotlock;
2039
2040
/*
2041
* For the case that matters, this is the thread that set
2042
* MNTK_UNMOUNTF, so it will see it set. The code that follows is
2043
* done to ensure that any thread executing nfscl_getcl() after
2044
* this time, will see MNTK_UNMOUNTF set. nfscl_getcl() uses the
2045
* mutex for NFSLOCKCLSTATE(), so it is "m" for the following
2046
* explanation, courtesy of Alan Cox.
2047
* What follows is a snippet from Alan Cox's email at:
2048
* https://docs.FreeBSD.org/cgi/mid.cgi?BANLkTikR3d65zPHo9==08ZfJ2vmqZucEvw
2049
*
2050
* 1. Set MNTK_UNMOUNTF
2051
* 2. Acquire a standard FreeBSD mutex "m".
2052
* 3. Update some data structures.
2053
* 4. Release mutex "m".
2054
*
2055
* Then, other threads that acquire "m" after step 4 has occurred will
2056
* see MNTK_UNMOUNTF as set. But, other threads that beat thread X to
2057
* step 2 may or may not see MNTK_UNMOUNTF as set.
2058
*/
2059
NFSLOCKCLSTATE();
2060
if ((nmp->nm_mountp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) {
2061
fake_global++;
2062
NFSUNLOCKCLSTATE();
2063
NFSLOCKCLSTATE();
2064
}
2065
2066
clp = nmp->nm_clp;
2067
if (clp != NULL) {
2068
if ((clp->nfsc_flags & NFSCLFLAGS_INITED) == 0)
2069
panic("nfscl umount");
2070
2071
/*
2072
* First, handshake with the nfscl renew thread, to terminate
2073
* it.
2074
*/
2075
clp->nfsc_flags |= NFSCLFLAGS_UMOUNT;
2076
while (clp->nfsc_flags & NFSCLFLAGS_HASTHREAD)
2077
(void)mtx_sleep(clp, NFSCLSTATEMUTEXPTR, PWAIT,
2078
"nfsclumnt", hz);
2079
2080
/*
2081
* Now, get the exclusive lock on the client state, so
2082
* that no uses of the state are still in progress.
2083
*/
2084
do {
2085
igotlock = nfsv4_lock(&clp->nfsc_lock, 1, NULL,
2086
NFSCLSTATEMUTEXPTR, NULL);
2087
} while (!igotlock);
2088
NFSUNLOCKCLSTATE();
2089
2090
/*
2091
* Free up all the state. It will expire on the server, but
2092
* maybe we should do a SetClientId/SetClientIdConfirm so
2093
* the server throws it away?
2094
*/
2095
LIST_REMOVE(clp, nfsc_list);
2096
nfscl_delegreturnall(clp, p, dhp);
2097
cred = newnfs_getcred();
2098
if (NFSHASNFSV4N(nmp)) {
2099
nfsrpc_destroysession(nmp, NULL, cred, p);
2100
nfsrpc_destroyclient(nmp, clp, cred, p);
2101
} else
2102
nfsrpc_setclient(nmp, clp, 0, NULL, cred, p);
2103
nfscl_cleanclient(clp);
2104
nmp->nm_clp = NULL;
2105
NFSFREECRED(cred);
2106
free(clp, M_NFSCLCLIENT);
2107
} else
2108
NFSUNLOCKCLSTATE();
2109
}
2110
2111
/*
2112
* This function is called when a server replies with NFSERR_STALECLIENTID
2113
* NFSERR_STALESTATEID or NFSERR_BADSESSION. It traverses the clientid lists,
2114
* doing Opens and Locks with reclaim. If these fail, it deletes the
2115
* corresponding state.
2116
*/
2117
static void
2118
nfscl_recover(struct nfsclclient *clp, bool *retokp, struct ucred *cred,
2119
NFSPROC_T *p)
2120
{
2121
struct nfsclowner *owp, *nowp;
2122
struct nfsclopen *op, *nop;
2123
struct nfscllockowner *lp, *nlp;
2124
struct nfscllock *lop, *nlop;
2125
struct nfscldeleg *dp, *ndp, *tdp;
2126
struct nfsmount *nmp;
2127
struct ucred *tcred;
2128
struct nfsclopenhead extra_open;
2129
struct nfscldeleghead extra_deleg;
2130
struct nfsreq *rep;
2131
u_int64_t len;
2132
u_int32_t delegtype = NFSV4OPEN_DELEGATEWRITE, mode;
2133
int i, igotlock = 0, error, trycnt, firstlock;
2134
struct nfscllayout *lyp, *nlyp;
2135
bool recovered_one;
2136
2137
/*
2138
* First, lock the client structure, so everyone else will
2139
* block when trying to use state.
2140
*/
2141
NFSLOCKCLSTATE();
2142
clp->nfsc_flags |= NFSCLFLAGS_RECVRINPROG;
2143
do {
2144
igotlock = nfsv4_lock(&clp->nfsc_lock, 1, NULL,
2145
NFSCLSTATEMUTEXPTR, NULL);
2146
} while (!igotlock);
2147
NFSUNLOCKCLSTATE();
2148
2149
nmp = clp->nfsc_nmp;
2150
if (nmp == NULL)
2151
panic("nfscl recover");
2152
2153
/*
2154
* For now, just get rid of all layouts. There may be a need
2155
* to do LayoutCommit Ops with reclaim == true later.
2156
*/
2157
TAILQ_FOREACH_SAFE(lyp, &clp->nfsc_layout, nfsly_list, nlyp)
2158
nfscl_freelayout(lyp);
2159
TAILQ_INIT(&clp->nfsc_layout);
2160
for (i = 0; i < NFSCLLAYOUTHASHSIZE; i++)
2161
LIST_INIT(&clp->nfsc_layouthash[i]);
2162
2163
trycnt = 5;
2164
tcred = NULL;
2165
do {
2166
error = nfsrpc_setclient(nmp, clp, 1, retokp, cred, p);
2167
} while ((error == NFSERR_STALECLIENTID ||
2168
error == NFSERR_BADSESSION ||
2169
error == NFSERR_STALEDONTRECOVER) && --trycnt > 0);
2170
if (error) {
2171
NFSLOCKCLSTATE();
2172
clp->nfsc_flags &= ~(NFSCLFLAGS_RECOVER |
2173
NFSCLFLAGS_RECVRINPROG);
2174
wakeup(&clp->nfsc_flags);
2175
nfsv4_unlock(&clp->nfsc_lock, 0);
2176
NFSUNLOCKCLSTATE();
2177
return;
2178
}
2179
clp->nfsc_flags |= NFSCLFLAGS_HASCLIENTID;
2180
clp->nfsc_flags &= ~NFSCLFLAGS_RECOVER;
2181
2182
/*
2183
* Mark requests already queued on the server, so that they don't
2184
* initiate another recovery cycle. Any requests already in the
2185
* queue that handle state information will have the old stale
2186
* clientid/stateid and will get a NFSERR_STALESTATEID,
2187
* NFSERR_STALECLIENTID or NFSERR_BADSESSION reply from the server.
2188
* This will be translated to NFSERR_STALEDONTRECOVER when
2189
* R_DONTRECOVER is set.
2190
*/
2191
NFSLOCKREQ();
2192
TAILQ_FOREACH(rep, &nfsd_reqq, r_chain) {
2193
if (rep->r_nmp == nmp)
2194
rep->r_flags |= R_DONTRECOVER;
2195
}
2196
NFSUNLOCKREQ();
2197
2198
/*
2199
* If nfsrpc_setclient() returns *retokp == true,
2200
* no more recovery is needed.
2201
*/
2202
if (*retokp)
2203
goto out;
2204
2205
/*
2206
* Now, mark all delegations "need reclaim".
2207
*/
2208
TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list)
2209
dp->nfsdl_flags |= NFSCLDL_NEEDRECLAIM;
2210
2211
TAILQ_INIT(&extra_deleg);
2212
LIST_INIT(&extra_open);
2213
/*
2214
* Now traverse the state lists, doing Open and Lock Reclaims.
2215
*/
2216
tcred = newnfs_getcred();
2217
recovered_one = false;
2218
owp = LIST_FIRST(&clp->nfsc_owner);
2219
while (owp != NULL) {
2220
nowp = LIST_NEXT(owp, nfsow_list);
2221
owp->nfsow_seqid = 0;
2222
op = LIST_FIRST(&owp->nfsow_open);
2223
while (op != NULL) {
2224
nop = LIST_NEXT(op, nfso_list);
2225
if (error != NFSERR_NOGRACE && error != NFSERR_BADSESSION) {
2226
/* Search for a delegation to reclaim with the open */
2227
TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) {
2228
if (!(dp->nfsdl_flags & NFSCLDL_NEEDRECLAIM))
2229
continue;
2230
if ((dp->nfsdl_flags & NFSCLDL_WRITE)) {
2231
mode = NFSV4OPEN_ACCESSWRITE;
2232
delegtype = NFSV4OPEN_DELEGATEWRITE;
2233
} else {
2234
mode = NFSV4OPEN_ACCESSREAD;
2235
delegtype = NFSV4OPEN_DELEGATEREAD;
2236
}
2237
if ((op->nfso_mode & mode) == mode &&
2238
op->nfso_fhlen == dp->nfsdl_fhlen &&
2239
!NFSBCMP(op->nfso_fh, dp->nfsdl_fh, op->nfso_fhlen))
2240
break;
2241
}
2242
ndp = dp;
2243
if (dp == NULL)
2244
delegtype = NFSV4OPEN_DELEGATENONE;
2245
newnfs_copycred(&op->nfso_cred, tcred);
2246
error = nfscl_tryopen(nmp, NULL, op->nfso_fh,
2247
op->nfso_fhlen, op->nfso_fh, op->nfso_fhlen,
2248
op->nfso_mode, op, NULL, 0, &ndp, 1, delegtype,
2249
tcred, p);
2250
if (!error) {
2251
recovered_one = true;
2252
/* Handle any replied delegation */
2253
if (ndp != NULL && ((ndp->nfsdl_flags & NFSCLDL_WRITE)
2254
|| NFSMNT_RDONLY(nmp->nm_mountp))) {
2255
if ((ndp->nfsdl_flags & NFSCLDL_WRITE))
2256
mode = NFSV4OPEN_ACCESSWRITE;
2257
else
2258
mode = NFSV4OPEN_ACCESSREAD;
2259
TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) {
2260
if (!(dp->nfsdl_flags & NFSCLDL_NEEDRECLAIM))
2261
continue;
2262
if ((op->nfso_mode & mode) == mode &&
2263
op->nfso_fhlen == dp->nfsdl_fhlen &&
2264
!NFSBCMP(op->nfso_fh, dp->nfsdl_fh,
2265
op->nfso_fhlen)) {
2266
dp->nfsdl_stateid = ndp->nfsdl_stateid;
2267
dp->nfsdl_sizelimit = ndp->nfsdl_sizelimit;
2268
dp->nfsdl_ace = ndp->nfsdl_ace;
2269
dp->nfsdl_change = ndp->nfsdl_change;
2270
dp->nfsdl_flags &= ~NFSCLDL_NEEDRECLAIM;
2271
if ((ndp->nfsdl_flags & NFSCLDL_RECALL))
2272
dp->nfsdl_flags |= NFSCLDL_RECALL;
2273
free(ndp, M_NFSCLDELEG);
2274
ndp = NULL;
2275
break;
2276
}
2277
}
2278
}
2279
if (ndp != NULL)
2280
TAILQ_INSERT_HEAD(&extra_deleg, ndp, nfsdl_list);
2281
2282
/* and reclaim all byte range locks */
2283
lp = LIST_FIRST(&op->nfso_lock);
2284
while (lp != NULL) {
2285
nlp = LIST_NEXT(lp, nfsl_list);
2286
lp->nfsl_seqid = 0;
2287
firstlock = 1;
2288
lop = LIST_FIRST(&lp->nfsl_lock);
2289
while (lop != NULL) {
2290
nlop = LIST_NEXT(lop, nfslo_list);
2291
if (lop->nfslo_end == NFS64BITSSET)
2292
len = NFS64BITSSET;
2293
else
2294
len = lop->nfslo_end - lop->nfslo_first;
2295
error = nfscl_trylock(nmp, NULL,
2296
op->nfso_fh, op->nfso_fhlen, lp,
2297
firstlock, 1, lop->nfslo_first, len,
2298
lop->nfslo_type, tcred, p);
2299
if (error != 0)
2300
nfscl_freelock(lop, 0);
2301
else
2302
firstlock = 0;
2303
lop = nlop;
2304
}
2305
/* If no locks, but a lockowner, just delete it. */
2306
if (LIST_EMPTY(&lp->nfsl_lock))
2307
nfscl_freelockowner(lp, 0);
2308
lp = nlp;
2309
}
2310
} else if (error == NFSERR_NOGRACE && !recovered_one &&
2311
NFSHASNFSV4N(nmp)) {
2312
/*
2313
* For NFSv4.1/4.2, the NFSERR_EXPIRED case will
2314
* actually end up here, since the client will do
2315
* a recovery for NFSERR_BADSESSION, but will get
2316
* an NFSERR_NOGRACE reply for the first "reclaim"
2317
* attempt.
2318
* So, call nfscl_expireclient() to recover the
2319
* opens as best we can and then do a reclaim
2320
* complete and return.
2321
*/
2322
nfsrpc_reclaimcomplete(nmp, cred, p);
2323
nfscl_expireclient(clp, nmp, tcred, p);
2324
goto out;
2325
}
2326
}
2327
if (error != 0 && error != NFSERR_BADSESSION)
2328
nfscl_freeopen(op, 0, true);
2329
op = nop;
2330
}
2331
owp = nowp;
2332
}
2333
2334
/*
2335
* Now, try and get any delegations not yet reclaimed by cobbling
2336
* to-gether an appropriate open.
2337
*/
2338
nowp = NULL;
2339
dp = TAILQ_FIRST(&clp->nfsc_deleg);
2340
while (dp != NULL) {
2341
ndp = TAILQ_NEXT(dp, nfsdl_list);
2342
if ((dp->nfsdl_flags & NFSCLDL_NEEDRECLAIM)) {
2343
if (nowp == NULL) {
2344
nowp = malloc(
2345
sizeof (struct nfsclowner), M_NFSCLOWNER, M_WAITOK);
2346
/*
2347
* Name must be as long an largest possible
2348
* NFSV4CL_LOCKNAMELEN. 12 for now.
2349
*/
2350
NFSBCOPY("RECLAIMDELEG", nowp->nfsow_owner,
2351
NFSV4CL_LOCKNAMELEN);
2352
LIST_INIT(&nowp->nfsow_open);
2353
nowp->nfsow_clp = clp;
2354
nowp->nfsow_seqid = 0;
2355
nowp->nfsow_defunct = 0;
2356
nfscl_lockinit(&nowp->nfsow_rwlock);
2357
}
2358
nop = NULL;
2359
if (error != NFSERR_NOGRACE && error != NFSERR_BADSESSION) {
2360
nop = malloc(sizeof (struct nfsclopen) +
2361
dp->nfsdl_fhlen - 1, M_NFSCLOPEN, M_WAITOK);
2362
nop->nfso_own = nowp;
2363
if ((dp->nfsdl_flags & NFSCLDL_WRITE)) {
2364
nop->nfso_mode = NFSV4OPEN_ACCESSWRITE;
2365
delegtype = NFSV4OPEN_DELEGATEWRITE;
2366
} else {
2367
nop->nfso_mode = NFSV4OPEN_ACCESSREAD;
2368
delegtype = NFSV4OPEN_DELEGATEREAD;
2369
}
2370
nop->nfso_opencnt = 0;
2371
nop->nfso_posixlock = 1;
2372
nop->nfso_fhlen = dp->nfsdl_fhlen;
2373
NFSBCOPY(dp->nfsdl_fh, nop->nfso_fh, dp->nfsdl_fhlen);
2374
LIST_INIT(&nop->nfso_lock);
2375
nop->nfso_stateid.seqid = 0;
2376
nop->nfso_stateid.other[0] = 0;
2377
nop->nfso_stateid.other[1] = 0;
2378
nop->nfso_stateid.other[2] = 0;
2379
newnfs_copycred(&dp->nfsdl_cred, tcred);
2380
newnfs_copyincred(tcred, &nop->nfso_cred);
2381
tdp = NULL;
2382
error = nfscl_tryopen(nmp, NULL, nop->nfso_fh,
2383
nop->nfso_fhlen, nop->nfso_fh, nop->nfso_fhlen,
2384
nop->nfso_mode, nop, NULL, 0, &tdp, 1,
2385
delegtype, tcred, p);
2386
if (tdp != NULL) {
2387
if ((tdp->nfsdl_flags & NFSCLDL_WRITE))
2388
mode = NFSV4OPEN_ACCESSWRITE;
2389
else
2390
mode = NFSV4OPEN_ACCESSREAD;
2391
if ((nop->nfso_mode & mode) == mode &&
2392
nop->nfso_fhlen == tdp->nfsdl_fhlen &&
2393
!NFSBCMP(nop->nfso_fh, tdp->nfsdl_fh,
2394
nop->nfso_fhlen)) {
2395
dp->nfsdl_stateid = tdp->nfsdl_stateid;
2396
dp->nfsdl_sizelimit = tdp->nfsdl_sizelimit;
2397
dp->nfsdl_ace = tdp->nfsdl_ace;
2398
dp->nfsdl_change = tdp->nfsdl_change;
2399
dp->nfsdl_flags &= ~NFSCLDL_NEEDRECLAIM;
2400
if ((tdp->nfsdl_flags & NFSCLDL_RECALL))
2401
dp->nfsdl_flags |= NFSCLDL_RECALL;
2402
free(tdp, M_NFSCLDELEG);
2403
} else {
2404
TAILQ_INSERT_HEAD(&extra_deleg, tdp, nfsdl_list);
2405
}
2406
}
2407
}
2408
if (error) {
2409
if (nop != NULL)
2410
free(nop, M_NFSCLOPEN);
2411
if (error == NFSERR_NOGRACE && !recovered_one &&
2412
NFSHASNFSV4N(nmp)) {
2413
/*
2414
* For NFSv4.1/4.2, the NFSERR_EXPIRED case will
2415
* actually end up here, since the client will do
2416
* a recovery for NFSERR_BADSESSION, but will get
2417
* an NFSERR_NOGRACE reply for the first "reclaim"
2418
* attempt.
2419
* So, call nfscl_expireclient() to recover the
2420
* opens as best we can and then do a reclaim
2421
* complete and return.
2422
*/
2423
nfsrpc_reclaimcomplete(nmp, cred, p);
2424
nfscl_expireclient(clp, nmp, tcred, p);
2425
free(nowp, M_NFSCLOWNER);
2426
goto out;
2427
}
2428
/*
2429
* Couldn't reclaim it, so throw the state
2430
* away. Ouch!!
2431
*/
2432
nfscl_cleandeleg(dp);
2433
nfscl_freedeleg(&clp->nfsc_deleg, dp, true);
2434
} else {
2435
recovered_one = true;
2436
LIST_INSERT_HEAD(&extra_open, nop, nfso_list);
2437
}
2438
}
2439
dp = ndp;
2440
}
2441
2442
/*
2443
* Now, get rid of extra Opens and Delegations.
2444
*/
2445
LIST_FOREACH_SAFE(op, &extra_open, nfso_list, nop) {
2446
do {
2447
newnfs_copycred(&op->nfso_cred, tcred);
2448
error = nfscl_tryclose(op, tcred, nmp, p, true);
2449
if (error == NFSERR_GRACE)
2450
(void) nfs_catnap(PZERO, error, "nfsexcls");
2451
} while (error == NFSERR_GRACE);
2452
LIST_REMOVE(op, nfso_list);
2453
free(op, M_NFSCLOPEN);
2454
}
2455
if (nowp != NULL)
2456
free(nowp, M_NFSCLOWNER);
2457
2458
TAILQ_FOREACH_SAFE(dp, &extra_deleg, nfsdl_list, ndp) {
2459
do {
2460
newnfs_copycred(&dp->nfsdl_cred, tcred);
2461
error = nfscl_trydelegreturn(dp, tcred, nmp, p);
2462
if (error == NFSERR_GRACE)
2463
(void) nfs_catnap(PZERO, error, "nfsexdlg");
2464
} while (error == NFSERR_GRACE);
2465
TAILQ_REMOVE(&extra_deleg, dp, nfsdl_list);
2466
free(dp, M_NFSCLDELEG);
2467
}
2468
2469
/* For NFSv4.1 or later, do a RECLAIM_COMPLETE. */
2470
if (NFSHASNFSV4N(nmp))
2471
(void)nfsrpc_reclaimcomplete(nmp, cred, p);
2472
2473
out:
2474
NFSLOCKCLSTATE();
2475
clp->nfsc_flags &= ~NFSCLFLAGS_RECVRINPROG;
2476
wakeup(&clp->nfsc_flags);
2477
nfsv4_unlock(&clp->nfsc_lock, 0);
2478
NFSUNLOCKCLSTATE();
2479
if (tcred != NULL)
2480
NFSFREECRED(tcred);
2481
}
2482
2483
/*
2484
* This function is called when a server replies with NFSERR_EXPIRED.
2485
* It deletes all state for the client and does a fresh SetClientId/confirm.
2486
* XXX Someday it should post a signal to the process(es) that hold the
2487
* state, so they know that lock state has been lost.
2488
*/
2489
int
2490
nfscl_hasexpired(struct nfsclclient *clp, u_int32_t clidrev, NFSPROC_T *p)
2491
{
2492
struct nfsmount *nmp;
2493
struct ucred *cred;
2494
int igotlock = 0, error, trycnt;
2495
2496
/*
2497
* If the clientid has gone away or a new SetClientid has already
2498
* been done, just return ok.
2499
*/
2500
if (clp == NULL || clidrev != clp->nfsc_clientidrev)
2501
return (0);
2502
2503
/*
2504
* First, lock the client structure, so everyone else will
2505
* block when trying to use state. Also, use NFSCLFLAGS_EXPIREIT so
2506
* that only one thread does the work.
2507
*/
2508
NFSLOCKCLSTATE();
2509
clp->nfsc_flags |= NFSCLFLAGS_EXPIREIT;
2510
do {
2511
igotlock = nfsv4_lock(&clp->nfsc_lock, 1, NULL,
2512
NFSCLSTATEMUTEXPTR, NULL);
2513
} while (!igotlock && (clp->nfsc_flags & NFSCLFLAGS_EXPIREIT));
2514
if ((clp->nfsc_flags & NFSCLFLAGS_EXPIREIT) == 0) {
2515
if (igotlock)
2516
nfsv4_unlock(&clp->nfsc_lock, 0);
2517
NFSUNLOCKCLSTATE();
2518
return (0);
2519
}
2520
clp->nfsc_flags |= NFSCLFLAGS_RECVRINPROG;
2521
NFSUNLOCKCLSTATE();
2522
2523
nmp = clp->nfsc_nmp;
2524
if (nmp == NULL)
2525
panic("nfscl expired");
2526
cred = newnfs_getcred();
2527
trycnt = 5;
2528
do {
2529
error = nfsrpc_setclient(nmp, clp, 0, NULL, cred, p);
2530
} while ((error == NFSERR_STALECLIENTID ||
2531
error == NFSERR_BADSESSION ||
2532
error == NFSERR_STALEDONTRECOVER) && --trycnt > 0);
2533
if (error) {
2534
NFSLOCKCLSTATE();
2535
clp->nfsc_flags &= ~NFSCLFLAGS_RECOVER;
2536
} else {
2537
/*
2538
* Expire the state for the client.
2539
*/
2540
nfscl_expireclient(clp, nmp, cred, p);
2541
NFSLOCKCLSTATE();
2542
clp->nfsc_flags |= NFSCLFLAGS_HASCLIENTID;
2543
clp->nfsc_flags &= ~NFSCLFLAGS_RECOVER;
2544
}
2545
clp->nfsc_flags &= ~(NFSCLFLAGS_EXPIREIT | NFSCLFLAGS_RECVRINPROG);
2546
wakeup(&clp->nfsc_flags);
2547
nfsv4_unlock(&clp->nfsc_lock, 0);
2548
NFSUNLOCKCLSTATE();
2549
NFSFREECRED(cred);
2550
return (error);
2551
}
2552
2553
/*
2554
* This function inserts a lock in the list after insert_lop.
2555
*/
2556
static void
2557
nfscl_insertlock(struct nfscllockowner *lp, struct nfscllock *new_lop,
2558
struct nfscllock *insert_lop, int local)
2559
{
2560
2561
if ((struct nfscllockowner *)insert_lop == lp)
2562
LIST_INSERT_HEAD(&lp->nfsl_lock, new_lop, nfslo_list);
2563
else
2564
LIST_INSERT_AFTER(insert_lop, new_lop, nfslo_list);
2565
if (local)
2566
nfsstatsv1.cllocallocks++;
2567
else
2568
nfsstatsv1.cllocks++;
2569
}
2570
2571
/*
2572
* This function updates the locking for a lock owner and given file. It
2573
* maintains a list of lock ranges ordered on increasing file offset that
2574
* are NFSCLLOCK_READ or NFSCLLOCK_WRITE and non-overlapping (aka POSIX style).
2575
* It always adds new_lop to the list and sometimes uses the one pointed
2576
* at by other_lopp.
2577
* Returns 1 if the locks were modified, 0 otherwise.
2578
*/
2579
static int
2580
nfscl_updatelock(struct nfscllockowner *lp, struct nfscllock **new_lopp,
2581
struct nfscllock **other_lopp, int local)
2582
{
2583
struct nfscllock *new_lop = *new_lopp;
2584
struct nfscllock *lop, *tlop, *ilop;
2585
struct nfscllock *other_lop;
2586
int unlock = 0, modified = 0;
2587
u_int64_t tmp;
2588
2589
/*
2590
* Work down the list until the lock is merged.
2591
*/
2592
if (new_lop->nfslo_type == F_UNLCK)
2593
unlock = 1;
2594
ilop = (struct nfscllock *)lp;
2595
lop = LIST_FIRST(&lp->nfsl_lock);
2596
while (lop != NULL) {
2597
/*
2598
* Only check locks for this file that aren't before the start of
2599
* new lock's range.
2600
*/
2601
if (lop->nfslo_end >= new_lop->nfslo_first) {
2602
if (new_lop->nfslo_end < lop->nfslo_first) {
2603
/*
2604
* If the new lock ends before the start of the
2605
* current lock's range, no merge, just insert
2606
* the new lock.
2607
*/
2608
break;
2609
}
2610
if (new_lop->nfslo_type == lop->nfslo_type ||
2611
(new_lop->nfslo_first <= lop->nfslo_first &&
2612
new_lop->nfslo_end >= lop->nfslo_end)) {
2613
/*
2614
* This lock can be absorbed by the new lock/unlock.
2615
* This happens when it covers the entire range
2616
* of the old lock or is contiguous
2617
* with the old lock and is of the same type or an
2618
* unlock.
2619
*/
2620
if (new_lop->nfslo_type != lop->nfslo_type ||
2621
new_lop->nfslo_first != lop->nfslo_first ||
2622
new_lop->nfslo_end != lop->nfslo_end)
2623
modified = 1;
2624
if (lop->nfslo_first < new_lop->nfslo_first)
2625
new_lop->nfslo_first = lop->nfslo_first;
2626
if (lop->nfslo_end > new_lop->nfslo_end)
2627
new_lop->nfslo_end = lop->nfslo_end;
2628
tlop = lop;
2629
lop = LIST_NEXT(lop, nfslo_list);
2630
nfscl_freelock(tlop, local);
2631
continue;
2632
}
2633
2634
/*
2635
* All these cases are for contiguous locks that are not the
2636
* same type, so they can't be merged.
2637
*/
2638
if (new_lop->nfslo_first <= lop->nfslo_first) {
2639
/*
2640
* This case is where the new lock overlaps with the
2641
* first part of the old lock. Move the start of the
2642
* old lock to just past the end of the new lock. The
2643
* new lock will be inserted in front of the old, since
2644
* ilop hasn't been updated. (We are done now.)
2645
*/
2646
if (lop->nfslo_first != new_lop->nfslo_end) {
2647
lop->nfslo_first = new_lop->nfslo_end;
2648
modified = 1;
2649
}
2650
break;
2651
}
2652
if (new_lop->nfslo_end >= lop->nfslo_end) {
2653
/*
2654
* This case is where the new lock overlaps with the
2655
* end of the old lock's range. Move the old lock's
2656
* end to just before the new lock's first and insert
2657
* the new lock after the old lock.
2658
* Might not be done yet, since the new lock could
2659
* overlap further locks with higher ranges.
2660
*/
2661
if (lop->nfslo_end != new_lop->nfslo_first) {
2662
lop->nfslo_end = new_lop->nfslo_first;
2663
modified = 1;
2664
}
2665
ilop = lop;
2666
lop = LIST_NEXT(lop, nfslo_list);
2667
continue;
2668
}
2669
/*
2670
* The final case is where the new lock's range is in the
2671
* middle of the current lock's and splits the current lock
2672
* up. Use *other_lopp to handle the second part of the
2673
* split old lock range. (We are done now.)
2674
* For unlock, we use new_lop as other_lop and tmp, since
2675
* other_lop and new_lop are the same for this case.
2676
* We noted the unlock case above, so we don't need
2677
* new_lop->nfslo_type any longer.
2678
*/
2679
tmp = new_lop->nfslo_first;
2680
if (unlock) {
2681
other_lop = new_lop;
2682
*new_lopp = NULL;
2683
} else {
2684
other_lop = *other_lopp;
2685
*other_lopp = NULL;
2686
}
2687
other_lop->nfslo_first = new_lop->nfslo_end;
2688
other_lop->nfslo_end = lop->nfslo_end;
2689
other_lop->nfslo_type = lop->nfslo_type;
2690
lop->nfslo_end = tmp;
2691
nfscl_insertlock(lp, other_lop, lop, local);
2692
ilop = lop;
2693
modified = 1;
2694
break;
2695
}
2696
ilop = lop;
2697
lop = LIST_NEXT(lop, nfslo_list);
2698
if (lop == NULL)
2699
break;
2700
}
2701
2702
/*
2703
* Insert the new lock in the list at the appropriate place.
2704
*/
2705
if (!unlock) {
2706
nfscl_insertlock(lp, new_lop, ilop, local);
2707
*new_lopp = NULL;
2708
modified = 1;
2709
}
2710
return (modified);
2711
}
2712
2713
/*
2714
* This function must be run as a kernel thread.
2715
* It does Renew Ops and recovery, when required.
2716
*/
2717
void
2718
nfscl_renewthread(struct nfsclclient *clp, NFSPROC_T *p)
2719
{
2720
struct nfsclowner *owp, *nowp;
2721
struct nfsclopen *op;
2722
struct nfscllockowner *lp, *nlp;
2723
struct nfscldeleghead dh;
2724
struct nfscldeleg *dp, *ndp;
2725
struct ucred *cred;
2726
u_int32_t clidrev;
2727
int error, cbpathdown, islept, igotlock, ret, clearok;
2728
uint32_t recover_done_time = 0;
2729
time_t mytime;
2730
static time_t prevsec = 0;
2731
struct nfscllockownerfh *lfhp, *nlfhp;
2732
struct nfscllockownerfhhead lfh;
2733
struct nfscllayout *lyp, *nlyp;
2734
struct nfscldevinfo *dip, *ndip;
2735
struct nfscllayouthead rlh;
2736
struct nfsclrecalllayout *recallp;
2737
struct nfsclds *dsp;
2738
bool retok;
2739
struct mount *mp;
2740
vnode_t vp;
2741
2742
cred = newnfs_getcred();
2743
NFSLOCKCLSTATE();
2744
clp->nfsc_flags |= NFSCLFLAGS_HASTHREAD;
2745
mp = clp->nfsc_nmp->nm_mountp;
2746
NFSUNLOCKCLSTATE();
2747
for(;;) {
2748
newnfs_setroot(cred);
2749
cbpathdown = 0;
2750
if (clp->nfsc_flags & NFSCLFLAGS_RECOVER) {
2751
/*
2752
* Only allow one full recover within 1/2 of the lease
2753
* duration (nfsc_renew).
2754
* retok is value/result. If passed in set to true,
2755
* it indicates only a CreateSession operation should
2756
* be attempted.
2757
* If it is returned true, it indicates that the
2758
* recovery only required a CreateSession.
2759
*/
2760
retok = true;
2761
if (recover_done_time < NFSD_MONOSEC) {
2762
recover_done_time = NFSD_MONOSEC +
2763
clp->nfsc_renew;
2764
retok = false;
2765
}
2766
NFSCL_DEBUG(1, "Doing recovery, only "
2767
"createsession=%d\n", retok);
2768
nfscl_recover(clp, &retok, cred, p);
2769
}
2770
if (clp->nfsc_expire <= NFSD_MONOSEC &&
2771
(clp->nfsc_flags & NFSCLFLAGS_HASCLIENTID)) {
2772
clp->nfsc_expire = NFSD_MONOSEC + clp->nfsc_renew;
2773
clidrev = clp->nfsc_clientidrev;
2774
error = nfsrpc_renew(clp, NULL, cred, p);
2775
if (error == NFSERR_CBPATHDOWN)
2776
cbpathdown = 1;
2777
else if (error == NFSERR_STALECLIENTID) {
2778
NFSLOCKCLSTATE();
2779
clp->nfsc_flags |= NFSCLFLAGS_RECOVER;
2780
NFSUNLOCKCLSTATE();
2781
} else if (error == NFSERR_EXPIRED)
2782
(void) nfscl_hasexpired(clp, clidrev, p);
2783
}
2784
2785
checkdsrenew:
2786
if (NFSHASNFSV4N(clp->nfsc_nmp)) {
2787
/* Do renews for any DS sessions. */
2788
NFSLOCKMNT(clp->nfsc_nmp);
2789
/* Skip first entry, since the MDS is handled above. */
2790
dsp = TAILQ_FIRST(&clp->nfsc_nmp->nm_sess);
2791
if (dsp != NULL)
2792
dsp = TAILQ_NEXT(dsp, nfsclds_list);
2793
while (dsp != NULL) {
2794
if (dsp->nfsclds_expire <= NFSD_MONOSEC &&
2795
dsp->nfsclds_sess.nfsess_defunct == 0) {
2796
dsp->nfsclds_expire = NFSD_MONOSEC +
2797
clp->nfsc_renew;
2798
NFSUNLOCKMNT(clp->nfsc_nmp);
2799
(void)nfsrpc_renew(clp, dsp, cred, p);
2800
goto checkdsrenew;
2801
}
2802
dsp = TAILQ_NEXT(dsp, nfsclds_list);
2803
}
2804
NFSUNLOCKMNT(clp->nfsc_nmp);
2805
}
2806
2807
TAILQ_INIT(&dh);
2808
NFSLOCKCLSTATE();
2809
if (cbpathdown)
2810
/* It's a Total Recall! */
2811
nfscl_totalrecall(clp);
2812
2813
/*
2814
* Now, handle defunct owners.
2815
*/
2816
LIST_FOREACH_SAFE(owp, &clp->nfsc_owner, nfsow_list, nowp) {
2817
if (LIST_EMPTY(&owp->nfsow_open)) {
2818
if (owp->nfsow_defunct != 0)
2819
nfscl_freeopenowner(owp, 0);
2820
}
2821
}
2822
2823
/*
2824
* Do the recall on any delegations. To avoid trouble, always
2825
* come back up here after having slept.
2826
*/
2827
igotlock = 0;
2828
tryagain:
2829
dp = TAILQ_FIRST(&clp->nfsc_deleg);
2830
while (dp != NULL) {
2831
ndp = TAILQ_NEXT(dp, nfsdl_list);
2832
if ((dp->nfsdl_flags & NFSCLDL_RECALL)) {
2833
/*
2834
* Wait for outstanding I/O ops to be done.
2835
*/
2836
if (dp->nfsdl_rwlock.nfslock_usecnt > 0) {
2837
if (igotlock) {
2838
nfsv4_unlock(&clp->nfsc_lock, 0);
2839
igotlock = 0;
2840
}
2841
dp->nfsdl_rwlock.nfslock_lock |=
2842
NFSV4LOCK_WANTED;
2843
msleep(&dp->nfsdl_rwlock,
2844
NFSCLSTATEMUTEXPTR, PVFS, "nfscld",
2845
5 * hz);
2846
if (NFSCL_FORCEDISM(mp))
2847
goto terminate;
2848
goto tryagain;
2849
}
2850
while (!igotlock) {
2851
igotlock = nfsv4_lock(&clp->nfsc_lock, 1,
2852
&islept, NFSCLSTATEMUTEXPTR, mp);
2853
if (igotlock == 0 && NFSCL_FORCEDISM(mp))
2854
goto terminate;
2855
if (islept)
2856
goto tryagain;
2857
}
2858
NFSUNLOCKCLSTATE();
2859
newnfs_copycred(&dp->nfsdl_cred, cred);
2860
ret = nfscl_recalldeleg(clp, clp->nfsc_nmp, dp,
2861
NULL, cred, p, 1, &vp);
2862
if (!ret) {
2863
nfscl_cleandeleg(dp);
2864
TAILQ_REMOVE(&clp->nfsc_deleg, dp,
2865
nfsdl_list);
2866
LIST_REMOVE(dp, nfsdl_hash);
2867
TAILQ_INSERT_HEAD(&dh, dp, nfsdl_list);
2868
clp->nfsc_delegcnt--;
2869
nfsstatsv1.cldelegates--;
2870
}
2871
NFSLOCKCLSTATE();
2872
/*
2873
* The nfsc_lock must be released before doing
2874
* vrele(), since it might call nfs_inactive().
2875
* For the unlikely case where the vnode failed
2876
* to be acquired by nfscl_recalldeleg(), a
2877
* VOP_RECLAIM() should be in progress and it
2878
* will return the delegation.
2879
*/
2880
nfsv4_unlock(&clp->nfsc_lock, 0);
2881
igotlock = 0;
2882
if (vp != NULL) {
2883
NFSUNLOCKCLSTATE();
2884
vrele(vp);
2885
NFSLOCKCLSTATE();
2886
}
2887
goto tryagain;
2888
}
2889
dp = ndp;
2890
}
2891
2892
/*
2893
* Clear out old delegations, if we are above the high water
2894
* mark. Only clear out ones with no state related to them.
2895
* The tailq list is in LRU order.
2896
*/
2897
dp = TAILQ_LAST(&clp->nfsc_deleg, nfscldeleghead);
2898
while (clp->nfsc_delegcnt > clp->nfsc_deleghighwater &&
2899
dp != NULL) {
2900
ndp = TAILQ_PREV(dp, nfscldeleghead, nfsdl_list);
2901
if (dp->nfsdl_rwlock.nfslock_usecnt == 0 &&
2902
dp->nfsdl_rwlock.nfslock_lock == 0 &&
2903
dp->nfsdl_timestamp < NFSD_MONOSEC &&
2904
(dp->nfsdl_flags & (NFSCLDL_RECALL | NFSCLDL_ZAPPED |
2905
NFSCLDL_NEEDRECLAIM | NFSCLDL_DELEGRET)) == 0) {
2906
clearok = 1;
2907
LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) {
2908
op = LIST_FIRST(&owp->nfsow_open);
2909
if (op != NULL) {
2910
clearok = 0;
2911
break;
2912
}
2913
}
2914
if (clearok) {
2915
LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
2916
if (!LIST_EMPTY(&lp->nfsl_lock)) {
2917
clearok = 0;
2918
break;
2919
}
2920
}
2921
}
2922
if (clearok) {
2923
TAILQ_REMOVE(&clp->nfsc_deleg, dp, nfsdl_list);
2924
LIST_REMOVE(dp, nfsdl_hash);
2925
TAILQ_INSERT_HEAD(&dh, dp, nfsdl_list);
2926
clp->nfsc_delegcnt--;
2927
nfsstatsv1.cldelegates--;
2928
}
2929
}
2930
dp = ndp;
2931
}
2932
if (igotlock)
2933
nfsv4_unlock(&clp->nfsc_lock, 0);
2934
2935
/*
2936
* Do the recall on any layouts. To avoid trouble, always
2937
* come back up here after having slept.
2938
*/
2939
TAILQ_INIT(&rlh);
2940
tryagain2:
2941
TAILQ_FOREACH_SAFE(lyp, &clp->nfsc_layout, nfsly_list, nlyp) {
2942
if ((lyp->nfsly_flags & NFSLY_RECALL) != 0) {
2943
/*
2944
* Wait for outstanding I/O ops to be done.
2945
*/
2946
if (lyp->nfsly_lock.nfslock_usecnt > 0 ||
2947
(lyp->nfsly_lock.nfslock_lock &
2948
NFSV4LOCK_LOCK) != 0) {
2949
lyp->nfsly_lock.nfslock_lock |=
2950
NFSV4LOCK_WANTED;
2951
msleep(&lyp->nfsly_lock.nfslock_lock,
2952
NFSCLSTATEMUTEXPTR, PVFS, "nfslyp",
2953
5 * hz);
2954
if (NFSCL_FORCEDISM(mp))
2955
goto terminate;
2956
goto tryagain2;
2957
}
2958
/* Move the layout to the recall list. */
2959
TAILQ_REMOVE(&clp->nfsc_layout, lyp,
2960
nfsly_list);
2961
LIST_REMOVE(lyp, nfsly_hash);
2962
TAILQ_INSERT_HEAD(&rlh, lyp, nfsly_list);
2963
2964
/* Handle any layout commits. */
2965
if (!NFSHASNOLAYOUTCOMMIT(clp->nfsc_nmp) &&
2966
(lyp->nfsly_flags & NFSLY_WRITTEN) != 0) {
2967
lyp->nfsly_flags &= ~NFSLY_WRITTEN;
2968
NFSUNLOCKCLSTATE();
2969
NFSCL_DEBUG(3, "do layoutcommit\n");
2970
nfscl_dolayoutcommit(clp->nfsc_nmp, lyp,
2971
cred, p);
2972
NFSLOCKCLSTATE();
2973
goto tryagain2;
2974
}
2975
}
2976
}
2977
2978
/* Now, look for stale layouts. */
2979
lyp = TAILQ_LAST(&clp->nfsc_layout, nfscllayouthead);
2980
while (lyp != NULL) {
2981
nlyp = TAILQ_PREV(lyp, nfscllayouthead, nfsly_list);
2982
if ((lyp->nfsly_timestamp < NFSD_MONOSEC ||
2983
clp->nfsc_layoutcnt > clp->nfsc_layouthighwater) &&
2984
(lyp->nfsly_flags & (NFSLY_RECALL |
2985
NFSLY_RETONCLOSE)) == 0 &&
2986
lyp->nfsly_lock.nfslock_usecnt == 0 &&
2987
lyp->nfsly_lock.nfslock_lock == 0) {
2988
NFSCL_DEBUG(4, "ret stale lay=%d\n",
2989
clp->nfsc_layoutcnt);
2990
recallp = malloc(sizeof(*recallp),
2991
M_NFSLAYRECALL, M_NOWAIT);
2992
if (recallp == NULL)
2993
break;
2994
(void)nfscl_layoutrecall(NFSLAYOUTRETURN_FILE,
2995
lyp, NFSLAYOUTIOMODE_ANY, 0, UINT64_MAX,
2996
lyp->nfsly_stateid.seqid, 0, 0, NULL,
2997
recallp);
2998
}
2999
lyp = nlyp;
3000
}
3001
3002
/*
3003
* Free up any unreferenced device info structures.
3004
*/
3005
LIST_FOREACH_SAFE(dip, &clp->nfsc_devinfo, nfsdi_list, ndip) {
3006
if (dip->nfsdi_layoutrefs == 0 &&
3007
dip->nfsdi_refcnt == 0) {
3008
NFSCL_DEBUG(4, "freeing devinfo\n");
3009
LIST_REMOVE(dip, nfsdi_list);
3010
nfscl_freedevinfo(dip);
3011
}
3012
}
3013
NFSUNLOCKCLSTATE();
3014
3015
/* Do layout return(s), as required. */
3016
TAILQ_FOREACH_SAFE(lyp, &rlh, nfsly_list, nlyp) {
3017
TAILQ_REMOVE(&rlh, lyp, nfsly_list);
3018
NFSCL_DEBUG(4, "ret layout\n");
3019
nfscl_layoutreturn(clp->nfsc_nmp, lyp, cred, p);
3020
if ((lyp->nfsly_flags & NFSLY_RETONCLOSE) != 0) {
3021
NFSLOCKCLSTATE();
3022
lyp->nfsly_flags |= NFSLY_RETURNED;
3023
wakeup(lyp);
3024
NFSUNLOCKCLSTATE();
3025
} else
3026
nfscl_freelayout(lyp);
3027
}
3028
3029
/*
3030
* Delegreturn any delegations cleaned out or recalled.
3031
*/
3032
TAILQ_FOREACH_SAFE(dp, &dh, nfsdl_list, ndp) {
3033
newnfs_copycred(&dp->nfsdl_cred, cred);
3034
(void) nfscl_trydelegreturn(dp, cred, clp->nfsc_nmp, p);
3035
TAILQ_REMOVE(&dh, dp, nfsdl_list);
3036
free(dp, M_NFSCLDELEG);
3037
}
3038
3039
SLIST_INIT(&lfh);
3040
/*
3041
* Call nfscl_cleanupkext() once per second to check for
3042
* open/lock owners where the process has exited.
3043
*/
3044
mytime = NFSD_MONOSEC;
3045
if (prevsec != mytime) {
3046
prevsec = mytime;
3047
nfscl_cleanupkext(clp, &lfh);
3048
}
3049
3050
/*
3051
* Do a ReleaseLockOwner for all lock owners where the
3052
* associated process no longer exists, as found by
3053
* nfscl_cleanupkext().
3054
*/
3055
newnfs_setroot(cred);
3056
SLIST_FOREACH_SAFE(lfhp, &lfh, nfslfh_list, nlfhp) {
3057
LIST_FOREACH_SAFE(lp, &lfhp->nfslfh_lock, nfsl_list,
3058
nlp) {
3059
(void)nfsrpc_rellockown(clp->nfsc_nmp, lp,
3060
lfhp->nfslfh_fh, lfhp->nfslfh_len, cred,
3061
p);
3062
nfscl_freelockowner(lp, 0);
3063
}
3064
free(lfhp, M_TEMP);
3065
}
3066
SLIST_INIT(&lfh);
3067
3068
NFSLOCKCLSTATE();
3069
if ((clp->nfsc_flags & NFSCLFLAGS_RECOVER) == 0)
3070
(void)mtx_sleep(clp, NFSCLSTATEMUTEXPTR, PWAIT, "nfscl",
3071
hz);
3072
terminate:
3073
if (clp->nfsc_flags & NFSCLFLAGS_UMOUNT) {
3074
clp->nfsc_flags &= ~NFSCLFLAGS_HASTHREAD;
3075
NFSUNLOCKCLSTATE();
3076
NFSFREECRED(cred);
3077
wakeup((caddr_t)clp);
3078
return;
3079
}
3080
NFSUNLOCKCLSTATE();
3081
}
3082
}
3083
3084
/*
3085
* Initiate state recovery. Called when NFSERR_STALECLIENTID,
3086
* NFSERR_STALESTATEID or NFSERR_BADSESSION is received.
3087
*/
3088
void
3089
nfscl_initiate_recovery(struct nfsclclient *clp)
3090
{
3091
3092
if (clp == NULL)
3093
return;
3094
NFSLOCKCLSTATE();
3095
clp->nfsc_flags |= NFSCLFLAGS_RECOVER;
3096
NFSUNLOCKCLSTATE();
3097
wakeup((caddr_t)clp);
3098
}
3099
3100
/*
3101
* Dump out the state stuff for debugging.
3102
*/
3103
void
3104
nfscl_dumpstate(struct nfsmount *nmp, int openowner, int opens,
3105
int lockowner, int locks)
3106
{
3107
struct nfsclclient *clp;
3108
struct nfsclowner *owp;
3109
struct nfsclopen *op;
3110
struct nfscllockowner *lp;
3111
struct nfscllock *lop;
3112
struct nfscldeleg *dp;
3113
3114
clp = nmp->nm_clp;
3115
if (clp == NULL) {
3116
printf("nfscl dumpstate NULL clp\n");
3117
return;
3118
}
3119
NFSLOCKCLSTATE();
3120
TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) {
3121
LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) {
3122
if (openowner && !LIST_EMPTY(&owp->nfsow_open))
3123
printf("owner=0x%x 0x%x 0x%x 0x%x seqid=%d\n",
3124
owp->nfsow_owner[0], owp->nfsow_owner[1],
3125
owp->nfsow_owner[2], owp->nfsow_owner[3],
3126
owp->nfsow_seqid);
3127
LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
3128
if (opens)
3129
printf("open st=0x%x 0x%x 0x%x cnt=%d fh12=0x%x\n",
3130
op->nfso_stateid.other[0], op->nfso_stateid.other[1],
3131
op->nfso_stateid.other[2], op->nfso_opencnt,
3132
op->nfso_fh[12]);
3133
LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
3134
if (lockowner)
3135
printf("lckown=0x%x 0x%x 0x%x 0x%x seqid=%d st=0x%x 0x%x 0x%x\n",
3136
lp->nfsl_owner[0], lp->nfsl_owner[1],
3137
lp->nfsl_owner[2], lp->nfsl_owner[3],
3138
lp->nfsl_seqid,
3139
lp->nfsl_stateid.other[0], lp->nfsl_stateid.other[1],
3140
lp->nfsl_stateid.other[2]);
3141
LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) {
3142
if (locks)
3143
#ifdef __FreeBSD__
3144
printf("lck typ=%d fst=%ju end=%ju\n",
3145
lop->nfslo_type, (intmax_t)lop->nfslo_first,
3146
(intmax_t)lop->nfslo_end);
3147
#else
3148
printf("lck typ=%d fst=%qd end=%qd\n",
3149
lop->nfslo_type, lop->nfslo_first,
3150
lop->nfslo_end);
3151
#endif
3152
}
3153
}
3154
}
3155
}
3156
}
3157
LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
3158
if (openowner && !LIST_EMPTY(&owp->nfsow_open))
3159
printf("owner=0x%x 0x%x 0x%x 0x%x seqid=%d\n",
3160
owp->nfsow_owner[0], owp->nfsow_owner[1],
3161
owp->nfsow_owner[2], owp->nfsow_owner[3],
3162
owp->nfsow_seqid);
3163
LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
3164
if (opens)
3165
printf("open st=0x%x 0x%x 0x%x cnt=%d fh12=0x%x\n",
3166
op->nfso_stateid.other[0], op->nfso_stateid.other[1],
3167
op->nfso_stateid.other[2], op->nfso_opencnt,
3168
op->nfso_fh[12]);
3169
LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
3170
if (lockowner)
3171
printf("lckown=0x%x 0x%x 0x%x 0x%x seqid=%d st=0x%x 0x%x 0x%x\n",
3172
lp->nfsl_owner[0], lp->nfsl_owner[1],
3173
lp->nfsl_owner[2], lp->nfsl_owner[3],
3174
lp->nfsl_seqid,
3175
lp->nfsl_stateid.other[0], lp->nfsl_stateid.other[1],
3176
lp->nfsl_stateid.other[2]);
3177
LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) {
3178
if (locks)
3179
#ifdef __FreeBSD__
3180
printf("lck typ=%d fst=%ju end=%ju\n",
3181
lop->nfslo_type, (intmax_t)lop->nfslo_first,
3182
(intmax_t)lop->nfslo_end);
3183
#else
3184
printf("lck typ=%d fst=%qd end=%qd\n",
3185
lop->nfslo_type, lop->nfslo_first,
3186
lop->nfslo_end);
3187
#endif
3188
}
3189
}
3190
}
3191
}
3192
NFSUNLOCKCLSTATE();
3193
}
3194
3195
/*
3196
* Check for duplicate open owners and opens.
3197
* (Only used as a diagnostic aid.)
3198
*/
3199
void
3200
nfscl_dupopen(vnode_t vp, int dupopens)
3201
{
3202
struct nfsclclient *clp;
3203
struct nfsclowner *owp, *owp2;
3204
struct nfsclopen *op, *op2;
3205
struct nfsfh *nfhp;
3206
3207
clp = VFSTONFS(vp->v_mount)->nm_clp;
3208
if (clp == NULL) {
3209
printf("nfscl dupopen NULL clp\n");
3210
return;
3211
}
3212
nfhp = VTONFS(vp)->n_fhp;
3213
NFSLOCKCLSTATE();
3214
3215
/*
3216
* First, search for duplicate owners.
3217
* These should never happen!
3218
*/
3219
LIST_FOREACH(owp2, &clp->nfsc_owner, nfsow_list) {
3220
LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
3221
if (owp != owp2 &&
3222
!NFSBCMP(owp->nfsow_owner, owp2->nfsow_owner,
3223
NFSV4CL_LOCKNAMELEN)) {
3224
NFSUNLOCKCLSTATE();
3225
printf("DUP OWNER\n");
3226
nfscl_dumpstate(VFSTONFS(vp->v_mount), 1, 1, 0, 0);
3227
return;
3228
}
3229
}
3230
}
3231
3232
/*
3233
* Now, search for duplicate stateids.
3234
* These shouldn't happen, either.
3235
*/
3236
LIST_FOREACH(owp2, &clp->nfsc_owner, nfsow_list) {
3237
LIST_FOREACH(op2, &owp2->nfsow_open, nfso_list) {
3238
LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
3239
LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
3240
if (op != op2 &&
3241
(op->nfso_stateid.other[0] != 0 ||
3242
op->nfso_stateid.other[1] != 0 ||
3243
op->nfso_stateid.other[2] != 0) &&
3244
op->nfso_stateid.other[0] == op2->nfso_stateid.other[0] &&
3245
op->nfso_stateid.other[1] == op2->nfso_stateid.other[1] &&
3246
op->nfso_stateid.other[2] == op2->nfso_stateid.other[2]) {
3247
NFSUNLOCKCLSTATE();
3248
printf("DUP STATEID\n");
3249
nfscl_dumpstate(VFSTONFS(vp->v_mount), 1, 1, 0, 0);
3250
return;
3251
}
3252
}
3253
}
3254
}
3255
}
3256
3257
/*
3258
* Now search for duplicate opens.
3259
* Duplicate opens for the same owner
3260
* should never occur. Other duplicates are
3261
* possible and are checked for if "dupopens"
3262
* is true.
3263
*/
3264
LIST_FOREACH(owp2, &clp->nfsc_owner, nfsow_list) {
3265
LIST_FOREACH(op2, &owp2->nfsow_open, nfso_list) {
3266
if (nfhp->nfh_len == op2->nfso_fhlen &&
3267
!NFSBCMP(nfhp->nfh_fh, op2->nfso_fh, nfhp->nfh_len)) {
3268
LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
3269
LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
3270
if (op != op2 && nfhp->nfh_len == op->nfso_fhlen &&
3271
!NFSBCMP(nfhp->nfh_fh, op->nfso_fh, nfhp->nfh_len) &&
3272
(!NFSBCMP(op->nfso_own->nfsow_owner,
3273
op2->nfso_own->nfsow_owner, NFSV4CL_LOCKNAMELEN) ||
3274
dupopens)) {
3275
if (!NFSBCMP(op->nfso_own->nfsow_owner,
3276
op2->nfso_own->nfsow_owner, NFSV4CL_LOCKNAMELEN)) {
3277
NFSUNLOCKCLSTATE();
3278
printf("BADDUP OPEN\n");
3279
} else {
3280
NFSUNLOCKCLSTATE();
3281
printf("DUP OPEN\n");
3282
}
3283
nfscl_dumpstate(VFSTONFS(vp->v_mount), 1, 1, 0,
3284
0);
3285
return;
3286
}
3287
}
3288
}
3289
}
3290
}
3291
}
3292
NFSUNLOCKCLSTATE();
3293
}
3294
3295
/*
3296
* During close, find an open that needs to be dereferenced and
3297
* dereference it. If there are no more opens for this file,
3298
* log a message to that effect.
3299
* Opens aren't actually Close'd until VOP_INACTIVE() is performed
3300
* on the file's vnode.
3301
* This is the safe way, since it is difficult to identify
3302
* which open the close is for and I/O can be performed after the
3303
* close(2) system call when a file is mmap'd.
3304
* If it returns 0 for success, there will be a referenced
3305
* clp returned via clpp.
3306
*/
3307
int
3308
nfscl_getclose(vnode_t vp, struct nfsclclient **clpp)
3309
{
3310
struct nfsclclient *clp;
3311
struct nfsclowner *owp;
3312
struct nfsclopen *op;
3313
struct nfscldeleg *dp;
3314
struct nfsfh *nfhp;
3315
int error, notdecr;
3316
3317
error = nfscl_getcl(vp->v_mount, NULL, NULL, false, true, &clp);
3318
if (error)
3319
return (error);
3320
*clpp = clp;
3321
3322
nfhp = VTONFS(vp)->n_fhp;
3323
notdecr = 1;
3324
NFSLOCKCLSTATE();
3325
/*
3326
* First, look for one under a delegation that was locally issued
3327
* and just decrement the opencnt for it. Since all my Opens against
3328
* the server are DENY_NONE, I don't see a problem with hanging
3329
* onto them. (It is much easier to use one of the extant Opens
3330
* that I already have on the server when a Delegation is recalled
3331
* than to do fresh Opens.) Someday, I might need to rethink this, but.
3332
*/
3333
dp = nfscl_finddeleg(clp, nfhp->nfh_fh, nfhp->nfh_len);
3334
if (dp != NULL) {
3335
LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) {
3336
op = LIST_FIRST(&owp->nfsow_open);
3337
if (op != NULL) {
3338
/*
3339
* Since a delegation is for a file, there
3340
* should never be more than one open for
3341
* each openowner.
3342
*/
3343
if (LIST_NEXT(op, nfso_list) != NULL)
3344
panic("nfscdeleg opens");
3345
if (notdecr && op->nfso_opencnt > 0) {
3346
notdecr = 0;
3347
op->nfso_opencnt--;
3348
break;
3349
}
3350
}
3351
}
3352
}
3353
3354
/* Now process the opens against the server. */
3355
LIST_FOREACH(op, NFSCLOPENHASH(clp, nfhp->nfh_fh, nfhp->nfh_len),
3356
nfso_hash) {
3357
if (op->nfso_fhlen == nfhp->nfh_len &&
3358
!NFSBCMP(op->nfso_fh, nfhp->nfh_fh,
3359
nfhp->nfh_len)) {
3360
/* Found an open, decrement cnt if possible */
3361
if (notdecr && op->nfso_opencnt > 0) {
3362
notdecr = 0;
3363
op->nfso_opencnt--;
3364
}
3365
/*
3366
* There are more opens, so just return.
3367
*/
3368
if (op->nfso_opencnt > 0) {
3369
NFSUNLOCKCLSTATE();
3370
return (0);
3371
}
3372
}
3373
}
3374
NFSUNLOCKCLSTATE();
3375
if (notdecr)
3376
printf("nfscl: never fnd open\n");
3377
return (0);
3378
}
3379
3380
int
3381
nfscl_doclose(vnode_t vp, struct nfsclclient **clpp, NFSPROC_T *p)
3382
{
3383
struct nfsclclient *clp;
3384
struct nfsmount *nmp;
3385
struct nfsclowner *owp, *nowp;
3386
struct nfsclopen *op, *nop;
3387
struct nfsclopenhead delayed;
3388
struct nfscldeleg *dp;
3389
struct nfsfh *nfhp;
3390
struct nfsclrecalllayout *recallp;
3391
struct nfscllayout *lyp;
3392
int error;
3393
3394
error = nfscl_getcl(vp->v_mount, NULL, NULL, false, true, &clp);
3395
if (error)
3396
return (error);
3397
*clpp = clp;
3398
3399
nmp = VFSTONFS(vp->v_mount);
3400
nfhp = VTONFS(vp)->n_fhp;
3401
recallp = malloc(sizeof(*recallp), M_NFSLAYRECALL, M_WAITOK);
3402
NFSLOCKCLSTATE();
3403
/*
3404
* First get rid of the local Open structures, which should be no
3405
* longer in use.
3406
*/
3407
dp = nfscl_finddeleg(clp, nfhp->nfh_fh, nfhp->nfh_len);
3408
if (dp != NULL) {
3409
LIST_FOREACH_SAFE(owp, &dp->nfsdl_owner, nfsow_list, nowp) {
3410
op = LIST_FIRST(&owp->nfsow_open);
3411
if (op != NULL) {
3412
KASSERT((op->nfso_opencnt == 0),
3413
("nfscl: bad open cnt on deleg"));
3414
nfscl_freeopen(op, 1, true);
3415
}
3416
nfscl_freeopenowner(owp, 1);
3417
}
3418
}
3419
3420
/* Return any layouts marked return on close. */
3421
nfscl_retoncloselayout(vp, clp, nfhp->nfh_fh, nfhp->nfh_len, &recallp,
3422
&lyp);
3423
3424
/* Now process the opens against the server. */
3425
LIST_INIT(&delayed);
3426
lookformore:
3427
LIST_FOREACH(op, NFSCLOPENHASH(clp, nfhp->nfh_fh, nfhp->nfh_len),
3428
nfso_hash) {
3429
if (op->nfso_fhlen == nfhp->nfh_len &&
3430
!NFSBCMP(op->nfso_fh, nfhp->nfh_fh,
3431
nfhp->nfh_len)) {
3432
/* Found an open, close it. */
3433
#ifdef DIAGNOSTIC
3434
KASSERT((op->nfso_opencnt == 0),
3435
("nfscl: bad open cnt on server (%d)",
3436
op->nfso_opencnt));
3437
#endif
3438
NFSUNLOCKCLSTATE();
3439
if (NFSHASNFSV4N(nmp))
3440
error = nfsrpc_doclose(nmp, op, p, false, true);
3441
else
3442
error = nfsrpc_doclose(nmp, op, p, true, true);
3443
NFSLOCKCLSTATE();
3444
if (error == NFSERR_DELAY) {
3445
nfscl_unlinkopen(op);
3446
op->nfso_own = NULL;
3447
LIST_INSERT_HEAD(&delayed, op, nfso_list);
3448
}
3449
goto lookformore;
3450
}
3451
}
3452
nfscl_clrelease(clp);
3453
3454
/* Now, wait for any layout that is returned upon close. */
3455
if (lyp != NULL) {
3456
while ((lyp->nfsly_flags & NFSLY_RETURNED) == 0) {
3457
if (NFSCL_FORCEDISM(nmp->nm_mountp)) {
3458
lyp = NULL;
3459
break;
3460
}
3461
msleep(lyp, NFSCLSTATEMUTEXPTR, PZERO, "nfslroc", hz);
3462
}
3463
if (lyp != NULL)
3464
nfscl_freelayout(lyp);
3465
}
3466
3467
NFSUNLOCKCLSTATE();
3468
/*
3469
* recallp has been set NULL by nfscl_retoncloselayout() if it was
3470
* used by the function, but calling free() with a NULL pointer is ok.
3471
*/
3472
free(recallp, M_NFSLAYRECALL);
3473
3474
/* Now, loop retrying the delayed closes. */
3475
LIST_FOREACH_SAFE(op, &delayed, nfso_list, nop) {
3476
nfsrpc_doclose(nmp, op, p, true, false);
3477
LIST_REMOVE(op, nfso_list);
3478
nfscl_freeopen(op, 0, false);
3479
}
3480
return (0);
3481
}
3482
3483
/*
3484
* Return all delegations on this client.
3485
* (Must be called with client sleep lock.)
3486
*/
3487
static void
3488
nfscl_delegreturnall(struct nfsclclient *clp, NFSPROC_T *p,
3489
struct nfscldeleghead *dhp)
3490
{
3491
struct nfscldeleg *dp, *ndp;
3492
struct ucred *cred;
3493
3494
cred = newnfs_getcred();
3495
TAILQ_FOREACH_SAFE(dp, &clp->nfsc_deleg, nfsdl_list, ndp) {
3496
nfscl_cleandeleg(dp);
3497
(void) nfscl_trydelegreturn(dp, cred, clp->nfsc_nmp, p);
3498
if (dhp != NULL) {
3499
nfscl_freedeleg(&clp->nfsc_deleg, dp, false);
3500
TAILQ_INSERT_HEAD(dhp, dp, nfsdl_list);
3501
} else
3502
nfscl_freedeleg(&clp->nfsc_deleg, dp, true);
3503
}
3504
NFSFREECRED(cred);
3505
}
3506
3507
/*
3508
* Return any delegation for this vp.
3509
*/
3510
void
3511
nfscl_delegreturnvp(struct vnode *vp, bool retdeleg, NFSPROC_T *p)
3512
{
3513
struct nfsclclient *clp;
3514
struct nfscldeleg *dp;
3515
struct ucred *cred;
3516
struct nfsnode *np;
3517
struct nfsmount *nmp;
3518
3519
nmp = VFSTONFS(vp->v_mount);
3520
NFSLOCKMNT(nmp);
3521
if ((nmp->nm_privflag & NFSMNTP_DELEGISSUED) == 0) {
3522
NFSUNLOCKMNT(nmp);
3523
return;
3524
}
3525
NFSUNLOCKMNT(nmp);
3526
np = VTONFS(vp);
3527
cred = newnfs_getcred();
3528
dp = NULL;
3529
NFSLOCKCLSTATE();
3530
clp = nmp->nm_clp;
3531
if (clp != NULL)
3532
dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh,
3533
np->n_fhp->nfh_len);
3534
if (dp != NULL &&
3535
(dp->nfsdl_flags & (NFSCLDL_RECALL | NFSCLDL_DELEGRET)) == 0) {
3536
nfscl_cleandeleg(dp);
3537
nfscl_freedeleg(&clp->nfsc_deleg, dp, false);
3538
NFSUNLOCKCLSTATE();
3539
if (retdeleg) {
3540
newnfs_copycred(&dp->nfsdl_cred, cred);
3541
nfscl_trydelegreturn(dp, cred, clp->nfsc_nmp, p);
3542
}
3543
free(dp, M_NFSCLDELEG);
3544
} else
3545
NFSUNLOCKCLSTATE();
3546
NFSFREECRED(cred);
3547
}
3548
3549
/*
3550
* Do a callback RPC.
3551
*/
3552
void
3553
nfscl_docb(struct nfsrv_descript *nd, NFSPROC_T *p)
3554
{
3555
int clist, gotseq_ok, i, j, k, op, rcalls;
3556
u_int32_t *tl;
3557
struct nfsclclient *clp;
3558
struct nfscldeleg *dp = NULL;
3559
int numops, taglen = -1, error = 0, trunc __unused;
3560
u_int32_t minorvers = 0, retops = 0, *retopsp = NULL, *repp, cbident;
3561
u_char tag[NFSV4_SMALLSTR + 1], *tagstr;
3562
vnode_t vp = NULL;
3563
struct nfsnode *np;
3564
struct vattr va;
3565
struct nfsfh *nfhp;
3566
mount_t mp;
3567
nfsattrbit_t attrbits, rattrbits;
3568
nfsv4stateid_t stateid;
3569
uint32_t seqid, slotid = 0, highslot, cachethis __unused;
3570
uint8_t sessionid[NFSX_V4SESSIONID];
3571
struct mbuf *rep;
3572
struct nfscllayout *lyp;
3573
uint64_t filesid[2], len, off;
3574
int changed, gotone, laytype, recalltype;
3575
uint32_t iomode;
3576
struct nfsclrecalllayout *recallp = NULL;
3577
struct nfsclsession *tsep;
3578
3579
gotseq_ok = 0;
3580
nfsrvd_rephead(nd);
3581
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3582
taglen = fxdr_unsigned(int, *tl);
3583
if (taglen < 0 || taglen > NFSV4_OPAQUELIMIT) {
3584
error = EBADRPC;
3585
taglen = -1;
3586
goto nfsmout;
3587
}
3588
if (taglen <= NFSV4_SMALLSTR)
3589
tagstr = tag;
3590
else
3591
tagstr = malloc(taglen + 1, M_TEMP, M_WAITOK);
3592
error = nfsrv_mtostr(nd, tagstr, taglen);
3593
if (error) {
3594
if (taglen > NFSV4_SMALLSTR)
3595
free(tagstr, M_TEMP);
3596
taglen = -1;
3597
goto nfsmout;
3598
}
3599
(void) nfsm_strtom(nd, tag, taglen);
3600
if (taglen > NFSV4_SMALLSTR) {
3601
free(tagstr, M_TEMP);
3602
}
3603
NFSM_BUILD(retopsp, u_int32_t *, NFSX_UNSIGNED);
3604
NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
3605
minorvers = fxdr_unsigned(u_int32_t, *tl++);
3606
if (minorvers != NFSV4_MINORVERSION &&
3607
minorvers != NFSV41_MINORVERSION &&
3608
minorvers != NFSV42_MINORVERSION)
3609
nd->nd_repstat = NFSERR_MINORVERMISMATCH;
3610
cbident = fxdr_unsigned(u_int32_t, *tl++);
3611
if (nd->nd_repstat)
3612
numops = 0;
3613
else
3614
numops = fxdr_unsigned(int, *tl);
3615
/*
3616
* Loop around doing the sub ops.
3617
*/
3618
for (i = 0; i < numops; i++) {
3619
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3620
NFSM_BUILD(repp, u_int32_t *, 2 * NFSX_UNSIGNED);
3621
*repp++ = *tl;
3622
op = fxdr_unsigned(int, *tl);
3623
nd->nd_procnum = op;
3624
if (i == 0 && op != NFSV4OP_CBSEQUENCE && minorvers !=
3625
NFSV4_MINORVERSION) {
3626
nd->nd_repstat = NFSERR_OPNOTINSESS;
3627
*repp = nfscl_errmap(nd, minorvers);
3628
retops++;
3629
break;
3630
}
3631
if (op < NFSV4OP_CBGETATTR ||
3632
(op > NFSV4OP_CBRECALL && minorvers == NFSV4_MINORVERSION) ||
3633
(op > NFSV4OP_CBNOTIFYDEVID &&
3634
minorvers == NFSV41_MINORVERSION) ||
3635
(op > NFSV4OP_CBOFFLOAD &&
3636
minorvers == NFSV42_MINORVERSION)) {
3637
nd->nd_repstat = NFSERR_OPILLEGAL;
3638
*repp = nfscl_errmap(nd, minorvers);
3639
retops++;
3640
break;
3641
}
3642
if (op < NFSV42_CBNOPS)
3643
nfsstatsv1.cbrpccnt[nd->nd_procnum]++;
3644
switch (op) {
3645
case NFSV4OP_CBGETATTR:
3646
NFSCL_DEBUG(4, "cbgetattr\n");
3647
mp = NULL;
3648
vp = NULL;
3649
error = nfsm_getfh(nd, &nfhp);
3650
if (!error)
3651
error = nfsrv_getattrbits(nd, &attrbits,
3652
NULL, NULL);
3653
if (!error) {
3654
mp = nfscl_getmnt(minorvers, sessionid, cbident,
3655
&clp);
3656
if (mp == NULL)
3657
error = NFSERR_SERVERFAULT;
3658
}
3659
if (!error) {
3660
error = nfscl_ngetreopen(mp, nfhp->nfh_fh,
3661
nfhp->nfh_len, p, &np);
3662
if (!error)
3663
vp = NFSTOV(np);
3664
}
3665
if (!error) {
3666
NFSZERO_ATTRBIT(&rattrbits);
3667
NFSLOCKCLSTATE();
3668
dp = nfscl_finddeleg(clp, nfhp->nfh_fh,
3669
nfhp->nfh_len);
3670
if (dp != NULL) {
3671
if (NFSISSET_ATTRBIT(&attrbits,
3672
NFSATTRBIT_SIZE)) {
3673
if (vp != NULL)
3674
va.va_size = np->n_size;
3675
else
3676
va.va_size =
3677
dp->nfsdl_size;
3678
NFSSETBIT_ATTRBIT(&rattrbits,
3679
NFSATTRBIT_SIZE);
3680
}
3681
if (NFSISSET_ATTRBIT(&attrbits,
3682
NFSATTRBIT_CHANGE)) {
3683
va.va_filerev =
3684
dp->nfsdl_change;
3685
if (vp == NULL ||
3686
(np->n_flag & NDELEGMOD))
3687
va.va_filerev++;
3688
NFSSETBIT_ATTRBIT(&rattrbits,
3689
NFSATTRBIT_CHANGE);
3690
}
3691
} else
3692
error = NFSERR_SERVERFAULT;
3693
NFSUNLOCKCLSTATE();
3694
}
3695
if (vp != NULL)
3696
vrele(vp);
3697
if (mp != NULL)
3698
vfs_unbusy(mp);
3699
if (nfhp != NULL)
3700
free(nfhp, M_NFSFH);
3701
if (!error)
3702
(void) nfsv4_fillattr(nd, NULL, NULL, NULL, &va,
3703
NULL, 0, &rattrbits, NULL, p, 0, 0, 0, 0,
3704
(uint64_t)0, NULL, false, false, false, 0);
3705
break;
3706
case NFSV4OP_CBRECALL:
3707
NFSCL_DEBUG(4, "cbrecall\n");
3708
NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID +
3709
NFSX_UNSIGNED);
3710
stateid.seqid = *tl++;
3711
NFSBCOPY((caddr_t)tl, (caddr_t)stateid.other,
3712
NFSX_STATEIDOTHER);
3713
tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
3714
trunc = fxdr_unsigned(int, *tl);
3715
error = nfsm_getfh(nd, &nfhp);
3716
if (!error) {
3717
NFSLOCKCLSTATE();
3718
if (minorvers == NFSV4_MINORVERSION)
3719
clp = nfscl_getclnt(cbident);
3720
else
3721
clp = nfscl_getclntsess(sessionid);
3722
if (clp != NULL)
3723
nfscl_startdelegrecall(clp, nfhp);
3724
else
3725
error = NFSERR_SERVERFAULT;
3726
NFSUNLOCKCLSTATE();
3727
}
3728
if (nfhp != NULL)
3729
free(nfhp, M_NFSFH);
3730
break;
3731
case NFSV4OP_CBLAYOUTRECALL:
3732
NFSCL_DEBUG(4, "cblayrec\n");
3733
nfhp = NULL;
3734
NFSM_DISSECT(tl, uint32_t *, 4 * NFSX_UNSIGNED);
3735
laytype = fxdr_unsigned(int, *tl++);
3736
iomode = fxdr_unsigned(uint32_t, *tl++);
3737
if (newnfs_true == *tl++)
3738
changed = 1;
3739
else
3740
changed = 0;
3741
recalltype = fxdr_unsigned(int, *tl);
3742
NFSCL_DEBUG(4, "layt=%d iom=%d ch=%d rectyp=%d\n",
3743
laytype, iomode, changed, recalltype);
3744
recallp = malloc(sizeof(*recallp), M_NFSLAYRECALL,
3745
M_WAITOK);
3746
if (laytype != NFSLAYOUT_NFSV4_1_FILES &&
3747
laytype != NFSLAYOUT_FLEXFILE)
3748
error = NFSERR_NOMATCHLAYOUT;
3749
else if (recalltype == NFSLAYOUTRETURN_FILE) {
3750
error = nfsm_getfh(nd, &nfhp);
3751
NFSCL_DEBUG(4, "retfile getfh=%d\n", error);
3752
if (error != 0)
3753
goto nfsmout;
3754
NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_HYPER +
3755
NFSX_STATEID);
3756
off = fxdr_hyper(tl); tl += 2;
3757
len = fxdr_hyper(tl); tl += 2;
3758
stateid.seqid = fxdr_unsigned(uint32_t, *tl++);
3759
NFSBCOPY(tl, stateid.other, NFSX_STATEIDOTHER);
3760
if (minorvers == NFSV4_MINORVERSION)
3761
error = NFSERR_NOTSUPP;
3762
NFSCL_DEBUG(4, "off=%ju len=%ju sq=%u err=%d\n",
3763
(uintmax_t)off, (uintmax_t)len,
3764
stateid.seqid, error);
3765
if (error == 0) {
3766
NFSLOCKCLSTATE();
3767
clp = nfscl_getclntsess(sessionid);
3768
NFSCL_DEBUG(4, "cbly clp=%p\n", clp);
3769
if (clp != NULL) {
3770
lyp = nfscl_findlayout(clp,
3771
nfhp->nfh_fh,
3772
nfhp->nfh_len);
3773
NFSCL_DEBUG(4, "cblyp=%p\n",
3774
lyp);
3775
if (lyp != NULL &&
3776
(lyp->nfsly_flags &
3777
(NFSLY_FILES |
3778
NFSLY_FLEXFILE)) != 0 &&
3779
!NFSBCMP(stateid.other,
3780
lyp->nfsly_stateid.other,
3781
NFSX_STATEIDOTHER)) {
3782
error =
3783
nfscl_layoutrecall(
3784
recalltype,
3785
lyp, iomode, off,
3786
len, stateid.seqid,
3787
0, 0, NULL,
3788
recallp);
3789
if (error == 0 &&
3790
stateid.seqid >
3791
lyp->nfsly_stateid.seqid)
3792
lyp->nfsly_stateid.seqid =
3793
stateid.seqid;
3794
recallp = NULL;
3795
wakeup(clp);
3796
NFSCL_DEBUG(4,
3797
"aft layrcal=%d "
3798
"layseqid=%d\n",
3799
error,
3800
lyp->nfsly_stateid.seqid);
3801
} else
3802
error =
3803
NFSERR_NOMATCHLAYOUT;
3804
} else
3805
error = NFSERR_NOMATCHLAYOUT;
3806
NFSUNLOCKCLSTATE();
3807
}
3808
free(nfhp, M_NFSFH);
3809
} else if (recalltype == NFSLAYOUTRETURN_FSID) {
3810
NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_HYPER);
3811
filesid[0] = fxdr_hyper(tl); tl += 2;
3812
filesid[1] = fxdr_hyper(tl); tl += 2;
3813
gotone = 0;
3814
NFSLOCKCLSTATE();
3815
clp = nfscl_getclntsess(sessionid);
3816
if (clp != NULL) {
3817
TAILQ_FOREACH(lyp, &clp->nfsc_layout,
3818
nfsly_list) {
3819
if (lyp->nfsly_filesid[0] ==
3820
filesid[0] &&
3821
lyp->nfsly_filesid[1] ==
3822
filesid[1]) {
3823
error =
3824
nfscl_layoutrecall(
3825
recalltype,
3826
lyp, iomode, 0,
3827
UINT64_MAX,
3828
lyp->nfsly_stateid.seqid,
3829
0, 0, NULL,
3830
recallp);
3831
recallp = NULL;
3832
gotone = 1;
3833
}
3834
}
3835
if (gotone != 0)
3836
wakeup(clp);
3837
else
3838
error = NFSERR_NOMATCHLAYOUT;
3839
} else
3840
error = NFSERR_NOMATCHLAYOUT;
3841
NFSUNLOCKCLSTATE();
3842
} else if (recalltype == NFSLAYOUTRETURN_ALL) {
3843
gotone = 0;
3844
NFSLOCKCLSTATE();
3845
clp = nfscl_getclntsess(sessionid);
3846
if (clp != NULL) {
3847
TAILQ_FOREACH(lyp, &clp->nfsc_layout,
3848
nfsly_list) {
3849
error = nfscl_layoutrecall(
3850
recalltype, lyp, iomode, 0,
3851
UINT64_MAX,
3852
lyp->nfsly_stateid.seqid,
3853
0, 0, NULL, recallp);
3854
recallp = NULL;
3855
gotone = 1;
3856
}
3857
if (gotone != 0)
3858
wakeup(clp);
3859
else
3860
error = NFSERR_NOMATCHLAYOUT;
3861
} else
3862
error = NFSERR_NOMATCHLAYOUT;
3863
NFSUNLOCKCLSTATE();
3864
} else
3865
error = NFSERR_NOMATCHLAYOUT;
3866
if (recallp != NULL) {
3867
free(recallp, M_NFSLAYRECALL);
3868
recallp = NULL;
3869
}
3870
break;
3871
case NFSV4OP_CBSEQUENCE:
3872
if (i != 0) {
3873
error = NFSERR_SEQUENCEPOS;
3874
break;
3875
}
3876
NFSM_DISSECT(tl, uint32_t *, NFSX_V4SESSIONID +
3877
5 * NFSX_UNSIGNED);
3878
bcopy(tl, sessionid, NFSX_V4SESSIONID);
3879
tl += NFSX_V4SESSIONID / NFSX_UNSIGNED;
3880
seqid = fxdr_unsigned(uint32_t, *tl++);
3881
slotid = fxdr_unsigned(uint32_t, *tl++);
3882
highslot = fxdr_unsigned(uint32_t, *tl++);
3883
cachethis = *tl++;
3884
/* Throw away the referring call stuff. */
3885
clist = fxdr_unsigned(int, *tl);
3886
for (j = 0; j < clist; j++) {
3887
NFSM_DISSECT(tl, uint32_t *, NFSX_V4SESSIONID +
3888
NFSX_UNSIGNED);
3889
tl += NFSX_V4SESSIONID / NFSX_UNSIGNED;
3890
rcalls = fxdr_unsigned(int, *tl);
3891
for (k = 0; k < rcalls; k++) {
3892
NFSM_DISSECT(tl, uint32_t *,
3893
2 * NFSX_UNSIGNED);
3894
}
3895
}
3896
NFSLOCKCLSTATE();
3897
clp = nfscl_getclntsess(sessionid);
3898
if (clp == NULL)
3899
error = NFSERR_SERVERFAULT;
3900
if (error == 0) {
3901
tsep = nfsmnt_mdssession(clp->nfsc_nmp);
3902
error = nfsv4_seqsession(seqid, slotid,
3903
highslot, tsep->nfsess_cbslots, &rep,
3904
tsep->nfsess_backslots);
3905
}
3906
NFSUNLOCKCLSTATE();
3907
if (error == 0 || error == NFSERR_REPLYFROMCACHE) {
3908
gotseq_ok = 1;
3909
if (rep != NULL) {
3910
/*
3911
* Handle a reply for a retried
3912
* callback. The reply will be
3913
* re-inserted in the session cache
3914
* by the nfsv4_seqsess_cacherep() call
3915
* after out:
3916
*/
3917
KASSERT(error == NFSERR_REPLYFROMCACHE,
3918
("cbsequence: non-NULL rep"));
3919
NFSCL_DEBUG(4, "Got cbretry\n");
3920
m_freem(nd->nd_mreq);
3921
nd->nd_mreq = rep;
3922
rep = NULL;
3923
goto out;
3924
}
3925
NFSM_BUILD(tl, uint32_t *,
3926
NFSX_V4SESSIONID + 4 * NFSX_UNSIGNED);
3927
bcopy(sessionid, tl, NFSX_V4SESSIONID);
3928
tl += NFSX_V4SESSIONID / NFSX_UNSIGNED;
3929
*tl++ = txdr_unsigned(seqid);
3930
*tl++ = txdr_unsigned(slotid);
3931
*tl++ = txdr_unsigned(NFSV4_CBSLOTS - 1);
3932
*tl = txdr_unsigned(NFSV4_CBSLOTS - 1);
3933
}
3934
break;
3935
case NFSV4OP_CBRECALLSLOT:
3936
NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
3937
highslot = fxdr_unsigned(uint32_t, *tl);
3938
NFSLOCKCLSTATE();
3939
clp = nfscl_getclntsess(sessionid);
3940
if (clp == NULL)
3941
error = NFSERR_SERVERFAULT;
3942
if (error == 0) {
3943
tsep = nfsmnt_mdssession(clp->nfsc_nmp);
3944
mtx_lock(&tsep->nfsess_mtx);
3945
if ((highslot + 1) < tsep->nfsess_foreslots) {
3946
tsep->nfsess_foreslots = (highslot + 1);
3947
nfs_resetslots(tsep);
3948
}
3949
mtx_unlock(&tsep->nfsess_mtx);
3950
}
3951
NFSUNLOCKCLSTATE();
3952
break;
3953
case NFSV4OP_CBRECALLANY:
3954
NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
3955
i = fxdr_unsigned(int, *tl++);
3956
j = fxdr_unsigned(int, *tl);
3957
if (i < 0 || j != 1)
3958
error = NFSERR_BADXDR;
3959
if (error == 0) {
3960
NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
3961
j = fxdr_unsigned(int, *tl);
3962
if (i < 100)
3963
i = 100;
3964
else if (i > 100000)
3965
i = 100000;
3966
NFSLOCKCLSTATE();
3967
clp = nfscl_getclntsess(sessionid);
3968
if (clp == NULL)
3969
error = NFSERR_SERVERFAULT;
3970
if (((j & NFSRCA4_RDATA_DLG) != 0 ||
3971
(j & NFSRCA4_WDATA_DLG) != 0) &&
3972
error == 0 && i <
3973
clp->nfsc_deleghighwater)
3974
clp->nfsc_deleghighwater = i;
3975
if (error == 0 &&
3976
((!NFSHASFLEXFILE(clp->nfsc_nmp) &&
3977
(j & NFSRCA4_FILE_LAYOUT) != 0 &&
3978
i < clp->nfsc_layouthighwater) ||
3979
(NFSHASFLEXFILE(clp->nfsc_nmp) &&
3980
(j & (NFSRCA4_FF_LAYOUT_READ |
3981
NFSRCA4_FF_LAYOUT_RW)) != 0 &&
3982
i < clp->nfsc_layouthighwater)))
3983
clp->nfsc_layouthighwater = i;
3984
NFSUNLOCKCLSTATE();
3985
}
3986
break;
3987
case NFSV4OP_CBNOTIFY:
3988
case NFSV4OP_CBRECALLOBJAVAIL:
3989
case NFSV4OP_CBNOTIFYLOCK:
3990
/*
3991
* These callbacks are not necessarily optional,
3992
* so I think it is better to reply NFS_OK than
3993
* NFSERR_NOTSUPP.
3994
* All provide information for which the FreeBSD client
3995
* does not currently have a use.
3996
* I am not sure if any of these could be generated
3997
* by a NFSv4.1/4.2 server for this client?
3998
*/
3999
error = 0;
4000
NFSCL_DEBUG(1, "unsupp callback %d\n", op);
4001
break;
4002
case NFSV4OP_CBPUSHDELEG:
4003
error = NFSERR_REJECTDELEG;
4004
NFSCL_DEBUG(1, "unsupp callback %d\n", op);
4005
break;
4006
default:
4007
if (i == 0 && minorvers != NFSV4_MINORVERSION)
4008
error = NFSERR_OPNOTINSESS;
4009
else {
4010
NFSCL_DEBUG(1, "unsupp callback %d\n", op);
4011
error = NFSERR_NOTSUPP;
4012
}
4013
break;
4014
}
4015
if (error) {
4016
if (error == EBADRPC || error == NFSERR_BADXDR) {
4017
nd->nd_repstat = NFSERR_BADXDR;
4018
} else {
4019
nd->nd_repstat = error;
4020
}
4021
error = 0;
4022
}
4023
retops++;
4024
if (nd->nd_repstat) {
4025
*repp = nfscl_errmap(nd, minorvers);
4026
break;
4027
} else
4028
*repp = 0; /* NFS4_OK */
4029
}
4030
nfsmout:
4031
if (recallp != NULL)
4032
free(recallp, M_NFSLAYRECALL);
4033
if (error) {
4034
if (error == EBADRPC || error == NFSERR_BADXDR)
4035
nd->nd_repstat = NFSERR_BADXDR;
4036
else
4037
printf("nfsv4 comperr1=%d\n", error);
4038
}
4039
if (taglen == -1) {
4040
NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
4041
*tl++ = 0;
4042
*tl = 0;
4043
} else {
4044
*retopsp = txdr_unsigned(retops);
4045
}
4046
*nd->nd_errp = nfscl_errmap(nd, minorvers);
4047
out:
4048
if (gotseq_ok != 0) {
4049
rep = m_copym(nd->nd_mreq, 0, M_COPYALL, M_WAITOK);
4050
NFSLOCKCLSTATE();
4051
clp = nfscl_getclntsess(sessionid);
4052
if (clp != NULL) {
4053
tsep = nfsmnt_mdssession(clp->nfsc_nmp);
4054
nfsv4_seqsess_cacherep(slotid, tsep->nfsess_cbslots,
4055
NFSERR_OK, &rep);
4056
NFSUNLOCKCLSTATE();
4057
} else {
4058
NFSUNLOCKCLSTATE();
4059
m_freem(rep);
4060
}
4061
}
4062
}
4063
4064
/*
4065
* Generate the next cbident value. Basically just increment a static value
4066
* and then check that it isn't already in the list, if it has wrapped around.
4067
*/
4068
static u_int32_t
4069
nfscl_nextcbident(void)
4070
{
4071
struct nfsclclient *clp;
4072
int matched;
4073
static u_int32_t nextcbident = 0;
4074
static int haswrapped = 0;
4075
4076
nextcbident++;
4077
if (nextcbident == 0)
4078
haswrapped = 1;
4079
if (haswrapped) {
4080
/*
4081
* Search the clientid list for one already using this cbident.
4082
*/
4083
do {
4084
matched = 0;
4085
NFSLOCKCLSTATE();
4086
LIST_FOREACH(clp, &nfsclhead, nfsc_list) {
4087
if (clp->nfsc_cbident == nextcbident) {
4088
matched = 1;
4089
break;
4090
}
4091
}
4092
NFSUNLOCKCLSTATE();
4093
if (matched == 1)
4094
nextcbident++;
4095
} while (matched);
4096
}
4097
return (nextcbident);
4098
}
4099
4100
/*
4101
* Get the mount point related to a given cbident or session and busy it.
4102
*/
4103
static mount_t
4104
nfscl_getmnt(int minorvers, uint8_t *sessionid, u_int32_t cbident,
4105
struct nfsclclient **clpp)
4106
{
4107
struct nfsclclient *clp;
4108
mount_t mp;
4109
int error;
4110
struct nfsclsession *tsep;
4111
4112
*clpp = NULL;
4113
NFSLOCKCLSTATE();
4114
LIST_FOREACH(clp, &nfsclhead, nfsc_list) {
4115
tsep = nfsmnt_mdssession(clp->nfsc_nmp);
4116
if (minorvers == NFSV4_MINORVERSION) {
4117
if (clp->nfsc_cbident == cbident)
4118
break;
4119
} else if (!NFSBCMP(tsep->nfsess_sessionid, sessionid,
4120
NFSX_V4SESSIONID))
4121
break;
4122
}
4123
if (clp == NULL) {
4124
NFSUNLOCKCLSTATE();
4125
return (NULL);
4126
}
4127
mp = clp->nfsc_nmp->nm_mountp;
4128
vfs_ref(mp);
4129
NFSUNLOCKCLSTATE();
4130
error = vfs_busy(mp, 0);
4131
vfs_rel(mp);
4132
if (error != 0)
4133
return (NULL);
4134
*clpp = clp;
4135
return (mp);
4136
}
4137
4138
/*
4139
* Get the clientid pointer related to a given cbident.
4140
*/
4141
static struct nfsclclient *
4142
nfscl_getclnt(u_int32_t cbident)
4143
{
4144
struct nfsclclient *clp;
4145
4146
LIST_FOREACH(clp, &nfsclhead, nfsc_list)
4147
if (clp->nfsc_cbident == cbident)
4148
break;
4149
return (clp);
4150
}
4151
4152
/*
4153
* Get the clientid pointer related to a given sessionid.
4154
*/
4155
static struct nfsclclient *
4156
nfscl_getclntsess(uint8_t *sessionid)
4157
{
4158
struct nfsclclient *clp;
4159
struct nfsclsession *tsep;
4160
4161
LIST_FOREACH(clp, &nfsclhead, nfsc_list) {
4162
tsep = nfsmnt_mdssession(clp->nfsc_nmp);
4163
if (!NFSBCMP(tsep->nfsess_sessionid, sessionid,
4164
NFSX_V4SESSIONID))
4165
break;
4166
}
4167
return (clp);
4168
}
4169
4170
/*
4171
* Search for a lock conflict locally on the client. A conflict occurs if
4172
* - not same owner and overlapping byte range and at least one of them is
4173
* a write lock or this is an unlock.
4174
*/
4175
static int
4176
nfscl_localconflict(struct nfsclclient *clp, u_int8_t *fhp, int fhlen,
4177
struct nfscllock *nlop, u_int8_t *own, struct nfscldeleg *dp,
4178
struct nfscllock **lopp)
4179
{
4180
struct nfsclopen *op;
4181
int ret;
4182
4183
if (dp != NULL) {
4184
ret = nfscl_checkconflict(&dp->nfsdl_lock, nlop, own, lopp);
4185
if (ret)
4186
return (ret);
4187
}
4188
LIST_FOREACH(op, NFSCLOPENHASH(clp, fhp, fhlen), nfso_hash) {
4189
if (op->nfso_fhlen == fhlen &&
4190
!NFSBCMP(op->nfso_fh, fhp, fhlen)) {
4191
ret = nfscl_checkconflict(&op->nfso_lock, nlop,
4192
own, lopp);
4193
if (ret)
4194
return (ret);
4195
}
4196
}
4197
return (0);
4198
}
4199
4200
static int
4201
nfscl_checkconflict(struct nfscllockownerhead *lhp, struct nfscllock *nlop,
4202
u_int8_t *own, struct nfscllock **lopp)
4203
{
4204
struct nfscllockowner *lp;
4205
struct nfscllock *lop;
4206
4207
LIST_FOREACH(lp, lhp, nfsl_list) {
4208
if (NFSBCMP(lp->nfsl_owner, own, NFSV4CL_LOCKNAMELEN)) {
4209
LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) {
4210
if (lop->nfslo_first >= nlop->nfslo_end)
4211
break;
4212
if (lop->nfslo_end <= nlop->nfslo_first)
4213
continue;
4214
if (lop->nfslo_type == F_WRLCK ||
4215
nlop->nfslo_type == F_WRLCK ||
4216
nlop->nfslo_type == F_UNLCK) {
4217
if (lopp != NULL)
4218
*lopp = lop;
4219
return (NFSERR_DENIED);
4220
}
4221
}
4222
}
4223
}
4224
return (0);
4225
}
4226
4227
/*
4228
* Check for a local conflicting lock.
4229
*/
4230
int
4231
nfscl_lockt(vnode_t vp, struct nfsclclient *clp, u_int64_t off,
4232
u_int64_t len, struct flock *fl, NFSPROC_T *p, void *id, int flags)
4233
{
4234
struct nfscllock *lop, nlck;
4235
struct nfscldeleg *dp;
4236
struct nfsnode *np;
4237
u_int8_t own[NFSV4CL_LOCKNAMELEN];
4238
int error;
4239
4240
nlck.nfslo_type = fl->l_type;
4241
nlck.nfslo_first = off;
4242
if (len == NFS64BITSSET) {
4243
nlck.nfslo_end = NFS64BITSSET;
4244
} else {
4245
nlck.nfslo_end = off + len;
4246
if (nlck.nfslo_end <= nlck.nfslo_first)
4247
return (NFSERR_INVAL);
4248
}
4249
np = VTONFS(vp);
4250
nfscl_filllockowner(id, own, flags);
4251
NFSLOCKCLSTATE();
4252
dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
4253
error = nfscl_localconflict(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len,
4254
&nlck, own, dp, &lop);
4255
if (error != 0) {
4256
fl->l_whence = SEEK_SET;
4257
fl->l_start = lop->nfslo_first;
4258
if (lop->nfslo_end == NFS64BITSSET)
4259
fl->l_len = 0;
4260
else
4261
fl->l_len = lop->nfslo_end - lop->nfslo_first;
4262
fl->l_pid = (pid_t)0;
4263
fl->l_type = lop->nfslo_type;
4264
error = -1; /* no RPC required */
4265
} else if (dp != NULL && ((dp->nfsdl_flags & NFSCLDL_WRITE) ||
4266
fl->l_type == F_RDLCK)) {
4267
/*
4268
* The delegation ensures that there isn't a conflicting
4269
* lock on the server, so return -1 to indicate an RPC
4270
* isn't required.
4271
*/
4272
fl->l_type = F_UNLCK;
4273
error = -1;
4274
}
4275
NFSUNLOCKCLSTATE();
4276
return (error);
4277
}
4278
4279
/*
4280
* Handle Recall of a delegation.
4281
* The clp must be exclusive locked when this is called.
4282
*/
4283
static int
4284
nfscl_recalldeleg(struct nfsclclient *clp, struct nfsmount *nmp,
4285
struct nfscldeleg *dp, vnode_t vp, struct ucred *cred, NFSPROC_T *p,
4286
int called_from_renewthread, vnode_t *vpp)
4287
{
4288
struct nfsclowner *owp, *lowp, *nowp;
4289
struct nfsclopen *op, *lop;
4290
struct nfscllockowner *lp;
4291
struct nfscllock *lckp;
4292
struct nfsnode *np;
4293
int error = 0, ret;
4294
4295
if (vp == NULL) {
4296
KASSERT(vpp != NULL, ("nfscl_recalldeleg: vpp NULL"));
4297
*vpp = NULL;
4298
/*
4299
* First, get a vnode for the file. This is needed to do RPCs.
4300
*/
4301
ret = nfscl_ngetreopen(nmp->nm_mountp, dp->nfsdl_fh,
4302
dp->nfsdl_fhlen, p, &np);
4303
if (ret) {
4304
/*
4305
* File isn't open, so nothing to move over to the
4306
* server.
4307
*/
4308
return (0);
4309
}
4310
vp = NFSTOV(np);
4311
*vpp = vp;
4312
} else {
4313
np = VTONFS(vp);
4314
}
4315
dp->nfsdl_flags &= ~NFSCLDL_MODTIMESET;
4316
4317
/*
4318
* Ok, if it's a write delegation, flush data to the server, so
4319
* that close/open consistency is retained.
4320
*/
4321
ret = 0;
4322
NFSLOCKNODE(np);
4323
if ((dp->nfsdl_flags & NFSCLDL_WRITE) && (np->n_flag & NMODIFIED)) {
4324
np->n_flag |= NDELEGRECALL;
4325
NFSUNLOCKNODE(np);
4326
ret = ncl_flush(vp, MNT_WAIT, p, 1, called_from_renewthread);
4327
NFSLOCKNODE(np);
4328
np->n_flag &= ~NDELEGRECALL;
4329
}
4330
NFSINVALATTRCACHE(np);
4331
NFSUNLOCKNODE(np);
4332
if (ret == EIO && called_from_renewthread != 0) {
4333
/*
4334
* If the flush failed with EIO for the renew thread,
4335
* return now, so that the dirty buffer will be flushed
4336
* later.
4337
*/
4338
return (ret);
4339
}
4340
4341
/*
4342
* Now, for each openowner with opens issued locally, move them
4343
* over to state against the server.
4344
*/
4345
LIST_FOREACH(lowp, &dp->nfsdl_owner, nfsow_list) {
4346
lop = LIST_FIRST(&lowp->nfsow_open);
4347
if (lop != NULL) {
4348
if (LIST_NEXT(lop, nfso_list) != NULL)
4349
panic("nfsdlg mult opens");
4350
/*
4351
* Look for the same openowner against the server.
4352
*/
4353
LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
4354
if (!NFSBCMP(lowp->nfsow_owner,
4355
owp->nfsow_owner, NFSV4CL_LOCKNAMELEN)) {
4356
newnfs_copycred(&dp->nfsdl_cred, cred);
4357
ret = nfscl_moveopen(vp, clp, nmp, lop,
4358
owp, dp, cred, p);
4359
if (ret == NFSERR_STALECLIENTID ||
4360
ret == NFSERR_STALEDONTRECOVER ||
4361
ret == NFSERR_BADSESSION)
4362
return (ret);
4363
if (ret) {
4364
nfscl_freeopen(lop, 1, true);
4365
if (!error)
4366
error = ret;
4367
}
4368
break;
4369
}
4370
}
4371
4372
/*
4373
* If no openowner found, create one and get an open
4374
* for it.
4375
*/
4376
if (owp == NULL) {
4377
nowp = malloc(
4378
sizeof (struct nfsclowner), M_NFSCLOWNER,
4379
M_WAITOK);
4380
nfscl_newopen(clp, NULL, &owp, &nowp, &op,
4381
NULL, lowp->nfsow_owner, dp->nfsdl_fh,
4382
dp->nfsdl_fhlen, NULL, NULL);
4383
newnfs_copycred(&dp->nfsdl_cred, cred);
4384
ret = nfscl_moveopen(vp, clp, nmp, lop,
4385
owp, dp, cred, p);
4386
if (ret) {
4387
nfscl_freeopenowner(owp, 0);
4388
if (ret == NFSERR_STALECLIENTID ||
4389
ret == NFSERR_STALEDONTRECOVER ||
4390
ret == NFSERR_BADSESSION)
4391
return (ret);
4392
if (ret) {
4393
nfscl_freeopen(lop, 1, true);
4394
if (!error)
4395
error = ret;
4396
}
4397
}
4398
}
4399
}
4400
}
4401
4402
/*
4403
* Now, get byte range locks for any locks done locally.
4404
*/
4405
LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
4406
LIST_FOREACH(lckp, &lp->nfsl_lock, nfslo_list) {
4407
newnfs_copycred(&dp->nfsdl_cred, cred);
4408
ret = nfscl_relock(vp, clp, nmp, lp, lckp, cred, p);
4409
if (ret == NFSERR_STALESTATEID ||
4410
ret == NFSERR_STALEDONTRECOVER ||
4411
ret == NFSERR_STALECLIENTID ||
4412
ret == NFSERR_BADSESSION)
4413
return (ret);
4414
if (ret && !error)
4415
error = ret;
4416
}
4417
}
4418
return (error);
4419
}
4420
4421
/*
4422
* Move a locally issued open over to an owner on the state list.
4423
* SIDE EFFECT: If it needs to sleep (do an rpc), it unlocks clstate and
4424
* returns with it unlocked.
4425
*/
4426
static int
4427
nfscl_moveopen(vnode_t vp, struct nfsclclient *clp, struct nfsmount *nmp,
4428
struct nfsclopen *lop, struct nfsclowner *owp, struct nfscldeleg *dp,
4429
struct ucred *cred, NFSPROC_T *p)
4430
{
4431
struct nfsclopen *op, *nop;
4432
struct nfscldeleg *ndp;
4433
struct nfsnode *np;
4434
int error = 0, newone;
4435
4436
/*
4437
* First, look for an appropriate open, If found, just increment the
4438
* opencnt in it.
4439
*/
4440
LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
4441
if ((op->nfso_mode & lop->nfso_mode) == lop->nfso_mode &&
4442
op->nfso_fhlen == lop->nfso_fhlen &&
4443
!NFSBCMP(op->nfso_fh, lop->nfso_fh, op->nfso_fhlen)) {
4444
op->nfso_opencnt += lop->nfso_opencnt;
4445
nfscl_freeopen(lop, 1, true);
4446
return (0);
4447
}
4448
}
4449
4450
/* No appropriate open, so we have to do one against the server. */
4451
np = VTONFS(vp);
4452
nop = malloc(sizeof (struct nfsclopen) +
4453
lop->nfso_fhlen - 1, M_NFSCLOPEN, M_WAITOK);
4454
nop->nfso_hash.le_prev = NULL;
4455
newone = 0;
4456
nfscl_newopen(clp, NULL, &owp, NULL, &op, &nop, owp->nfsow_owner,
4457
lop->nfso_fh, lop->nfso_fhlen, cred, &newone);
4458
ndp = dp;
4459
if (NFSHASNFSV4N(nmp))
4460
error = nfscl_tryopen(nmp, vp, lop->nfso_fh, lop->nfso_fhlen,
4461
lop->nfso_fh, lop->nfso_fhlen, lop->nfso_mode, op,
4462
NULL, 0, &ndp, 0, 0, cred, p);
4463
else
4464
error = nfscl_tryopen(nmp, vp, np->n_v4->n4_data,
4465
np->n_v4->n4_fhlen, lop->nfso_fh, lop->nfso_fhlen,
4466
lop->nfso_mode, op, NFS4NODENAME(np->n_v4),
4467
np->n_v4->n4_namelen, &ndp, 0, 0, cred, p);
4468
if (error) {
4469
if (newone)
4470
nfscl_freeopen(op, 0, true);
4471
} else {
4472
op->nfso_mode |= lop->nfso_mode;
4473
op->nfso_opencnt += lop->nfso_opencnt;
4474
nfscl_freeopen(lop, 1, true);
4475
}
4476
if (nop != NULL)
4477
free(nop, M_NFSCLOPEN);
4478
if (ndp != NULL) {
4479
/*
4480
* What should I do with the returned delegation, since the
4481
* delegation is being recalled? For now, just printf and
4482
* through it away.
4483
*/
4484
printf("Moveopen returned deleg\n");
4485
free(ndp, M_NFSCLDELEG);
4486
}
4487
return (error);
4488
}
4489
4490
/*
4491
* Recall all delegations on this client.
4492
*/
4493
static void
4494
nfscl_totalrecall(struct nfsclclient *clp)
4495
{
4496
struct nfscldeleg *dp;
4497
4498
TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) {
4499
if ((dp->nfsdl_flags & NFSCLDL_DELEGRET) == 0)
4500
dp->nfsdl_flags |= NFSCLDL_RECALL;
4501
}
4502
}
4503
4504
/*
4505
* Relock byte ranges. Called for delegation recall and state expiry.
4506
*/
4507
static int
4508
nfscl_relock(vnode_t vp, struct nfsclclient *clp, struct nfsmount *nmp,
4509
struct nfscllockowner *lp, struct nfscllock *lop, struct ucred *cred,
4510
NFSPROC_T *p)
4511
{
4512
struct nfscllockowner *nlp;
4513
struct nfsfh *nfhp;
4514
struct nfsnode *np;
4515
u_int64_t off, len;
4516
int error, newone, donelocally;
4517
4518
if (NFSHASNFSV4N(nmp) && NFSHASONEOPENOWN(nmp)) {
4519
np = VTONFS(vp);
4520
NFSLOCKNODE(np);
4521
np->n_flag |= NMIGHTBELOCKED;
4522
NFSUNLOCKNODE(np);
4523
}
4524
4525
off = lop->nfslo_first;
4526
len = lop->nfslo_end - lop->nfslo_first;
4527
error = nfscl_getbytelock(vp, off, len, lop->nfslo_type, cred, p,
4528
clp, 1, NULL, lp->nfsl_lockflags, lp->nfsl_owner,
4529
lp->nfsl_openowner, &nlp, &newone, &donelocally);
4530
if (error || donelocally)
4531
return (error);
4532
nfhp = VTONFS(vp)->n_fhp;
4533
error = nfscl_trylock(nmp, vp, nfhp->nfh_fh,
4534
nfhp->nfh_len, nlp, newone, 0, off,
4535
len, lop->nfslo_type, cred, p);
4536
if (error)
4537
nfscl_freelockowner(nlp, 0);
4538
return (error);
4539
}
4540
4541
/*
4542
* Called to re-open a file. Basically get a vnode for the file handle
4543
* and then call nfsrpc_openrpc() to do the rest.
4544
*/
4545
static int
4546
nfsrpc_reopen(struct nfsmount *nmp, u_int8_t *fhp, int fhlen,
4547
u_int32_t mode, struct nfsclopen *op, struct nfscldeleg **dpp,
4548
struct ucred *cred, NFSPROC_T *p)
4549
{
4550
struct nfsnode *np;
4551
vnode_t vp;
4552
int error;
4553
4554
error = nfscl_ngetreopen(nmp->nm_mountp, fhp, fhlen, p, &np);
4555
if (error)
4556
return (error);
4557
vp = NFSTOV(np);
4558
if (NFSHASNFSV4N(nmp))
4559
error = nfscl_tryopen(nmp, vp, fhp, fhlen, fhp, fhlen, mode, op,
4560
NULL, 0, dpp, 0, 0, cred, p);
4561
else if (np->n_v4 != NULL)
4562
error = nfscl_tryopen(nmp, vp, np->n_v4->n4_data,
4563
np->n_v4->n4_fhlen, fhp, fhlen, mode, op,
4564
NFS4NODENAME(np->n_v4), np->n_v4->n4_namelen, dpp, 0, 0,
4565
cred, p);
4566
else
4567
error = EINVAL;
4568
vrele(vp);
4569
return (error);
4570
}
4571
4572
/*
4573
* Try an open against the server. Just call nfsrpc_openrpc(), retrying while
4574
* NFSERR_DELAY. Also, try system credentials, if the passed in credentials
4575
* fail.
4576
*/
4577
static int
4578
nfscl_tryopen(struct nfsmount *nmp, vnode_t vp, u_int8_t *fhp, int fhlen,
4579
u_int8_t *newfhp, int newfhlen, u_int32_t mode, struct nfsclopen *op,
4580
u_int8_t *name, int namelen, struct nfscldeleg **ndpp,
4581
int reclaim, u_int32_t delegtype, struct ucred *cred, NFSPROC_T *p)
4582
{
4583
int error;
4584
struct nfscldeleg *dp;
4585
4586
dp = *ndpp;
4587
do {
4588
*ndpp = dp; /* *ndpp needs to be set for retries. */
4589
error = nfsrpc_openrpc(nmp, vp, fhp, fhlen, newfhp, newfhlen,
4590
mode, op, name, namelen, ndpp, reclaim, delegtype, cred, p,
4591
0, 0);
4592
if (error == NFSERR_DELAY)
4593
(void) nfs_catnap(PZERO, error, "nfstryop");
4594
} while (error == NFSERR_DELAY);
4595
if (error == EAUTH || error == EACCES) {
4596
/* Try again using system credentials */
4597
newnfs_setroot(cred);
4598
do {
4599
*ndpp = dp; /* *ndpp needs to be set for retries. */
4600
error = nfsrpc_openrpc(nmp, vp, fhp, fhlen, newfhp,
4601
newfhlen, mode, op, name, namelen, ndpp, reclaim,
4602
delegtype, cred, p, 1, 0);
4603
if (error == NFSERR_DELAY)
4604
(void) nfs_catnap(PZERO, error, "nfstryop");
4605
} while (error == NFSERR_DELAY);
4606
}
4607
return (error);
4608
}
4609
4610
/*
4611
* Try a byte range lock. Just loop on nfsrpc_lock() while it returns
4612
* NFSERR_DELAY. Also, retry with system credentials, if the provided
4613
* cred don't work.
4614
*/
4615
static int
4616
nfscl_trylock(struct nfsmount *nmp, vnode_t vp, u_int8_t *fhp,
4617
int fhlen, struct nfscllockowner *nlp, int newone, int reclaim,
4618
u_int64_t off, u_int64_t len, short type, struct ucred *cred, NFSPROC_T *p)
4619
{
4620
struct nfsrv_descript nfsd, *nd = &nfsd;
4621
int error;
4622
4623
do {
4624
error = nfsrpc_lock(nd, nmp, vp, fhp, fhlen, nlp, newone,
4625
reclaim, off, len, type, cred, p, 0);
4626
if (!error && nd->nd_repstat == NFSERR_DELAY)
4627
(void) nfs_catnap(PZERO, (int)nd->nd_repstat,
4628
"nfstrylck");
4629
} while (!error && nd->nd_repstat == NFSERR_DELAY);
4630
if (!error)
4631
error = nd->nd_repstat;
4632
if (error == EAUTH || error == EACCES) {
4633
/* Try again using root credentials */
4634
newnfs_setroot(cred);
4635
do {
4636
error = nfsrpc_lock(nd, nmp, vp, fhp, fhlen, nlp,
4637
newone, reclaim, off, len, type, cred, p, 1);
4638
if (!error && nd->nd_repstat == NFSERR_DELAY)
4639
(void) nfs_catnap(PZERO, (int)nd->nd_repstat,
4640
"nfstrylck");
4641
} while (!error && nd->nd_repstat == NFSERR_DELAY);
4642
if (!error)
4643
error = nd->nd_repstat;
4644
}
4645
return (error);
4646
}
4647
4648
/*
4649
* Try a delegreturn against the server. Just call nfsrpc_delegreturn(),
4650
* retrying while NFSERR_DELAY. Also, try system credentials, if the passed in
4651
* credentials fail.
4652
*/
4653
int
4654
nfscl_trydelegreturn(struct nfscldeleg *dp, struct ucred *cred,
4655
struct nfsmount *nmp, NFSPROC_T *p)
4656
{
4657
int error;
4658
4659
do {
4660
error = nfsrpc_delegreturn(dp, cred, nmp, p, 0);
4661
if (error == NFSERR_DELAY)
4662
(void) nfs_catnap(PZERO, error, "nfstrydp");
4663
} while (error == NFSERR_DELAY);
4664
if (error == EAUTH || error == EACCES) {
4665
/* Try again using system credentials */
4666
newnfs_setroot(cred);
4667
do {
4668
error = nfsrpc_delegreturn(dp, cred, nmp, p, 1);
4669
if (error == NFSERR_DELAY)
4670
(void) nfs_catnap(PZERO, error, "nfstrydp");
4671
} while (error == NFSERR_DELAY);
4672
}
4673
return (error);
4674
}
4675
4676
/*
4677
* Try a close against the server. Just call nfsrpc_closerpc(),
4678
* retrying while NFSERR_DELAY. Also, try system credentials, if the passed in
4679
* credentials fail.
4680
*/
4681
int
4682
nfscl_tryclose(struct nfsclopen *op, struct ucred *cred,
4683
struct nfsmount *nmp, NFSPROC_T *p, bool loop_on_delayed)
4684
{
4685
struct nfsrv_descript nfsd, *nd = &nfsd;
4686
int error;
4687
4688
do {
4689
error = nfsrpc_closerpc(nd, nmp, op, cred, p, 0);
4690
if (loop_on_delayed && error == NFSERR_DELAY)
4691
(void) nfs_catnap(PZERO, error, "nfstrycl");
4692
} while (loop_on_delayed && error == NFSERR_DELAY);
4693
if (error == EAUTH || error == EACCES) {
4694
/* Try again using system credentials */
4695
newnfs_setroot(cred);
4696
do {
4697
error = nfsrpc_closerpc(nd, nmp, op, cred, p, 1);
4698
if (loop_on_delayed && error == NFSERR_DELAY)
4699
(void) nfs_catnap(PZERO, error, "nfstrycl");
4700
} while (loop_on_delayed && error == NFSERR_DELAY);
4701
}
4702
return (error);
4703
}
4704
4705
/*
4706
* Decide if a delegation on a file permits close without flushing writes
4707
* to the server. This might be a big performance win in some environments.
4708
* (Not useful until the client does caching on local stable storage.)
4709
*/
4710
int
4711
nfscl_mustflush(vnode_t vp)
4712
{
4713
struct nfsclclient *clp;
4714
struct nfscldeleg *dp;
4715
struct nfsnode *np;
4716
struct nfsmount *nmp;
4717
4718
np = VTONFS(vp);
4719
nmp = VFSTONFS(vp->v_mount);
4720
if (!NFSHASNFSV4(nmp) || vp->v_type != VREG)
4721
return (1);
4722
NFSLOCKMNT(nmp);
4723
if ((nmp->nm_privflag & NFSMNTP_DELEGISSUED) == 0) {
4724
NFSUNLOCKMNT(nmp);
4725
return (1);
4726
}
4727
NFSUNLOCKMNT(nmp);
4728
NFSLOCKCLSTATE();
4729
clp = nfscl_findcl(nmp);
4730
if (clp == NULL) {
4731
NFSUNLOCKCLSTATE();
4732
return (1);
4733
}
4734
dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
4735
if (dp != NULL && (dp->nfsdl_flags &
4736
(NFSCLDL_WRITE | NFSCLDL_RECALL | NFSCLDL_DELEGRET)) ==
4737
NFSCLDL_WRITE &&
4738
(dp->nfsdl_sizelimit >= np->n_size ||
4739
!NFSHASSTRICT3530(nmp))) {
4740
NFSUNLOCKCLSTATE();
4741
return (0);
4742
}
4743
NFSUNLOCKCLSTATE();
4744
return (1);
4745
}
4746
4747
/*
4748
* See if a (write) delegation exists for this file.
4749
*/
4750
int
4751
nfscl_nodeleg(vnode_t vp, int writedeleg)
4752
{
4753
struct nfsclclient *clp;
4754
struct nfscldeleg *dp;
4755
struct nfsnode *np;
4756
struct nfsmount *nmp;
4757
4758
np = VTONFS(vp);
4759
nmp = VFSTONFS(vp->v_mount);
4760
if (!NFSHASNFSV4(nmp) || vp->v_type != VREG)
4761
return (1);
4762
NFSLOCKMNT(nmp);
4763
if ((nmp->nm_privflag & NFSMNTP_DELEGISSUED) == 0) {
4764
NFSUNLOCKMNT(nmp);
4765
return (1);
4766
}
4767
NFSUNLOCKMNT(nmp);
4768
NFSLOCKCLSTATE();
4769
clp = nfscl_findcl(nmp);
4770
if (clp == NULL) {
4771
NFSUNLOCKCLSTATE();
4772
return (1);
4773
}
4774
dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
4775
if (dp != NULL &&
4776
(dp->nfsdl_flags & (NFSCLDL_RECALL | NFSCLDL_DELEGRET)) == 0 &&
4777
(writedeleg == 0 || (dp->nfsdl_flags & NFSCLDL_WRITE) ==
4778
NFSCLDL_WRITE)) {
4779
NFSUNLOCKCLSTATE();
4780
return (0);
4781
}
4782
NFSUNLOCKCLSTATE();
4783
return (1);
4784
}
4785
4786
/*
4787
* Look for an associated delegation that should be DelegReturned.
4788
*/
4789
int
4790
nfscl_removedeleg(vnode_t vp, NFSPROC_T *p, nfsv4stateid_t *stp)
4791
{
4792
struct nfsclclient *clp;
4793
struct nfscldeleg *dp;
4794
struct nfsclowner *owp;
4795
struct nfscllockowner *lp;
4796
struct nfsmount *nmp;
4797
struct mount *mp;
4798
struct ucred *cred;
4799
struct nfsnode *np;
4800
int igotlock = 0, triedrecall = 0, needsrecall, retcnt = 0, islept;
4801
4802
nmp = VFSTONFS(vp->v_mount);
4803
if (NFSHASPNFS(nmp))
4804
return (retcnt);
4805
NFSLOCKMNT(nmp);
4806
if ((nmp->nm_privflag & NFSMNTP_DELEGISSUED) == 0) {
4807
NFSUNLOCKMNT(nmp);
4808
return (retcnt);
4809
}
4810
NFSUNLOCKMNT(nmp);
4811
np = VTONFS(vp);
4812
mp = nmp->nm_mountp;
4813
NFSLOCKCLSTATE();
4814
/*
4815
* Loop around waiting for:
4816
* - outstanding I/O operations on delegations to complete
4817
* - for a delegation on vp that has state, lock the client and
4818
* do a recall
4819
* - return delegation with no state
4820
*/
4821
while (1) {
4822
clp = nfscl_findcl(nmp);
4823
if (clp == NULL) {
4824
NFSUNLOCKCLSTATE();
4825
return (retcnt);
4826
}
4827
dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh,
4828
np->n_fhp->nfh_len);
4829
if (dp != NULL) {
4830
/*
4831
* Wait for outstanding I/O ops to be done.
4832
*/
4833
if (dp->nfsdl_rwlock.nfslock_usecnt > 0) {
4834
if (igotlock) {
4835
nfsv4_unlock(&clp->nfsc_lock, 0);
4836
igotlock = 0;
4837
}
4838
dp->nfsdl_rwlock.nfslock_lock |= NFSV4LOCK_WANTED;
4839
msleep(&dp->nfsdl_rwlock, NFSCLSTATEMUTEXPTR, PZERO,
4840
"nfscld", hz);
4841
if (NFSCL_FORCEDISM(mp)) {
4842
dp->nfsdl_flags &= ~NFSCLDL_DELEGRET;
4843
NFSUNLOCKCLSTATE();
4844
return (0);
4845
}
4846
continue;
4847
}
4848
needsrecall = 0;
4849
LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) {
4850
if (!LIST_EMPTY(&owp->nfsow_open)) {
4851
needsrecall = 1;
4852
break;
4853
}
4854
}
4855
if (!needsrecall) {
4856
LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
4857
if (!LIST_EMPTY(&lp->nfsl_lock)) {
4858
needsrecall = 1;
4859
break;
4860
}
4861
}
4862
}
4863
if (needsrecall && !triedrecall) {
4864
dp->nfsdl_flags |= NFSCLDL_DELEGRET;
4865
islept = 0;
4866
while (!igotlock) {
4867
igotlock = nfsv4_lock(&clp->nfsc_lock, 1,
4868
&islept, NFSCLSTATEMUTEXPTR, mp);
4869
if (NFSCL_FORCEDISM(mp)) {
4870
dp->nfsdl_flags &= ~NFSCLDL_DELEGRET;
4871
if (igotlock)
4872
nfsv4_unlock(&clp->nfsc_lock, 0);
4873
NFSUNLOCKCLSTATE();
4874
return (0);
4875
}
4876
if (islept)
4877
break;
4878
}
4879
if (islept)
4880
continue;
4881
NFSUNLOCKCLSTATE();
4882
cred = newnfs_getcred();
4883
newnfs_copycred(&dp->nfsdl_cred, cred);
4884
nfscl_recalldeleg(clp, nmp, dp, vp, cred, p, 0, NULL);
4885
NFSFREECRED(cred);
4886
triedrecall = 1;
4887
NFSLOCKCLSTATE();
4888
nfsv4_unlock(&clp->nfsc_lock, 0);
4889
igotlock = 0;
4890
continue;
4891
}
4892
*stp = dp->nfsdl_stateid;
4893
retcnt = 1;
4894
nfscl_cleandeleg(dp);
4895
nfscl_freedeleg(&clp->nfsc_deleg, dp, true);
4896
}
4897
if (igotlock)
4898
nfsv4_unlock(&clp->nfsc_lock, 0);
4899
NFSUNLOCKCLSTATE();
4900
return (retcnt);
4901
}
4902
}
4903
4904
/*
4905
* Look for associated delegation(s) that should be DelegReturned.
4906
*/
4907
int
4908
nfscl_renamedeleg(vnode_t fvp, nfsv4stateid_t *fstp, int *gotfdp, vnode_t tvp,
4909
nfsv4stateid_t *tstp, int *gottdp, NFSPROC_T *p)
4910
{
4911
struct nfsclclient *clp;
4912
struct nfscldeleg *dp;
4913
struct nfsclowner *owp;
4914
struct nfscllockowner *lp;
4915
struct nfsmount *nmp;
4916
struct mount *mp;
4917
struct ucred *cred;
4918
struct nfsnode *np;
4919
int igotlock = 0, triedrecall = 0, needsrecall, retcnt = 0, islept;
4920
4921
nmp = VFSTONFS(fvp->v_mount);
4922
*gotfdp = 0;
4923
*gottdp = 0;
4924
if (NFSHASPNFS(nmp))
4925
return (retcnt);
4926
NFSLOCKMNT(nmp);
4927
if ((nmp->nm_privflag & NFSMNTP_DELEGISSUED) == 0) {
4928
NFSUNLOCKMNT(nmp);
4929
return (retcnt);
4930
}
4931
NFSUNLOCKMNT(nmp);
4932
mp = nmp->nm_mountp;
4933
NFSLOCKCLSTATE();
4934
/*
4935
* Loop around waiting for:
4936
* - outstanding I/O operations on delegations to complete
4937
* - for a delegation on fvp that has state, lock the client and
4938
* do a recall
4939
* - return delegation(s) with no state.
4940
*/
4941
while (1) {
4942
clp = nfscl_findcl(nmp);
4943
if (clp == NULL) {
4944
NFSUNLOCKCLSTATE();
4945
return (retcnt);
4946
}
4947
np = VTONFS(fvp);
4948
dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh,
4949
np->n_fhp->nfh_len);
4950
if (dp != NULL && *gotfdp == 0) {
4951
/*
4952
* Wait for outstanding I/O ops to be done.
4953
*/
4954
if (dp->nfsdl_rwlock.nfslock_usecnt > 0) {
4955
if (igotlock) {
4956
nfsv4_unlock(&clp->nfsc_lock, 0);
4957
igotlock = 0;
4958
}
4959
dp->nfsdl_rwlock.nfslock_lock |= NFSV4LOCK_WANTED;
4960
msleep(&dp->nfsdl_rwlock, NFSCLSTATEMUTEXPTR, PZERO,
4961
"nfscld", hz);
4962
if (NFSCL_FORCEDISM(mp)) {
4963
dp->nfsdl_flags &= ~NFSCLDL_DELEGRET;
4964
NFSUNLOCKCLSTATE();
4965
*gotfdp = 0;
4966
*gottdp = 0;
4967
return (0);
4968
}
4969
continue;
4970
}
4971
needsrecall = 0;
4972
LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) {
4973
if (!LIST_EMPTY(&owp->nfsow_open)) {
4974
needsrecall = 1;
4975
break;
4976
}
4977
}
4978
if (!needsrecall) {
4979
LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
4980
if (!LIST_EMPTY(&lp->nfsl_lock)) {
4981
needsrecall = 1;
4982
break;
4983
}
4984
}
4985
}
4986
if (needsrecall && !triedrecall) {
4987
dp->nfsdl_flags |= NFSCLDL_DELEGRET;
4988
islept = 0;
4989
while (!igotlock) {
4990
igotlock = nfsv4_lock(&clp->nfsc_lock, 1,
4991
&islept, NFSCLSTATEMUTEXPTR, mp);
4992
if (NFSCL_FORCEDISM(mp)) {
4993
dp->nfsdl_flags &= ~NFSCLDL_DELEGRET;
4994
if (igotlock)
4995
nfsv4_unlock(&clp->nfsc_lock, 0);
4996
NFSUNLOCKCLSTATE();
4997
*gotfdp = 0;
4998
*gottdp = 0;
4999
return (0);
5000
}
5001
if (islept)
5002
break;
5003
}
5004
if (islept)
5005
continue;
5006
NFSUNLOCKCLSTATE();
5007
cred = newnfs_getcred();
5008
newnfs_copycred(&dp->nfsdl_cred, cred);
5009
nfscl_recalldeleg(clp, nmp, dp, fvp, cred, p, 0, NULL);
5010
NFSFREECRED(cred);
5011
triedrecall = 1;
5012
NFSLOCKCLSTATE();
5013
nfsv4_unlock(&clp->nfsc_lock, 0);
5014
igotlock = 0;
5015
continue;
5016
}
5017
*fstp = dp->nfsdl_stateid;
5018
retcnt++;
5019
*gotfdp = 1;
5020
nfscl_cleandeleg(dp);
5021
nfscl_freedeleg(&clp->nfsc_deleg, dp, true);
5022
}
5023
if (igotlock) {
5024
nfsv4_unlock(&clp->nfsc_lock, 0);
5025
igotlock = 0;
5026
}
5027
if (tvp != NULL) {
5028
np = VTONFS(tvp);
5029
dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh,
5030
np->n_fhp->nfh_len);
5031
if (dp != NULL && *gottdp == 0) {
5032
/*
5033
* Wait for outstanding I/O ops to be done.
5034
*/
5035
if (dp->nfsdl_rwlock.nfslock_usecnt > 0) {
5036
dp->nfsdl_rwlock.nfslock_lock |= NFSV4LOCK_WANTED;
5037
msleep(&dp->nfsdl_rwlock, NFSCLSTATEMUTEXPTR, PZERO,
5038
"nfscld", hz);
5039
if (NFSCL_FORCEDISM(mp)) {
5040
NFSUNLOCKCLSTATE();
5041
*gotfdp = 0;
5042
*gottdp = 0;
5043
return (0);
5044
}
5045
continue;
5046
}
5047
LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) {
5048
if (!LIST_EMPTY(&owp->nfsow_open)) {
5049
NFSUNLOCKCLSTATE();
5050
return (retcnt);
5051
}
5052
}
5053
LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
5054
if (!LIST_EMPTY(&lp->nfsl_lock)) {
5055
NFSUNLOCKCLSTATE();
5056
return (retcnt);
5057
}
5058
}
5059
*tstp = dp->nfsdl_stateid;
5060
retcnt++;
5061
*gottdp = 1;
5062
nfscl_cleandeleg(dp);
5063
nfscl_freedeleg(&clp->nfsc_deleg, dp, true);
5064
}
5065
}
5066
NFSUNLOCKCLSTATE();
5067
return (retcnt);
5068
}
5069
}
5070
5071
/*
5072
* Get a reference on the clientid associated with the mount point.
5073
* Return 1 if success, 0 otherwise.
5074
*/
5075
int
5076
nfscl_getref(struct nfsmount *nmp)
5077
{
5078
struct nfsclclient *clp;
5079
int ret;
5080
5081
NFSLOCKCLSTATE();
5082
clp = nfscl_findcl(nmp);
5083
if (clp == NULL) {
5084
NFSUNLOCKCLSTATE();
5085
return (0);
5086
}
5087
nfsv4_getref(&clp->nfsc_lock, NULL, NFSCLSTATEMUTEXPTR, nmp->nm_mountp);
5088
ret = 1;
5089
if (NFSCL_FORCEDISM(nmp->nm_mountp))
5090
ret = 0;
5091
NFSUNLOCKCLSTATE();
5092
return (ret);
5093
}
5094
5095
/*
5096
* Release a reference on a clientid acquired with the above call.
5097
*/
5098
void
5099
nfscl_relref(struct nfsmount *nmp)
5100
{
5101
struct nfsclclient *clp;
5102
5103
NFSLOCKCLSTATE();
5104
clp = nfscl_findcl(nmp);
5105
if (clp == NULL) {
5106
NFSUNLOCKCLSTATE();
5107
return;
5108
}
5109
nfsv4_relref(&clp->nfsc_lock);
5110
NFSUNLOCKCLSTATE();
5111
}
5112
5113
/*
5114
* Save the size attribute in the delegation, since the nfsnode
5115
* is going away.
5116
*/
5117
void
5118
nfscl_reclaimnode(vnode_t vp)
5119
{
5120
struct nfsclclient *clp;
5121
struct nfscldeleg *dp;
5122
struct nfsnode *np = VTONFS(vp);
5123
struct nfsmount *nmp;
5124
5125
nmp = VFSTONFS(vp->v_mount);
5126
if (!NFSHASNFSV4(nmp))
5127
return;
5128
NFSLOCKCLSTATE();
5129
clp = nfscl_findcl(nmp);
5130
if (clp == NULL) {
5131
NFSUNLOCKCLSTATE();
5132
return;
5133
}
5134
dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
5135
if (dp != NULL && (dp->nfsdl_flags & NFSCLDL_WRITE))
5136
dp->nfsdl_size = np->n_size;
5137
NFSUNLOCKCLSTATE();
5138
}
5139
5140
/*
5141
* Get the saved size attribute in the delegation, since it is a
5142
* newly allocated nfsnode.
5143
*/
5144
void
5145
nfscl_newnode(vnode_t vp)
5146
{
5147
struct nfsclclient *clp;
5148
struct nfscldeleg *dp;
5149
struct nfsnode *np = VTONFS(vp);
5150
struct nfsmount *nmp;
5151
5152
nmp = VFSTONFS(vp->v_mount);
5153
if (!NFSHASNFSV4(nmp))
5154
return;
5155
NFSLOCKCLSTATE();
5156
clp = nfscl_findcl(nmp);
5157
if (clp == NULL) {
5158
NFSUNLOCKCLSTATE();
5159
return;
5160
}
5161
dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
5162
if (dp != NULL && (dp->nfsdl_flags & NFSCLDL_WRITE))
5163
np->n_size = dp->nfsdl_size;
5164
NFSUNLOCKCLSTATE();
5165
}
5166
5167
/*
5168
* If there is a valid write delegation for this file, set the modtime
5169
* to the local clock time.
5170
*/
5171
void
5172
nfscl_delegmodtime(struct vnode *vp, struct timespec *mtime)
5173
{
5174
struct nfsclclient *clp;
5175
struct nfscldeleg *dp;
5176
struct nfsnode *np = VTONFS(vp);
5177
struct nfsmount *nmp;
5178
5179
nmp = VFSTONFS(vp->v_mount);
5180
if (!NFSHASNFSV4(nmp))
5181
return;
5182
NFSLOCKMNT(nmp);
5183
if ((nmp->nm_privflag & NFSMNTP_DELEGISSUED) == 0) {
5184
NFSUNLOCKMNT(nmp);
5185
return;
5186
}
5187
NFSUNLOCKMNT(nmp);
5188
NFSLOCKCLSTATE();
5189
clp = nfscl_findcl(nmp);
5190
if (clp == NULL) {
5191
NFSUNLOCKCLSTATE();
5192
return;
5193
}
5194
dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
5195
if (dp != NULL && (dp->nfsdl_flags & NFSCLDL_WRITE)) {
5196
if (mtime != NULL)
5197
dp->nfsdl_modtime = *mtime;
5198
else
5199
nanotime(&dp->nfsdl_modtime);
5200
dp->nfsdl_flags |= NFSCLDL_MODTIMESET;
5201
}
5202
NFSUNLOCKCLSTATE();
5203
}
5204
5205
/*
5206
* If there is a valid write delegation for this file with a modtime set,
5207
* put that modtime in mtime.
5208
*/
5209
void
5210
nfscl_deleggetmodtime(vnode_t vp, struct timespec *mtime)
5211
{
5212
struct nfsclclient *clp;
5213
struct nfscldeleg *dp;
5214
struct nfsnode *np = VTONFS(vp);
5215
struct nfsmount *nmp;
5216
5217
nmp = VFSTONFS(vp->v_mount);
5218
if (!NFSHASNFSV4(nmp))
5219
return;
5220
NFSLOCKMNT(nmp);
5221
if ((nmp->nm_privflag & NFSMNTP_DELEGISSUED) == 0) {
5222
NFSUNLOCKMNT(nmp);
5223
return;
5224
}
5225
NFSUNLOCKMNT(nmp);
5226
NFSLOCKCLSTATE();
5227
clp = nfscl_findcl(nmp);
5228
if (clp == NULL) {
5229
NFSUNLOCKCLSTATE();
5230
return;
5231
}
5232
dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
5233
if (dp != NULL &&
5234
(dp->nfsdl_flags & (NFSCLDL_WRITE | NFSCLDL_MODTIMESET)) ==
5235
(NFSCLDL_WRITE | NFSCLDL_MODTIMESET))
5236
*mtime = dp->nfsdl_modtime;
5237
NFSUNLOCKCLSTATE();
5238
}
5239
5240
static int
5241
nfscl_errmap(struct nfsrv_descript *nd, u_int32_t minorvers)
5242
{
5243
short *defaulterrp, *errp;
5244
5245
if (!nd->nd_repstat)
5246
return (0);
5247
if (nd->nd_procnum == NFSPROC_NOOP)
5248
return (txdr_unsigned(nd->nd_repstat & 0xffff));
5249
if (nd->nd_repstat == EBADRPC)
5250
return (txdr_unsigned(NFSERR_BADXDR));
5251
if (nd->nd_repstat == NFSERR_MINORVERMISMATCH ||
5252
nd->nd_repstat == NFSERR_OPILLEGAL)
5253
return (txdr_unsigned(nd->nd_repstat));
5254
if (nd->nd_repstat >= NFSERR_BADIOMODE && nd->nd_repstat < 20000 &&
5255
minorvers > NFSV4_MINORVERSION) {
5256
/* NFSv4.n error. */
5257
return (txdr_unsigned(nd->nd_repstat));
5258
}
5259
if (nd->nd_procnum < NFSV4OP_CBNOPS)
5260
errp = defaulterrp = nfscl_cberrmap[nd->nd_procnum];
5261
else
5262
return (txdr_unsigned(nd->nd_repstat));
5263
while (*++errp)
5264
if (*errp == (short)nd->nd_repstat)
5265
return (txdr_unsigned(nd->nd_repstat));
5266
return (txdr_unsigned(*defaulterrp));
5267
}
5268
5269
/*
5270
* Called to find/add a layout to a client.
5271
* This function returns the layout with a refcnt (shared lock) upon
5272
* success (returns 0) or with no lock/refcnt on the layout when an
5273
* error is returned.
5274
* If a layout is passed in via lypp, it is locked (exclusively locked).
5275
*/
5276
int
5277
nfscl_layout(struct nfsmount *nmp, vnode_t vp, u_int8_t *fhp, int fhlen,
5278
nfsv4stateid_t *stateidp, int layouttype, int retonclose,
5279
struct nfsclflayouthead *fhlp, struct nfscllayout **lypp,
5280
struct ucred *cred, NFSPROC_T *p)
5281
{
5282
struct nfsclclient *clp;
5283
struct nfscllayout *lyp, *tlyp;
5284
struct nfsclflayout *flp;
5285
struct nfsnode *np = VTONFS(vp);
5286
mount_t mp;
5287
int layout_passed_in;
5288
5289
mp = nmp->nm_mountp;
5290
layout_passed_in = 1;
5291
tlyp = NULL;
5292
lyp = *lypp;
5293
if (lyp == NULL) {
5294
layout_passed_in = 0;
5295
tlyp = malloc(sizeof(*tlyp) + fhlen - 1, M_NFSLAYOUT,
5296
M_WAITOK | M_ZERO);
5297
}
5298
5299
NFSLOCKCLSTATE();
5300
clp = nmp->nm_clp;
5301
if (clp == NULL) {
5302
if (layout_passed_in != 0)
5303
nfsv4_unlock(&lyp->nfsly_lock, 0);
5304
NFSUNLOCKCLSTATE();
5305
if (tlyp != NULL)
5306
free(tlyp, M_NFSLAYOUT);
5307
return (EPERM);
5308
}
5309
if (lyp == NULL) {
5310
/*
5311
* Although no lyp was passed in, another thread might have
5312
* allocated one. If one is found, just increment it's ref
5313
* count and return it.
5314
*/
5315
lyp = nfscl_findlayout(clp, fhp, fhlen);
5316
if (lyp == NULL) {
5317
lyp = tlyp;
5318
tlyp = NULL;
5319
lyp->nfsly_stateid.seqid = stateidp->seqid;
5320
lyp->nfsly_stateid.other[0] = stateidp->other[0];
5321
lyp->nfsly_stateid.other[1] = stateidp->other[1];
5322
lyp->nfsly_stateid.other[2] = stateidp->other[2];
5323
lyp->nfsly_lastbyte = 0;
5324
LIST_INIT(&lyp->nfsly_flayread);
5325
LIST_INIT(&lyp->nfsly_flayrw);
5326
LIST_INIT(&lyp->nfsly_recall);
5327
lyp->nfsly_filesid[0] = np->n_vattr.na_filesid[0];
5328
lyp->nfsly_filesid[1] = np->n_vattr.na_filesid[1];
5329
lyp->nfsly_clp = clp;
5330
if (layouttype == NFSLAYOUT_FLEXFILE)
5331
lyp->nfsly_flags = NFSLY_FLEXFILE;
5332
else
5333
lyp->nfsly_flags = NFSLY_FILES;
5334
if (retonclose != 0)
5335
lyp->nfsly_flags |= NFSLY_RETONCLOSE;
5336
lyp->nfsly_fhlen = fhlen;
5337
NFSBCOPY(fhp, lyp->nfsly_fh, fhlen);
5338
TAILQ_INSERT_HEAD(&clp->nfsc_layout, lyp, nfsly_list);
5339
LIST_INSERT_HEAD(NFSCLLAYOUTHASH(clp, fhp, fhlen), lyp,
5340
nfsly_hash);
5341
lyp->nfsly_timestamp = NFSD_MONOSEC + 120;
5342
clp->nfsc_layoutcnt++;
5343
nfsstatsv1.cllayouts++;
5344
} else {
5345
if (retonclose != 0)
5346
lyp->nfsly_flags |= NFSLY_RETONCLOSE;
5347
if (stateidp->seqid > lyp->nfsly_stateid.seqid)
5348
lyp->nfsly_stateid.seqid = stateidp->seqid;
5349
TAILQ_REMOVE(&clp->nfsc_layout, lyp, nfsly_list);
5350
TAILQ_INSERT_HEAD(&clp->nfsc_layout, lyp, nfsly_list);
5351
lyp->nfsly_timestamp = NFSD_MONOSEC + 120;
5352
}
5353
nfsv4_getref(&lyp->nfsly_lock, NULL, NFSCLSTATEMUTEXPTR, mp);
5354
if (NFSCL_FORCEDISM(mp)) {
5355
NFSUNLOCKCLSTATE();
5356
if (tlyp != NULL)
5357
free(tlyp, M_NFSLAYOUT);
5358
return (EPERM);
5359
}
5360
*lypp = lyp;
5361
} else if (stateidp->seqid > lyp->nfsly_stateid.seqid)
5362
lyp->nfsly_stateid.seqid = stateidp->seqid;
5363
5364
/* Merge the new list of File Layouts into the list. */
5365
flp = LIST_FIRST(fhlp);
5366
if (flp != NULL) {
5367
if (flp->nfsfl_iomode == NFSLAYOUTIOMODE_READ)
5368
nfscl_mergeflayouts(&lyp->nfsly_flayread, fhlp);
5369
else
5370
nfscl_mergeflayouts(&lyp->nfsly_flayrw, fhlp);
5371
}
5372
if (layout_passed_in != 0)
5373
nfsv4_unlock(&lyp->nfsly_lock, 1);
5374
NFSUNLOCKCLSTATE();
5375
if (tlyp != NULL)
5376
free(tlyp, M_NFSLAYOUT);
5377
return (0);
5378
}
5379
5380
/*
5381
* Search for a layout by MDS file handle.
5382
* If one is found, it is returned with a refcnt (shared lock) iff
5383
* retflpp returned non-NULL and locked (exclusive locked) iff retflpp is
5384
* returned NULL.
5385
*/
5386
struct nfscllayout *
5387
nfscl_getlayout(struct nfsclclient *clp, uint8_t *fhp, int fhlen,
5388
uint64_t off, uint32_t rwaccess, struct nfsclflayout **retflpp,
5389
int *recalledp)
5390
{
5391
struct nfscllayout *lyp;
5392
mount_t mp;
5393
int error, igotlock;
5394
5395
mp = clp->nfsc_nmp->nm_mountp;
5396
*recalledp = 0;
5397
*retflpp = NULL;
5398
NFSLOCKCLSTATE();
5399
lyp = nfscl_findlayout(clp, fhp, fhlen);
5400
if (lyp != NULL) {
5401
if ((lyp->nfsly_flags & NFSLY_RECALL) == 0) {
5402
TAILQ_REMOVE(&clp->nfsc_layout, lyp, nfsly_list);
5403
TAILQ_INSERT_HEAD(&clp->nfsc_layout, lyp, nfsly_list);
5404
lyp->nfsly_timestamp = NFSD_MONOSEC + 120;
5405
error = nfscl_findlayoutforio(lyp, off, rwaccess,
5406
retflpp);
5407
if (error == 0)
5408
nfsv4_getref(&lyp->nfsly_lock, NULL,
5409
NFSCLSTATEMUTEXPTR, mp);
5410
else {
5411
do {
5412
igotlock = nfsv4_lock(&lyp->nfsly_lock,
5413
1, NULL, NFSCLSTATEMUTEXPTR, mp);
5414
} while (igotlock == 0 && !NFSCL_FORCEDISM(mp));
5415
*retflpp = NULL;
5416
}
5417
if (NFSCL_FORCEDISM(mp)) {
5418
lyp = NULL;
5419
*recalledp = 1;
5420
}
5421
} else {
5422
lyp = NULL;
5423
*recalledp = 1;
5424
}
5425
}
5426
NFSUNLOCKCLSTATE();
5427
return (lyp);
5428
}
5429
5430
/*
5431
* Search for a layout by MDS file handle. If one is found, mark in to be
5432
* recalled, if it already marked "return on close".
5433
*/
5434
static void
5435
nfscl_retoncloselayout(vnode_t vp, struct nfsclclient *clp, uint8_t *fhp,
5436
int fhlen, struct nfsclrecalllayout **recallpp, struct nfscllayout **lypp)
5437
{
5438
struct nfscllayout *lyp;
5439
uint32_t iomode;
5440
5441
*lypp = NULL;
5442
if (vp->v_type != VREG || !NFSHASPNFS(VFSTONFS(vp->v_mount)) ||
5443
nfscl_enablecallb == 0 || nfs_numnfscbd == 0 ||
5444
(VTONFS(vp)->n_flag & NNOLAYOUT) != 0)
5445
return;
5446
lyp = nfscl_findlayout(clp, fhp, fhlen);
5447
if (lyp != NULL && (lyp->nfsly_flags & NFSLY_RETONCLOSE) != 0) {
5448
if ((lyp->nfsly_flags & NFSLY_RECALL) == 0) {
5449
iomode = 0;
5450
if (!LIST_EMPTY(&lyp->nfsly_flayread))
5451
iomode |= NFSLAYOUTIOMODE_READ;
5452
if (!LIST_EMPTY(&lyp->nfsly_flayrw))
5453
iomode |= NFSLAYOUTIOMODE_RW;
5454
nfscl_layoutrecall(NFSLAYOUTRETURN_FILE, lyp, iomode,
5455
0, UINT64_MAX, lyp->nfsly_stateid.seqid, 0, 0, NULL,
5456
*recallpp);
5457
NFSCL_DEBUG(4, "retoncls recall iomode=%d\n", iomode);
5458
*recallpp = NULL;
5459
}
5460
5461
/* Now, wake up renew thread to do LayoutReturn. */
5462
wakeup(clp);
5463
*lypp = lyp;
5464
}
5465
}
5466
5467
/*
5468
* Mark the layout to be recalled and with an error.
5469
* Also, disable the dsp from further use.
5470
*/
5471
void
5472
nfscl_dserr(uint32_t op, uint32_t stat, struct nfscldevinfo *dp,
5473
struct nfscllayout *lyp, struct nfsclds *dsp)
5474
{
5475
struct nfsclrecalllayout *recallp;
5476
uint32_t iomode;
5477
5478
printf("DS being disabled, error=%d\n", stat);
5479
/* Set up the return of the layout. */
5480
recallp = malloc(sizeof(*recallp), M_NFSLAYRECALL, M_WAITOK);
5481
iomode = 0;
5482
NFSLOCKCLSTATE();
5483
if ((lyp->nfsly_flags & NFSLY_RECALL) == 0) {
5484
if (!LIST_EMPTY(&lyp->nfsly_flayread))
5485
iomode |= NFSLAYOUTIOMODE_READ;
5486
if (!LIST_EMPTY(&lyp->nfsly_flayrw))
5487
iomode |= NFSLAYOUTIOMODE_RW;
5488
(void)nfscl_layoutrecall(NFSLAYOUTRETURN_FILE, lyp, iomode,
5489
0, UINT64_MAX, lyp->nfsly_stateid.seqid, stat, op,
5490
dp->nfsdi_deviceid, recallp);
5491
NFSUNLOCKCLSTATE();
5492
NFSCL_DEBUG(4, "nfscl_dserr recall iomode=%d\n", iomode);
5493
} else {
5494
NFSUNLOCKCLSTATE();
5495
free(recallp, M_NFSLAYRECALL);
5496
}
5497
5498
/* And shut the TCP connection down. */
5499
nfscl_cancelreqs(dsp);
5500
}
5501
5502
/*
5503
* Cancel all RPCs for this "dsp" by closing the connection.
5504
* Also, mark the session as defunct.
5505
* If NFSCLDS_SAMECONN is set, the connection is shared with other DSs and
5506
* cannot be shut down.
5507
*/
5508
void
5509
nfscl_cancelreqs(struct nfsclds *dsp)
5510
{
5511
struct __rpc_client *cl;
5512
static int non_event;
5513
5514
NFSLOCKDS(dsp);
5515
if ((dsp->nfsclds_flags & (NFSCLDS_CLOSED | NFSCLDS_SAMECONN)) == 0 &&
5516
dsp->nfsclds_sockp != NULL &&
5517
dsp->nfsclds_sockp->nr_client != NULL) {
5518
dsp->nfsclds_flags |= NFSCLDS_CLOSED;
5519
cl = dsp->nfsclds_sockp->nr_client;
5520
dsp->nfsclds_sess.nfsess_defunct = 1;
5521
NFSUNLOCKDS(dsp);
5522
CLNT_CLOSE(cl);
5523
/*
5524
* This 1sec sleep is done to reduce the number of reconnect
5525
* attempts made on the DS while it has failed.
5526
*/
5527
tsleep(&non_event, PVFS, "ndscls", hz);
5528
return;
5529
}
5530
NFSUNLOCKDS(dsp);
5531
}
5532
5533
/*
5534
* Dereference a layout.
5535
*/
5536
void
5537
nfscl_rellayout(struct nfscllayout *lyp, int exclocked)
5538
{
5539
5540
NFSLOCKCLSTATE();
5541
if (exclocked != 0)
5542
nfsv4_unlock(&lyp->nfsly_lock, 0);
5543
else
5544
nfsv4_relref(&lyp->nfsly_lock);
5545
NFSUNLOCKCLSTATE();
5546
}
5547
5548
/*
5549
* Search for a devinfo by deviceid. If one is found, return it after
5550
* acquiring a reference count on it.
5551
*/
5552
struct nfscldevinfo *
5553
nfscl_getdevinfo(struct nfsclclient *clp, uint8_t *deviceid,
5554
struct nfscldevinfo *dip)
5555
{
5556
5557
NFSLOCKCLSTATE();
5558
if (dip == NULL)
5559
dip = nfscl_finddevinfo(clp, deviceid);
5560
if (dip != NULL)
5561
dip->nfsdi_refcnt++;
5562
NFSUNLOCKCLSTATE();
5563
return (dip);
5564
}
5565
5566
/*
5567
* Dereference a devinfo structure.
5568
*/
5569
static void
5570
nfscl_reldevinfo_locked(struct nfscldevinfo *dip)
5571
{
5572
5573
dip->nfsdi_refcnt--;
5574
if (dip->nfsdi_refcnt == 0)
5575
wakeup(&dip->nfsdi_refcnt);
5576
}
5577
5578
/*
5579
* Dereference a devinfo structure.
5580
*/
5581
void
5582
nfscl_reldevinfo(struct nfscldevinfo *dip)
5583
{
5584
5585
NFSLOCKCLSTATE();
5586
nfscl_reldevinfo_locked(dip);
5587
NFSUNLOCKCLSTATE();
5588
}
5589
5590
/*
5591
* Find a layout for this file handle. Return NULL upon failure.
5592
*/
5593
static struct nfscllayout *
5594
nfscl_findlayout(struct nfsclclient *clp, u_int8_t *fhp, int fhlen)
5595
{
5596
struct nfscllayout *lyp;
5597
5598
LIST_FOREACH(lyp, NFSCLLAYOUTHASH(clp, fhp, fhlen), nfsly_hash)
5599
if (lyp->nfsly_fhlen == fhlen &&
5600
!NFSBCMP(lyp->nfsly_fh, fhp, fhlen))
5601
break;
5602
return (lyp);
5603
}
5604
5605
/*
5606
* Find a devinfo for this deviceid. Return NULL upon failure.
5607
*/
5608
static struct nfscldevinfo *
5609
nfscl_finddevinfo(struct nfsclclient *clp, uint8_t *deviceid)
5610
{
5611
struct nfscldevinfo *dip;
5612
5613
LIST_FOREACH(dip, &clp->nfsc_devinfo, nfsdi_list)
5614
if (NFSBCMP(dip->nfsdi_deviceid, deviceid, NFSX_V4DEVICEID)
5615
== 0)
5616
break;
5617
return (dip);
5618
}
5619
5620
/*
5621
* Merge the new file layout list into the main one, maintaining it in
5622
* increasing offset order.
5623
*/
5624
static void
5625
nfscl_mergeflayouts(struct nfsclflayouthead *fhlp,
5626
struct nfsclflayouthead *newfhlp)
5627
{
5628
struct nfsclflayout *flp, *nflp, *prevflp, *tflp;
5629
5630
flp = LIST_FIRST(fhlp);
5631
prevflp = NULL;
5632
LIST_FOREACH_SAFE(nflp, newfhlp, nfsfl_list, tflp) {
5633
while (flp != NULL && flp->nfsfl_off < nflp->nfsfl_off) {
5634
prevflp = flp;
5635
flp = LIST_NEXT(flp, nfsfl_list);
5636
}
5637
if (prevflp == NULL)
5638
LIST_INSERT_HEAD(fhlp, nflp, nfsfl_list);
5639
else
5640
LIST_INSERT_AFTER(prevflp, nflp, nfsfl_list);
5641
prevflp = nflp;
5642
}
5643
}
5644
5645
/*
5646
* Add this nfscldevinfo to the client, if it doesn't already exist.
5647
* This function consumes the structure pointed at by dip, if not NULL.
5648
*/
5649
int
5650
nfscl_adddevinfo(struct nfsmount *nmp, struct nfscldevinfo *dip, int ind,
5651
struct nfsclflayout *flp)
5652
{
5653
struct nfsclclient *clp;
5654
struct nfscldevinfo *tdip;
5655
uint8_t *dev;
5656
5657
NFSLOCKCLSTATE();
5658
clp = nmp->nm_clp;
5659
if (clp == NULL) {
5660
NFSUNLOCKCLSTATE();
5661
if (dip != NULL)
5662
free(dip, M_NFSDEVINFO);
5663
return (ENODEV);
5664
}
5665
if ((flp->nfsfl_flags & NFSFL_FILE) != 0)
5666
dev = flp->nfsfl_dev;
5667
else
5668
dev = flp->nfsfl_ffm[ind].dev;
5669
tdip = nfscl_finddevinfo(clp, dev);
5670
if (tdip != NULL) {
5671
tdip->nfsdi_layoutrefs++;
5672
if ((flp->nfsfl_flags & NFSFL_FILE) != 0)
5673
flp->nfsfl_devp = tdip;
5674
else
5675
flp->nfsfl_ffm[ind].devp = tdip;
5676
nfscl_reldevinfo_locked(tdip);
5677
NFSUNLOCKCLSTATE();
5678
if (dip != NULL)
5679
free(dip, M_NFSDEVINFO);
5680
return (0);
5681
}
5682
if (dip != NULL) {
5683
LIST_INSERT_HEAD(&clp->nfsc_devinfo, dip, nfsdi_list);
5684
dip->nfsdi_layoutrefs = 1;
5685
if ((flp->nfsfl_flags & NFSFL_FILE) != 0)
5686
flp->nfsfl_devp = dip;
5687
else
5688
flp->nfsfl_ffm[ind].devp = dip;
5689
}
5690
NFSUNLOCKCLSTATE();
5691
if (dip == NULL)
5692
return (ENODEV);
5693
return (0);
5694
}
5695
5696
/*
5697
* Free up a layout structure and associated file layout structure(s).
5698
*/
5699
void
5700
nfscl_freelayout(struct nfscllayout *layp)
5701
{
5702
struct nfsclflayout *flp, *nflp;
5703
struct nfsclrecalllayout *rp, *nrp;
5704
5705
LIST_FOREACH_SAFE(flp, &layp->nfsly_flayread, nfsfl_list, nflp) {
5706
LIST_REMOVE(flp, nfsfl_list);
5707
nfscl_freeflayout(flp);
5708
}
5709
LIST_FOREACH_SAFE(flp, &layp->nfsly_flayrw, nfsfl_list, nflp) {
5710
LIST_REMOVE(flp, nfsfl_list);
5711
nfscl_freeflayout(flp);
5712
}
5713
LIST_FOREACH_SAFE(rp, &layp->nfsly_recall, nfsrecly_list, nrp) {
5714
LIST_REMOVE(rp, nfsrecly_list);
5715
free(rp, M_NFSLAYRECALL);
5716
}
5717
layp->nfsly_clp->nfsc_layoutcnt--;
5718
nfsstatsv1.cllayouts--;
5719
free(layp, M_NFSLAYOUT);
5720
}
5721
5722
/*
5723
* Free up a file layout structure.
5724
*/
5725
void
5726
nfscl_freeflayout(struct nfsclflayout *flp)
5727
{
5728
int i, j;
5729
5730
if ((flp->nfsfl_flags & NFSFL_FILE) != 0) {
5731
for (i = 0; i < flp->nfsfl_fhcnt; i++)
5732
free(flp->nfsfl_fh[i], M_NFSFH);
5733
if (flp->nfsfl_devp != NULL)
5734
flp->nfsfl_devp->nfsdi_layoutrefs--;
5735
}
5736
if ((flp->nfsfl_flags & NFSFL_FLEXFILE) != 0)
5737
for (i = 0; i < flp->nfsfl_mirrorcnt; i++) {
5738
for (j = 0; j < flp->nfsfl_ffm[i].fhcnt; j++)
5739
free(flp->nfsfl_ffm[i].fh[j], M_NFSFH);
5740
if (flp->nfsfl_ffm[i].devp != NULL)
5741
flp->nfsfl_ffm[i].devp->nfsdi_layoutrefs--;
5742
}
5743
free(flp, M_NFSFLAYOUT);
5744
}
5745
5746
/*
5747
* Free up a file layout devinfo structure.
5748
*/
5749
void
5750
nfscl_freedevinfo(struct nfscldevinfo *dip)
5751
{
5752
5753
free(dip, M_NFSDEVINFO);
5754
}
5755
5756
/*
5757
* Mark any layouts that match as recalled.
5758
*/
5759
static int
5760
nfscl_layoutrecall(int recalltype, struct nfscllayout *lyp, uint32_t iomode,
5761
uint64_t off, uint64_t len, uint32_t stateseqid, uint32_t stat, uint32_t op,
5762
char *devid, struct nfsclrecalllayout *recallp)
5763
{
5764
struct nfsclrecalllayout *rp, *orp;
5765
5766
recallp->nfsrecly_recalltype = recalltype;
5767
recallp->nfsrecly_iomode = iomode;
5768
recallp->nfsrecly_stateseqid = stateseqid;
5769
recallp->nfsrecly_off = off;
5770
recallp->nfsrecly_len = len;
5771
recallp->nfsrecly_stat = stat;
5772
recallp->nfsrecly_op = op;
5773
if (devid != NULL)
5774
NFSBCOPY(devid, recallp->nfsrecly_devid, NFSX_V4DEVICEID);
5775
/*
5776
* Order the list as file returns first, followed by fsid and any
5777
* returns, both in increasing stateseqid order.
5778
* Note that the seqids wrap around, so 1 is after 0xffffffff.
5779
* (I'm not sure this is correct because I find RFC5661 confusing
5780
* on this, but hopefully it will work ok.)
5781
*/
5782
orp = NULL;
5783
LIST_FOREACH(rp, &lyp->nfsly_recall, nfsrecly_list) {
5784
orp = rp;
5785
if ((recalltype == NFSLAYOUTRETURN_FILE &&
5786
(rp->nfsrecly_recalltype != NFSLAYOUTRETURN_FILE ||
5787
nfscl_seq(stateseqid, rp->nfsrecly_stateseqid) != 0)) ||
5788
(recalltype != NFSLAYOUTRETURN_FILE &&
5789
rp->nfsrecly_recalltype != NFSLAYOUTRETURN_FILE &&
5790
nfscl_seq(stateseqid, rp->nfsrecly_stateseqid) != 0)) {
5791
LIST_INSERT_BEFORE(rp, recallp, nfsrecly_list);
5792
break;
5793
}
5794
5795
/*
5796
* Put any error return on all the file returns that will
5797
* preceed this one.
5798
*/
5799
if (rp->nfsrecly_recalltype == NFSLAYOUTRETURN_FILE &&
5800
stat != 0 && rp->nfsrecly_stat == 0) {
5801
rp->nfsrecly_stat = stat;
5802
rp->nfsrecly_op = op;
5803
if (devid != NULL)
5804
NFSBCOPY(devid, rp->nfsrecly_devid,
5805
NFSX_V4DEVICEID);
5806
}
5807
}
5808
if (rp == NULL) {
5809
if (orp == NULL)
5810
LIST_INSERT_HEAD(&lyp->nfsly_recall, recallp,
5811
nfsrecly_list);
5812
else
5813
LIST_INSERT_AFTER(orp, recallp, nfsrecly_list);
5814
}
5815
lyp->nfsly_flags |= NFSLY_RECALL;
5816
wakeup(lyp->nfsly_clp);
5817
return (0);
5818
}
5819
5820
/*
5821
* Compare the two seqids for ordering. The trick is that the seqids can
5822
* wrap around from 0xffffffff->0, so check for the cases where one
5823
* has wrapped around.
5824
* Return 1 if seqid1 comes before seqid2, 0 otherwise.
5825
*/
5826
static int
5827
nfscl_seq(uint32_t seqid1, uint32_t seqid2)
5828
{
5829
5830
if (seqid2 > seqid1 && (seqid2 - seqid1) >= 0x7fffffff)
5831
/* seqid2 has wrapped around. */
5832
return (0);
5833
if (seqid1 > seqid2 && (seqid1 - seqid2) >= 0x7fffffff)
5834
/* seqid1 has wrapped around. */
5835
return (1);
5836
if (seqid1 <= seqid2)
5837
return (1);
5838
return (0);
5839
}
5840
5841
/*
5842
* Do a layout return for each of the recalls.
5843
*/
5844
static void
5845
nfscl_layoutreturn(struct nfsmount *nmp, struct nfscllayout *lyp,
5846
struct ucred *cred, NFSPROC_T *p)
5847
{
5848
struct nfsclrecalllayout *rp;
5849
nfsv4stateid_t stateid;
5850
int layouttype;
5851
5852
NFSBCOPY(lyp->nfsly_stateid.other, stateid.other, NFSX_STATEIDOTHER);
5853
stateid.seqid = lyp->nfsly_stateid.seqid;
5854
if ((lyp->nfsly_flags & NFSLY_FILES) != 0)
5855
layouttype = NFSLAYOUT_NFSV4_1_FILES;
5856
else
5857
layouttype = NFSLAYOUT_FLEXFILE;
5858
LIST_FOREACH(rp, &lyp->nfsly_recall, nfsrecly_list) {
5859
(void)nfsrpc_layoutreturn(nmp, lyp->nfsly_fh,
5860
lyp->nfsly_fhlen, 0, layouttype,
5861
rp->nfsrecly_iomode, rp->nfsrecly_recalltype,
5862
rp->nfsrecly_off, rp->nfsrecly_len,
5863
&stateid, cred, p, rp->nfsrecly_stat, rp->nfsrecly_op,
5864
rp->nfsrecly_devid);
5865
}
5866
}
5867
5868
/*
5869
* Do the layout commit for a file layout.
5870
*/
5871
static void
5872
nfscl_dolayoutcommit(struct nfsmount *nmp, struct nfscllayout *lyp,
5873
struct ucred *cred, NFSPROC_T *p)
5874
{
5875
struct nfsclflayout *flp;
5876
uint64_t len;
5877
int error, layouttype;
5878
5879
if ((lyp->nfsly_flags & NFSLY_FILES) != 0)
5880
layouttype = NFSLAYOUT_NFSV4_1_FILES;
5881
else
5882
layouttype = NFSLAYOUT_FLEXFILE;
5883
LIST_FOREACH(flp, &lyp->nfsly_flayrw, nfsfl_list) {
5884
if (layouttype == NFSLAYOUT_FLEXFILE &&
5885
(flp->nfsfl_fflags & NFSFLEXFLAG_NO_LAYOUTCOMMIT) != 0) {
5886
NFSCL_DEBUG(4, "Flex file: no layoutcommit\n");
5887
/* If not supported, don't bother doing it. */
5888
NFSLOCKMNT(nmp);
5889
nmp->nm_state |= NFSSTA_NOLAYOUTCOMMIT;
5890
NFSUNLOCKMNT(nmp);
5891
break;
5892
} else if (flp->nfsfl_off <= lyp->nfsly_lastbyte) {
5893
len = flp->nfsfl_end - flp->nfsfl_off;
5894
error = nfsrpc_layoutcommit(nmp, lyp->nfsly_fh,
5895
lyp->nfsly_fhlen, 0, flp->nfsfl_off, len,
5896
lyp->nfsly_lastbyte, &lyp->nfsly_stateid,
5897
layouttype, cred, p);
5898
NFSCL_DEBUG(4, "layoutcommit err=%d\n", error);
5899
if (error == NFSERR_NOTSUPP) {
5900
/* If not supported, don't bother doing it. */
5901
NFSLOCKMNT(nmp);
5902
nmp->nm_state |= NFSSTA_NOLAYOUTCOMMIT;
5903
NFSUNLOCKMNT(nmp);
5904
break;
5905
}
5906
}
5907
}
5908
}
5909
5910
/*
5911
* Commit all layouts for a file (vnode).
5912
*/
5913
int
5914
nfscl_layoutcommit(vnode_t vp, NFSPROC_T *p)
5915
{
5916
struct nfsclclient *clp;
5917
struct nfscllayout *lyp;
5918
struct nfsnode *np = VTONFS(vp);
5919
mount_t mp;
5920
struct nfsmount *nmp;
5921
5922
mp = vp->v_mount;
5923
nmp = VFSTONFS(mp);
5924
if (NFSHASNOLAYOUTCOMMIT(nmp))
5925
return (0);
5926
NFSLOCKCLSTATE();
5927
clp = nmp->nm_clp;
5928
if (clp == NULL) {
5929
NFSUNLOCKCLSTATE();
5930
return (EPERM);
5931
}
5932
lyp = nfscl_findlayout(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
5933
if (lyp == NULL) {
5934
NFSUNLOCKCLSTATE();
5935
return (EPERM);
5936
}
5937
nfsv4_getref(&lyp->nfsly_lock, NULL, NFSCLSTATEMUTEXPTR, mp);
5938
if (NFSCL_FORCEDISM(mp)) {
5939
NFSUNLOCKCLSTATE();
5940
return (EPERM);
5941
}
5942
tryagain:
5943
if ((lyp->nfsly_flags & NFSLY_WRITTEN) != 0) {
5944
lyp->nfsly_flags &= ~NFSLY_WRITTEN;
5945
NFSUNLOCKCLSTATE();
5946
NFSCL_DEBUG(4, "do layoutcommit2\n");
5947
nfscl_dolayoutcommit(clp->nfsc_nmp, lyp, NFSPROCCRED(p), p);
5948
NFSLOCKCLSTATE();
5949
goto tryagain;
5950
}
5951
nfsv4_relref(&lyp->nfsly_lock);
5952
NFSUNLOCKCLSTATE();
5953
return (0);
5954
}
5955
5956
/*
5957
* Check access against a delegation ace.
5958
* Return EINVAL for any case where the check cannot be completed.
5959
*/
5960
int
5961
nfscl_delegacecheck(struct vnode *vp, accmode_t accmode, struct ucred *cred)
5962
{
5963
struct nfsclclient *clp;
5964
struct nfscldeleg *dp;
5965
struct nfsnode *np;
5966
struct nfsmount *nmp;
5967
struct acl *aclp;
5968
int error;
5969
5970
np = VTONFS(vp);
5971
nmp = VFSTONFS(vp->v_mount);
5972
if (!NFSHASNFSV4(nmp) || !NFSHASNFSV4N(nmp) || vp->v_type != VREG)
5973
return (EINVAL);
5974
NFSLOCKMNT(nmp);
5975
if ((nmp->nm_privflag & NFSMNTP_DELEGISSUED) == 0) {
5976
NFSUNLOCKMNT(nmp);
5977
return (EINVAL);
5978
}
5979
NFSUNLOCKMNT(nmp);
5980
aclp = acl_alloc(M_WAITOK);
5981
NFSLOCKCLSTATE();
5982
clp = nfscl_findcl(nmp);
5983
if (clp == NULL) {
5984
NFSUNLOCKCLSTATE();
5985
acl_free(aclp);
5986
return (EINVAL);
5987
}
5988
dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
5989
if (dp != NULL && (dp->nfsdl_flags & (NFSCLDL_RECALL |
5990
NFSCLDL_DELEGRET)) == 0) {
5991
memcpy(&aclp->acl_entry[0], &dp->nfsdl_ace,
5992
sizeof(struct acl_entry));
5993
NFSUNLOCKCLSTATE();
5994
aclp->acl_cnt = 1;
5995
error = vaccess_acl_nfs4(vp->v_type, np->n_vattr.na_uid,
5996
np->n_vattr.na_gid, aclp, accmode, cred);
5997
acl_free(aclp);
5998
if (error == 0 || error == EACCES)
5999
return (error);
6000
} else {
6001
NFSUNLOCKCLSTATE();
6002
acl_free(aclp);
6003
}
6004
return (EINVAL);
6005
}
6006
6007
/*
6008
* Start the recall of a delegation. Called for CB_RECALL and REMOVE
6009
* when nlink == 0 after the REMOVE.
6010
*/
6011
void nfscl_startdelegrecall(struct nfsclclient *clp, struct nfsfh *nfhp)
6012
{
6013
struct nfscldeleg *dp;
6014
6015
dp = nfscl_finddeleg(clp, nfhp->nfh_fh, nfhp->nfh_len);
6016
if (dp != NULL && (dp->nfsdl_flags & NFSCLDL_DELEGRET) == 0) {
6017
dp->nfsdl_flags |= NFSCLDL_RECALL;
6018
wakeup((caddr_t)clp);
6019
}
6020
}
6021
6022