smbfs_io.c revision 207746
1/*-
2 * Copyright (c) 2000-2001 Boris Popov
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD: head/sys/fs/smbfs/smbfs_io.c 207746 2010-05-07 15:49:43Z alc $
27 *
28 */
29#include <sys/param.h>
30#include <sys/systm.h>
31#include <sys/kernel.h>
32#include <sys/fcntl.h>
33#include <sys/bio.h>
34#include <sys/buf.h>
35#include <sys/mount.h>
36#include <sys/namei.h>
37#include <sys/vnode.h>
38#include <sys/dirent.h>
39#include <sys/signalvar.h>
40#include <sys/sysctl.h>
41#include <sys/vmmeter.h>
42
43#include <vm/vm.h>
44#include <vm/vm_page.h>
45#include <vm/vm_extern.h>
46#include <vm/vm_object.h>
47#include <vm/vm_pager.h>
48#include <vm/vnode_pager.h>
49/*
50#include <sys/ioccom.h>
51*/
52#include <netsmb/smb.h>
53#include <netsmb/smb_conn.h>
54#include <netsmb/smb_subr.h>
55
56#include <fs/smbfs/smbfs.h>
57#include <fs/smbfs/smbfs_node.h>
58#include <fs/smbfs/smbfs_subr.h>
59
60/*#define SMBFS_RWGENERIC*/
61
62extern int smbfs_pbuf_freecnt;
63
64static int smbfs_fastlookup = 1;
65
66SYSCTL_DECL(_vfs_smbfs);
67SYSCTL_INT(_vfs_smbfs, OID_AUTO, fastlookup, CTLFLAG_RW, &smbfs_fastlookup, 0, "");
68
69
70#define DE_SIZE	(sizeof(struct dirent))
71
72static int
73smbfs_readvdir(struct vnode *vp, struct uio *uio, struct ucred *cred)
74{
75	struct dirent de;
76	struct componentname cn;
77	struct smb_cred scred;
78	struct smbfs_fctx *ctx;
79	struct vnode *newvp;
80	struct smbnode *np = VTOSMB(vp);
81	int error/*, *eofflag = ap->a_eofflag*/;
82	long offset, limit;
83
84	np = VTOSMB(vp);
85	SMBVDEBUG("dirname='%s'\n", np->n_name);
86	smb_makescred(&scred, uio->uio_td, cred);
87	offset = uio->uio_offset / DE_SIZE;	/* offset in the directory */
88	limit = uio->uio_resid / DE_SIZE;
89	if (uio->uio_resid < DE_SIZE || uio->uio_offset < 0)
90		return EINVAL;
91	while (limit && offset < 2) {
92		limit--;
93		bzero((caddr_t)&de, DE_SIZE);
94		de.d_reclen = DE_SIZE;
95		de.d_fileno = (offset == 0) ? np->n_ino :
96		    (np->n_parent ? VTOSMB(np->n_parent)->n_ino : 2);
97		if (de.d_fileno == 0)
98			de.d_fileno = 0x7ffffffd + offset;
99		de.d_namlen = offset + 1;
100		de.d_name[0] = '.';
101		de.d_name[1] = '.';
102		de.d_name[offset + 1] = '\0';
103		de.d_type = DT_DIR;
104		error = uiomove(&de, DE_SIZE, uio);
105		if (error)
106			return error;
107		offset++;
108		uio->uio_offset += DE_SIZE;
109	}
110	if (limit == 0)
111		return 0;
112	if (offset != np->n_dirofs || np->n_dirseq == NULL) {
113		SMBVDEBUG("Reopening search %ld:%ld\n", offset, np->n_dirofs);
114		if (np->n_dirseq) {
115			smbfs_findclose(np->n_dirseq, &scred);
116			np->n_dirseq = NULL;
117		}
118		np->n_dirofs = 2;
119		error = smbfs_findopen(np, "*", 1,
120		    SMB_FA_SYSTEM | SMB_FA_HIDDEN | SMB_FA_DIR,
121		    &scred, &ctx);
122		if (error) {
123			SMBVDEBUG("can not open search, error = %d", error);
124			return error;
125		}
126		np->n_dirseq = ctx;
127	} else
128		ctx = np->n_dirseq;
129	while (np->n_dirofs < offset) {
130		error = smbfs_findnext(ctx, offset - np->n_dirofs++, &scred);
131		if (error) {
132			smbfs_findclose(np->n_dirseq, &scred);
133			np->n_dirseq = NULL;
134			return error == ENOENT ? 0 : error;
135		}
136	}
137	error = 0;
138	for (; limit; limit--, offset++) {
139		error = smbfs_findnext(ctx, limit, &scred);
140		if (error)
141			break;
142		np->n_dirofs++;
143		bzero((caddr_t)&de, DE_SIZE);
144		de.d_reclen = DE_SIZE;
145		de.d_fileno = ctx->f_attr.fa_ino;
146		de.d_type = (ctx->f_attr.fa_attr & SMB_FA_DIR) ? DT_DIR : DT_REG;
147		de.d_namlen = ctx->f_nmlen;
148		bcopy(ctx->f_name, de.d_name, de.d_namlen);
149		de.d_name[de.d_namlen] = '\0';
150		if (smbfs_fastlookup) {
151			error = smbfs_nget(vp->v_mount, vp, ctx->f_name,
152			    ctx->f_nmlen, &ctx->f_attr, &newvp);
153			if (!error) {
154				cn.cn_nameptr = de.d_name;
155				cn.cn_namelen = de.d_namlen;
156				cache_enter(vp, newvp, &cn);
157				vput(newvp);
158			}
159		}
160		error = uiomove(&de, DE_SIZE, uio);
161		if (error)
162			break;
163	}
164	if (error == ENOENT)
165		error = 0;
166	uio->uio_offset = offset * DE_SIZE;
167	return error;
168}
169
170int
171smbfs_readvnode(struct vnode *vp, struct uio *uiop, struct ucred *cred)
172{
173	struct smbmount *smp = VFSTOSMBFS(vp->v_mount);
174	struct smbnode *np = VTOSMB(vp);
175	struct thread *td;
176	struct vattr vattr;
177	struct smb_cred scred;
178	int error, lks;
179
180	/*
181	 * Protect against method which is not supported for now
182	 */
183	if (uiop->uio_segflg == UIO_NOCOPY)
184		return EOPNOTSUPP;
185
186	if (vp->v_type != VREG && vp->v_type != VDIR) {
187		SMBFSERR("vn types other than VREG or VDIR are unsupported !\n");
188		return EIO;
189	}
190	if (uiop->uio_resid == 0)
191		return 0;
192	if (uiop->uio_offset < 0)
193		return EINVAL;
194/*	if (uiop->uio_offset + uiop->uio_resid > smp->nm_maxfilesize)
195		return EFBIG;*/
196	td = uiop->uio_td;
197	if (vp->v_type == VDIR) {
198		lks = LK_EXCLUSIVE;	/* lockstatus(vp->v_vnlock); */
199		if (lks == LK_SHARED)
200			vn_lock(vp, LK_UPGRADE | LK_RETRY);
201		error = smbfs_readvdir(vp, uiop, cred);
202		if (lks == LK_SHARED)
203			vn_lock(vp, LK_DOWNGRADE | LK_RETRY);
204		return error;
205	}
206
207/*	biosize = SSTOCN(smp->sm_share)->sc_txmax;*/
208	if (np->n_flag & NMODIFIED) {
209		smbfs_attr_cacheremove(vp);
210		error = VOP_GETATTR(vp, &vattr, cred);
211		if (error)
212			return error;
213		np->n_mtime.tv_sec = vattr.va_mtime.tv_sec;
214	} else {
215		error = VOP_GETATTR(vp, &vattr, cred);
216		if (error)
217			return error;
218		if (np->n_mtime.tv_sec != vattr.va_mtime.tv_sec) {
219			error = smbfs_vinvalbuf(vp, td);
220			if (error)
221				return error;
222			np->n_mtime.tv_sec = vattr.va_mtime.tv_sec;
223		}
224	}
225	smb_makescred(&scred, td, cred);
226	return smb_read(smp->sm_share, np->n_fid, uiop, &scred);
227}
228
229int
230smbfs_writevnode(struct vnode *vp, struct uio *uiop,
231	struct ucred *cred, int ioflag)
232{
233	struct smbmount *smp = VTOSMBFS(vp);
234	struct smbnode *np = VTOSMB(vp);
235	struct smb_cred scred;
236	struct thread *td;
237	int error = 0;
238
239	if (vp->v_type != VREG) {
240		SMBERROR("vn types other than VREG unsupported !\n");
241		return EIO;
242	}
243	SMBVDEBUG("ofs=%d,resid=%d\n",(int)uiop->uio_offset, uiop->uio_resid);
244	if (uiop->uio_offset < 0)
245		return EINVAL;
246/*	if (uiop->uio_offset + uiop->uio_resid > smp->nm_maxfilesize)
247		return (EFBIG);*/
248	td = uiop->uio_td;
249	if (ioflag & (IO_APPEND | IO_SYNC)) {
250		if (np->n_flag & NMODIFIED) {
251			smbfs_attr_cacheremove(vp);
252			error = smbfs_vinvalbuf(vp, td);
253			if (error)
254				return error;
255		}
256		if (ioflag & IO_APPEND) {
257#ifdef notyet
258			/*
259			 * File size can be changed by another client
260			 */
261			smbfs_attr_cacheremove(vp);
262			error = VOP_GETATTR(vp, &vattr, cred);
263			if (error) return (error);
264#endif
265			uiop->uio_offset = np->n_size;
266		}
267	}
268	if (uiop->uio_resid == 0)
269		return 0;
270
271	if (vn_rlimit_fsize(vp, uiop, td))
272		return (EFBIG);
273
274	smb_makescred(&scred, td, cred);
275	error = smb_write(smp->sm_share, np->n_fid, uiop, &scred);
276	SMBVDEBUG("after: ofs=%d,resid=%d\n",(int)uiop->uio_offset, uiop->uio_resid);
277	if (!error) {
278		if (uiop->uio_offset > np->n_size) {
279			np->n_size = uiop->uio_offset;
280			vnode_pager_setsize(vp, np->n_size);
281		}
282	}
283	return error;
284}
285
286/*
287 * Do an I/O operation to/from a cache block.
288 */
289int
290smbfs_doio(struct vnode *vp, struct buf *bp, struct ucred *cr, struct thread *td)
291{
292	struct smbmount *smp = VFSTOSMBFS(vp->v_mount);
293	struct smbnode *np = VTOSMB(vp);
294	struct uio uio, *uiop = &uio;
295	struct iovec io;
296	struct smb_cred scred;
297	int error = 0;
298
299	uiop->uio_iov = &io;
300	uiop->uio_iovcnt = 1;
301	uiop->uio_segflg = UIO_SYSSPACE;
302	uiop->uio_td = td;
303
304	smb_makescred(&scred, td, cr);
305
306	if (bp->b_iocmd == BIO_READ) {
307	    io.iov_len = uiop->uio_resid = bp->b_bcount;
308	    io.iov_base = bp->b_data;
309	    uiop->uio_rw = UIO_READ;
310	    switch (vp->v_type) {
311	      case VREG:
312		uiop->uio_offset = ((off_t)bp->b_blkno) * DEV_BSIZE;
313		error = smb_read(smp->sm_share, np->n_fid, uiop, &scred);
314		if (error)
315			break;
316		if (uiop->uio_resid) {
317			int left = uiop->uio_resid;
318			int nread = bp->b_bcount - left;
319			if (left > 0)
320			    bzero((char *)bp->b_data + nread, left);
321		}
322		break;
323	    default:
324		printf("smbfs_doio:  type %x unexpected\n",vp->v_type);
325		break;
326	    };
327	    if (error) {
328		bp->b_error = error;
329		bp->b_ioflags |= BIO_ERROR;
330	    }
331	} else { /* write */
332	    if (((bp->b_blkno * DEV_BSIZE) + bp->b_dirtyend) > np->n_size)
333		bp->b_dirtyend = np->n_size - (bp->b_blkno * DEV_BSIZE);
334
335	    if (bp->b_dirtyend > bp->b_dirtyoff) {
336		io.iov_len = uiop->uio_resid = bp->b_dirtyend - bp->b_dirtyoff;
337		uiop->uio_offset = ((off_t)bp->b_blkno) * DEV_BSIZE + bp->b_dirtyoff;
338		io.iov_base = (char *)bp->b_data + bp->b_dirtyoff;
339		uiop->uio_rw = UIO_WRITE;
340		error = smb_write(smp->sm_share, np->n_fid, uiop, &scred);
341
342		/*
343		 * For an interrupted write, the buffer is still valid
344		 * and the write hasn't been pushed to the server yet,
345		 * so we can't set BIO_ERROR and report the interruption
346		 * by setting B_EINTR. For the B_ASYNC case, B_EINTR
347		 * is not relevant, so the rpc attempt is essentially
348		 * a noop.  For the case of a V3 write rpc not being
349		 * committed to stable storage, the block is still
350		 * dirty and requires either a commit rpc or another
351		 * write rpc with iomode == NFSV3WRITE_FILESYNC before
352		 * the block is reused. This is indicated by setting
353		 * the B_DELWRI and B_NEEDCOMMIT flags.
354		 */
355		if (error == EINTR
356		    || (!error && (bp->b_flags & B_NEEDCOMMIT))) {
357			int s;
358
359			s = splbio();
360			bp->b_flags &= ~(B_INVAL|B_NOCACHE);
361			if ((bp->b_flags & B_ASYNC) == 0)
362			    bp->b_flags |= B_EINTR;
363			if ((bp->b_flags & B_PAGING) == 0) {
364			    bdirty(bp);
365			    bp->b_flags &= ~B_DONE;
366			}
367			if ((bp->b_flags & B_ASYNC) == 0)
368			    bp->b_flags |= B_EINTR;
369			splx(s);
370		} else {
371			if (error) {
372				bp->b_ioflags |= BIO_ERROR;
373				bp->b_error = error;
374			}
375			bp->b_dirtyoff = bp->b_dirtyend = 0;
376		}
377	    } else {
378		bp->b_resid = 0;
379		bufdone(bp);
380		return 0;
381	    }
382	}
383	bp->b_resid = uiop->uio_resid;
384	bufdone(bp);
385	return error;
386}
387
388/*
389 * Vnode op for VM getpages.
390 * Wish wish .... get rid from multiple IO routines
391 */
392int
393smbfs_getpages(ap)
394	struct vop_getpages_args /* {
395		struct vnode *a_vp;
396		vm_page_t *a_m;
397		int a_count;
398		int a_reqpage;
399		vm_ooffset_t a_offset;
400	} */ *ap;
401{
402#ifdef SMBFS_RWGENERIC
403	return vop_stdgetpages(ap);
404#else
405	int i, error, nextoff, size, toff, npages, count, reqpage;
406	struct uio uio;
407	struct iovec iov;
408	vm_offset_t kva;
409	struct buf *bp;
410	struct vnode *vp;
411	struct thread *td;
412	struct ucred *cred;
413	struct smbmount *smp;
414	struct smbnode *np;
415	struct smb_cred scred;
416	vm_object_t object;
417	vm_page_t *pages, m;
418
419	vp = ap->a_vp;
420	if ((object = vp->v_object) == NULL) {
421		printf("smbfs_getpages: called with non-merged cache vnode??\n");
422		return VM_PAGER_ERROR;
423	}
424
425	td = curthread;				/* XXX */
426	cred = td->td_ucred;		/* XXX */
427	np = VTOSMB(vp);
428	smp = VFSTOSMBFS(vp->v_mount);
429	pages = ap->a_m;
430	count = ap->a_count;
431	npages = btoc(count);
432	reqpage = ap->a_reqpage;
433
434	/*
435	 * If the requested page is partially valid, just return it and
436	 * allow the pager to zero-out the blanks.  Partially valid pages
437	 * can only occur at the file EOF.
438	 */
439	m = pages[reqpage];
440
441	VM_OBJECT_LOCK(object);
442	if (m->valid != 0) {
443		for (i = 0; i < npages; ++i) {
444			if (i != reqpage) {
445				vm_page_lock(pages[i]);
446				vm_page_free(pages[i]);
447				vm_page_unlock(pages[i]);
448			}
449		}
450		VM_OBJECT_UNLOCK(object);
451		return 0;
452	}
453	VM_OBJECT_UNLOCK(object);
454
455	smb_makescred(&scred, td, cred);
456
457	bp = getpbuf(&smbfs_pbuf_freecnt);
458
459	kva = (vm_offset_t) bp->b_data;
460	pmap_qenter(kva, pages, npages);
461	PCPU_INC(cnt.v_vnodein);
462	PCPU_ADD(cnt.v_vnodepgsin, npages);
463
464	iov.iov_base = (caddr_t) kva;
465	iov.iov_len = count;
466	uio.uio_iov = &iov;
467	uio.uio_iovcnt = 1;
468	uio.uio_offset = IDX_TO_OFF(pages[0]->pindex);
469	uio.uio_resid = count;
470	uio.uio_segflg = UIO_SYSSPACE;
471	uio.uio_rw = UIO_READ;
472	uio.uio_td = td;
473
474	error = smb_read(smp->sm_share, np->n_fid, &uio, &scred);
475	pmap_qremove(kva, npages);
476
477	relpbuf(bp, &smbfs_pbuf_freecnt);
478
479	VM_OBJECT_LOCK(object);
480	if (error && (uio.uio_resid == count)) {
481		printf("smbfs_getpages: error %d\n",error);
482		for (i = 0; i < npages; i++) {
483			if (reqpage != i) {
484				vm_page_lock(pages[i]);
485				vm_page_free(pages[i]);
486				vm_page_unlock(pages[i]);
487			}
488		}
489		VM_OBJECT_UNLOCK(object);
490		return VM_PAGER_ERROR;
491	}
492
493	size = count - uio.uio_resid;
494
495	for (i = 0, toff = 0; i < npages; i++, toff = nextoff) {
496		vm_page_t m;
497		nextoff = toff + PAGE_SIZE;
498		m = pages[i];
499
500		if (nextoff <= size) {
501			/*
502			 * Read operation filled an entire page
503			 */
504			m->valid = VM_PAGE_BITS_ALL;
505			KASSERT(m->dirty == 0,
506			    ("smbfs_getpages: page %p is dirty", m));
507		} else if (size > toff) {
508			/*
509			 * Read operation filled a partial page.
510			 */
511			m->valid = 0;
512			vm_page_set_valid(m, 0, size - toff);
513			KASSERT(m->dirty == 0,
514			    ("smbfs_getpages: page %p is dirty", m));
515		} else {
516			/*
517			 * Read operation was short.  If no error occured
518			 * we may have hit a zero-fill section.   We simply
519			 * leave valid set to 0.
520			 */
521			;
522		}
523
524		if (i != reqpage) {
525			/*
526			 * Whether or not to leave the page activated is up in
527			 * the air, but we should put the page on a page queue
528			 * somewhere (it already is in the object).  Result:
529			 * It appears that emperical results show that
530			 * deactivating pages is best.
531			 */
532
533			/*
534			 * Just in case someone was asking for this page we
535			 * now tell them that it is ok to use.
536			 */
537			if (!error) {
538				if (m->oflags & VPO_WANTED) {
539					vm_page_lock(m);
540					vm_page_activate(m);
541					vm_page_unlock(m);
542				} else {
543					vm_page_lock(m);
544					vm_page_deactivate(m);
545					vm_page_unlock(m);
546				}
547				vm_page_wakeup(m);
548			} else {
549				vm_page_lock(m);
550				vm_page_free(m);
551				vm_page_unlock(m);
552			}
553		}
554	}
555	VM_OBJECT_UNLOCK(object);
556	return 0;
557#endif /* SMBFS_RWGENERIC */
558}
559
560/*
561 * Vnode op for VM putpages.
562 * possible bug: all IO done in sync mode
563 * Note that vop_close always invalidate pages before close, so it's
564 * not necessary to open vnode.
565 */
566int
567smbfs_putpages(ap)
568	struct vop_putpages_args /* {
569		struct vnode *a_vp;
570		vm_page_t *a_m;
571		int a_count;
572		int a_sync;
573		int *a_rtvals;
574		vm_ooffset_t a_offset;
575	} */ *ap;
576{
577	int error;
578	struct vnode *vp = ap->a_vp;
579	struct thread *td;
580	struct ucred *cred;
581
582#ifdef SMBFS_RWGENERIC
583	td = curthread;			/* XXX */
584	cred = td->td_ucred;		/* XXX */
585	VOP_OPEN(vp, FWRITE, cred, td, NULL);
586	error = vop_stdputpages(ap);
587	VOP_CLOSE(vp, FWRITE, cred, td);
588	return error;
589#else
590	struct uio uio;
591	struct iovec iov;
592	vm_offset_t kva;
593	struct buf *bp;
594	int i, npages, count;
595	int *rtvals;
596	struct smbmount *smp;
597	struct smbnode *np;
598	struct smb_cred scred;
599	vm_page_t *pages;
600
601	td = curthread;			/* XXX */
602	cred = td->td_ucred;		/* XXX */
603/*	VOP_OPEN(vp, FWRITE, cred, td, NULL);*/
604	np = VTOSMB(vp);
605	smp = VFSTOSMBFS(vp->v_mount);
606	pages = ap->a_m;
607	count = ap->a_count;
608	rtvals = ap->a_rtvals;
609	npages = btoc(count);
610
611	for (i = 0; i < npages; i++) {
612		rtvals[i] = VM_PAGER_AGAIN;
613	}
614
615	bp = getpbuf(&smbfs_pbuf_freecnt);
616
617	kva = (vm_offset_t) bp->b_data;
618	pmap_qenter(kva, pages, npages);
619	PCPU_INC(cnt.v_vnodeout);
620	PCPU_ADD(cnt.v_vnodepgsout, count);
621
622	iov.iov_base = (caddr_t) kva;
623	iov.iov_len = count;
624	uio.uio_iov = &iov;
625	uio.uio_iovcnt = 1;
626	uio.uio_offset = IDX_TO_OFF(pages[0]->pindex);
627	uio.uio_resid = count;
628	uio.uio_segflg = UIO_SYSSPACE;
629	uio.uio_rw = UIO_WRITE;
630	uio.uio_td = td;
631	SMBVDEBUG("ofs=%d,resid=%d\n",(int)uio.uio_offset, uio.uio_resid);
632
633	smb_makescred(&scred, td, cred);
634	error = smb_write(smp->sm_share, np->n_fid, &uio, &scred);
635/*	VOP_CLOSE(vp, FWRITE, cred, td);*/
636	SMBVDEBUG("paged write done: %d\n", error);
637
638	pmap_qremove(kva, npages);
639
640	relpbuf(bp, &smbfs_pbuf_freecnt);
641
642	if (!error) {
643		int nwritten = round_page(count - uio.uio_resid) / PAGE_SIZE;
644		vm_page_lock_queues();
645		for (i = 0; i < nwritten; i++) {
646			rtvals[i] = VM_PAGER_OK;
647			vm_page_undirty(pages[i]);
648		}
649		vm_page_unlock_queues();
650	}
651	return rtvals[0];
652#endif /* SMBFS_RWGENERIC */
653}
654
655/*
656 * Flush and invalidate all dirty buffers. If another process is already
657 * doing the flush, just wait for completion.
658 */
659int
660smbfs_vinvalbuf(struct vnode *vp, struct thread *td)
661{
662	struct smbnode *np = VTOSMB(vp);
663	int error = 0;
664
665	if (vp->v_iflag & VI_DOOMED)
666		return 0;
667
668	while (np->n_flag & NFLUSHINPROG) {
669		np->n_flag |= NFLUSHWANT;
670		error = tsleep(&np->n_flag, PRIBIO + 2, "smfsvinv", 2 * hz);
671		error = smb_td_intr(td);
672		if (error == EINTR)
673			return EINTR;
674	}
675	np->n_flag |= NFLUSHINPROG;
676
677	if (vp->v_bufobj.bo_object != NULL) {
678		VM_OBJECT_LOCK(vp->v_bufobj.bo_object);
679		vm_object_page_clean(vp->v_bufobj.bo_object, 0, 0, OBJPC_SYNC);
680		VM_OBJECT_UNLOCK(vp->v_bufobj.bo_object);
681	}
682
683	error = vinvalbuf(vp, V_SAVE, PCATCH, 0);
684	while (error) {
685		if (error == ERESTART || error == EINTR) {
686			np->n_flag &= ~NFLUSHINPROG;
687			if (np->n_flag & NFLUSHWANT) {
688				np->n_flag &= ~NFLUSHWANT;
689				wakeup(&np->n_flag);
690			}
691			return EINTR;
692		}
693		error = vinvalbuf(vp, V_SAVE, PCATCH, 0);
694	}
695	np->n_flag &= ~(NMODIFIED | NFLUSHINPROG);
696	if (np->n_flag & NFLUSHWANT) {
697		np->n_flag &= ~NFLUSHWANT;
698		wakeup(&np->n_flag);
699	}
700	return (error);
701}
702