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