xref: /original-bsd/sys/nfs/nfs_node.c (revision c0f053f7)
1 /*
2  * Copyright (c) 1989 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Rick Macklem at The University of Guelph.
7  *
8  * Redistribution and use in source and binary forms are permitted
9  * provided that the above copyright notice and this paragraph are
10  * duplicated in all such forms and that any documentation,
11  * advertising materials, and other materials related to such
12  * distribution and use acknowledge that the software was developed
13  * by the University of California, Berkeley.  The name of the
14  * University may not be used to endorse or promote products derived
15  * from this software without specific prior written permission.
16  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
19  *
20  *	@(#)nfs_node.c	7.3 (Berkeley) 07/06/89
21  */
22 
23 #include "param.h"
24 #include "systm.h"
25 #include "user.h"
26 #include "proc.h"
27 #include "mount.h"
28 #include "vnode.h"
29 #include "../ufs/dir.h"
30 #include "namei.h"
31 #include "errno.h"
32 #include "nfsv2.h"
33 #include "nfs.h"
34 #include "nfsnode.h"
35 #include "nfsmount.h"
36 #include "kernel.h"
37 #include "malloc.h"
38 
39 /* The request list head */
40 extern struct nfsreq nfsreqh;
41 
42 #define	NFSNOHSZ	512
43 #if	((NFSNOHSZ&(NFSNOHSZ-1)) == 0)
44 #define	NFSNOHASH(fhsum)	((fhsum)&(NFSNOHSZ-1))
45 #else
46 #define	NFSNOHASH(fhsum)	(((unsigned)(fhsum))%NFSNOHSZ)
47 #endif
48 
49 union nhead {				/* inode LRU cache, Chris Maltby */
50 	union  nhead *nh_head[2];
51 	struct nfsnode *nh_chain[2];
52 } nhead[NFSNOHSZ];
53 
54 struct nfsnode *nfreeh, **nfreet;
55 
56 /*
57  * Initialize hash links for nfsnodes
58  * and build nfsnode free list.
59  */
60 nfs_nhinit()
61 {
62 	register int i;
63 	register struct nfsnode *np = nfsnode;
64 	register union  nhead *nh = nhead;
65 
66 	for (i = NFSNOHSZ; --i >= 0; nh++) {
67 		nh->nh_head[0] = nh;
68 		nh->nh_head[1] = nh;
69 	}
70 	nfreeh = np;
71 	nfreet = &np->n_freef;
72 	np->n_freeb = &nfreeh;
73 	np->n_forw = np;
74 	np->n_back = np;
75 	NFSTOV(np)->v_data = (qaddr_t)np;
76 	for (i = nnfsnode; --i > 0; ) {
77 		++np;
78 		np->n_forw = np;
79 		np->n_back = np;
80 		NFSTOV(np)->v_data = (qaddr_t)np;
81 		*nfreet = np;
82 		np->n_freeb = nfreet;
83 		nfreet = &np->n_freef;
84 	}
85 	np->n_freef = NULL;
86 }
87 
88 /*
89  * Look up an vnode/nfsnode by file handle.
90  * Callers must check for mount points!!
91  * In all cases, a pointer to a
92  * nfsnode structure is returned.
93  */
94 nfs_nget(mntp, fhp, npp)
95 	struct mount *mntp;
96 	register nfsv2fh_t *fhp;
97 	struct nfsnode **npp;
98 {
99 	register struct nfsnode *np;
100 	register struct vnode *vp;
101 	register struct nfsnode *nq;
102 	register u_char *fhpp;
103 	register u_long fhsum;
104 	register int i;
105 	union  nhead *nh;
106 	int error;
107 
108 	fhpp = &fhp->fh_bytes[0];
109 	fhsum = 0;
110 	for (i = 0; i < NFSX_FH; i++)
111 		fhsum += *fhpp++;
112 loop:
113 	nh = &nhead[NFSNOHASH(fhsum)];
114 	for (np = nh->nh_chain[0]; np != (struct nfsnode *)nh; np = np->n_forw)
115 		if (mntp == NFSTOV(np)->v_mount &&
116 		    !bcmp((caddr_t)fhp, (caddr_t)&np->n_fh, NFSX_FH)) {
117 			/*
118 			 * Following is essentially an inline expanded
119 			 * copy of ngrab(), expanded inline for speed,
120 			 * and so that the test for a mounted on nfsnode
121 			 * can be deferred until after we are sure that
122 			 * the nfsnode isn't busy.
123 			 */
124 			if ((np->n_flag & NLOCKED) != 0) {
125 				np->n_flag |= NWANT;
126 				sleep((caddr_t)np, PINOD);
127 				goto loop;
128 			}
129 			vp = NFSTOV(np);
130 			if (vp->v_count == 0) {		/* nfsno on free list */
131 				if (nq = np->n_freef)
132 					nq->n_freeb = np->n_freeb;
133 				else
134 					nfreet = np->n_freeb;
135 				*np->n_freeb = nq;
136 				np->n_freef = NULL;
137 				np->n_freeb = NULL;
138 			}
139 			np->n_flag |= NLOCKED;
140 			VREF(vp);
141 			*npp = np;
142 			return(0);
143 		}
144 
145 	if ((np = nfreeh) == NULL) {
146 		tablefull("nfsnode");
147 		*npp = 0;
148 		return(ENFILE);
149 	}
150 	vp = NFSTOV(np);
151 	if (vp->v_count)
152 		panic("free nfsnode isn't");
153 	if (nq = np->n_freef)
154 		nq->n_freeb = &nfreeh;
155 	nfreeh = nq;
156 	np->n_freef = NULL;
157 	np->n_freeb = NULL;
158 	/*
159 	 * Now to take nfsnode off the hash chain it was on
160 	 * (initially, or after an nflush, it is on a "hash chain"
161 	 * consisting entirely of itself, and pointed to by no-one,
162 	 * but that doesn't matter), and put it on the chain for
163 	 * its new file handle
164 	 */
165 	remque(np);
166 	insque(np, nh);
167 	bcopy((caddr_t)fhp, (caddr_t)&np->n_fh, NFSX_FH);
168 #ifdef notyet
169 	cache_purge(vp);
170 #endif
171 	np->n_flag = NLOCKED;
172 	np->n_attrstamp = 0;
173 	np->n_sillyrename = (struct sillyrename *)0;
174 	np->n_id = ++nextnfsnodeid;
175 	/*
176 	 * Initialize the associated vnode
177 	 */
178 	vinit(vp, mntp, VNON, &nfsv2_vnodeops);
179 	*npp = np;
180 	return (0);
181 }
182 
183 /*
184  * Convert a pointer to an nfsnode into a reference to an nfsnode.
185  *
186  * This is basically the internal piece of nget (after the
187  * nfsnode pointer is located) but without the test for mounted
188  * filesystems.  It is caller's responsibility to check that
189  * the nfsnode pointer is valid.
190  */
191 nfs_ngrab(np)
192 	register struct nfsnode *np;
193 {
194 	register struct vnode *vp = NFSTOV(np);
195 
196 	while ((np->n_flag & NLOCKED) != 0) {
197 		np->n_flag |= NWANT;
198 		sleep((caddr_t)np, PINOD);
199 	}
200 	if (vp->v_count == 0) {		/* ino on free list */
201 		register struct nfsnode *nq;
202 
203 		if (nq = np->n_freef)
204 			nq->n_freeb = np->n_freeb;
205 		else
206 			nfreet = np->n_freeb;
207 		*np->n_freeb = nq;
208 		np->n_freef = NULL;
209 		np->n_freeb = NULL;
210 	}
211 	VREF(vp);
212 	np->n_flag |= NLOCKED;
213 }
214 
215 nfs_inactive(vp)
216 	struct vnode *vp;
217 {
218 	register struct nfsnode *np;
219 	register struct nameidata *ndp;
220 	register struct sillyrename *sp;
221 	register struct nfsreq *rep;
222 	struct nfsreq *rep2;
223 	struct nfsnode *dnp;
224 	int s;
225 
226 	if (vp == NULL)
227 		panic("nfs_inactive NULL vp");
228 	if (vp->v_count == 0) {
229 		np = VTONFS(vp);
230 		np->n_flag |= NLOCKED;
231 		if (np->n_sillyrename) {
232 			/*
233 			 * Remove the silly file that was rename'd earlier
234 			 */
235 			sp = np->n_sillyrename;
236 			ndp = &sp->s_namei;
237 			if (!nfs_nget(vp->v_mount, &sp->s_fh, &dnp)) {
238 				ndp->ni_dvp = NFSTOV(dnp);
239 				if (sp->s_flag == REMOVE)
240 					nfs_removeit(ndp);
241 				else
242 					nfs_rmdirit(ndp);
243 				nfs_nput(ndp->ni_dvp);
244 			}
245 			crfree(ndp->ni_cred);
246 			free((caddr_t)sp, M_TEMP);
247 			np->n_sillyrename = (struct sillyrename *)0;
248 		}
249 		nfs_unlock(vp);
250 		np->n_flag = 0;
251 		/*
252 		 * Scan the request list for any requests left hanging about
253 		 */
254 		s = splnet();
255 		rep = nfsreqh.r_next;
256 		while (rep) {
257 			if (rep->r_vp == vp) {
258 				rep->r_prev->r_next = rep2 = rep->r_next;
259 				if (rep->r_next != NULL)
260 					rep->r_next->r_prev = rep->r_prev;
261 				m_freem(rep->r_mreq);
262 				if (rep->r_mrep != NULL)
263 					m_freem(rep->r_mrep);
264 				free((caddr_t)rep, M_NFSREQ);
265 				rep = rep2;
266 			} else
267 				rep = rep->r_next;
268 		}
269 		splx(s);
270 		/*
271 		 * Put the nfsnode on the end of the free list.
272 		 */
273 		if (nfreeh) {
274 			*nfreet = np;
275 			np->n_freeb = nfreet;
276 		} else {
277 			nfreeh = np;
278 			np->n_freeb = &nfreeh;
279 		}
280 		np->n_freef = NULL;
281 		nfreet = &np->n_freef;
282 	}
283 	return (0);
284 }
285 
286 /*
287  * Remove any nfsnodes in the nfsnode cache belonging to mount.
288  *
289  * There should not be any active ones, return error if any are found
290  * (nb: this is a user error, not a system err).
291  */
292 nfs_nflush(mntp)
293 	struct mount *mntp;
294 {
295 	register struct nfsnode *np;
296 	register struct vnode *vp;
297 
298 	for (np = nfsnode; np < nfsnodeNNFSNODE; np++) {
299 		vp = NFSTOV(np);
300 		if (vp->v_mount == mntp)
301 			if (vp->v_count)
302 				return (EBUSY);
303 			else {
304 				remque(np);
305 				np->n_forw = np;
306 				np->n_back = np;
307 				/*
308 				 * as v_count == 0, the inode was on the free
309 				 * list already, just leave it there, it will
310 				 * fall off the bottom eventually. We could
311 				 * perhaps move it to the head of the free
312 				 * list, but as umounts are done so
313 				 * infrequently, we would gain very little,
314 				 * while making the code bigger.
315 				 */
316 			}
317 	}
318 	return (0);
319 }
320 
321 /*
322  * Lock an nfsnode
323  */
324 nfs_lock(vp)
325 	struct vnode *vp;
326 {
327 	register struct nfsnode *np = VTONFS(vp);
328 
329 	if (np->n_flag & NLOCKED)
330 		printf("pid %d hit locked nfsnode=0x%x\n",
331 		    u.u_procp->p_pid, np);
332 	while (np->n_flag & NLOCKED) {
333 		np->n_flag |= NWANT;
334 		sleep((caddr_t)np, PINOD);
335 	}
336 	np->n_flag |= NLOCKED;
337 }
338 
339 /*
340  * Unlock an nfsnode
341  */
342 nfs_unlock(vp)
343 	struct vnode *vp;
344 {
345 	register struct nfsnode *np = VTONFS(vp);
346 
347 	if ((np->n_flag & NLOCKED) == 0) {
348 		printf("pid %d unlocking unlocked nfsnode=0x%x ",
349 		    u.u_procp->p_pid, np);
350 		printf("fh0=0x%x fh1=0x%x fh2=0x%x fh3=0x%x fh4=0x%x fh5=0x%x fh6=0x%x fh7=0x%x\n",
351 			np->n_fh.fh_bytes[0],np->n_fh.fh_bytes[1],
352 			np->n_fh.fh_bytes[2],np->n_fh.fh_bytes[3],
353 			np->n_fh.fh_bytes[4],np->n_fh.fh_bytes[5],
354 			np->n_fh.fh_bytes[6],np->n_fh.fh_bytes[7]);
355 	}
356 	np->n_flag &= ~NLOCKED;
357 	if (np->n_flag & NWANT) {
358 		np->n_flag &= ~NWANT;
359 		wakeup((caddr_t)np);
360 	}
361 }
362 
363 /*
364  * Unlock and vrele()
365  * since I can't decide if dirs. should be locked, I will check for
366  * the lock and be flexible
367  */
368 nfs_nput(vp)
369 	struct vnode *vp;
370 {
371 	register struct nfsnode *np = VTONFS(vp);
372 
373 	if (np->n_flag & NLOCKED)
374 		nfs_unlock(vp);
375 	vrele(vp);
376 }
377 
378 nfs_abortop(ndp)
379 	register struct nameidata *ndp;
380 {
381 	register struct nfsnode *np;
382 
383 	if (ndp->ni_vp != NULL) {
384 		np = VTONFS(ndp->ni_vp);
385 		if (np->n_flag & NLOCKED)
386 			nfs_unlock(ndp->ni_vp);
387 		vrele(ndp->ni_vp);
388 	}
389 	if (ndp->ni_dvp != NULL) {
390 		np = VTONFS(ndp->ni_dvp);
391 		if (np->n_flag & NLOCKED)
392 			nfs_unlock(ndp->ni_dvp);
393 		vrele(ndp->ni_dvp);
394 	}
395 }
396 
397 /*
398  * This is silly, but if you use a macro and try and use it in a file
399  * that has mbuf.h included, m_data --> m_hdr.mh_data and this is not
400  * a good thing
401  */
402 struct nfsmount *vfs_to_nfs(mp)
403 	struct mount *mp;
404 {
405 	return ((struct nfsmount *)mp->m_data);
406 }
407