2 * Copyright (c) 1999, Boris Popov
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
32 * $FreeBSD: src/sys/nwfs/nwfs_io.c,v 1.6.2.1 2000/10/25 02:11:10 bp Exp $
33 * $DragonFly: src/sys/vfs/nwfs/nwfs_io.c,v 1.24 2007/02/22 15:50:50 corecode Exp $
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/resourcevar.h> /* defines plimit structure in proc struct */
39 #include <sys/kernel.h>
42 #include <sys/mount.h>
43 #include <sys/namei.h>
44 #include <sys/vnode.h>
45 #include <sys/dirent.h>
46 #include <sys/signalvar.h>
47 #include <sys/sysctl.h>
50 #include <vm/vm_page.h>
51 #include <vm/vm_extern.h>
52 #include <vm/vm_object.h>
53 #include <vm/vm_pager.h>
54 #include <vm/vnode_pager.h>
56 #include <netproto/ncp/ncp.h>
57 #include <netproto/ncp/ncp_conn.h>
58 #include <netproto/ncp/ncp_subr.h>
60 #include <sys/thread2.h>
62 #include <machine/limits.h>
65 #include "nwfs_node.h"
66 #include "nwfs_subr.h"
68 static int nwfs_fastlookup
= 1;
70 SYSCTL_DECL(_vfs_nwfs
);
71 SYSCTL_INT(_vfs_nwfs
, OID_AUTO
, fastlookup
, CTLFLAG_RW
, &nwfs_fastlookup
, 0, "");
74 extern int nwfs_pbuf_freecnt
;
79 nwfs_readvdir(struct vnode
*vp
, struct uio
*uio
, struct ucred
*cred
)
81 struct nwmount
*nmp
= VTONWFS(vp
);
84 struct nw_entry_info fattr
;
93 NCPVNDEBUG("dirname='%s'\n",np
->n_name
);
94 if (uio
->uio_resid
< 0 || uio
->uio_offset
< 0 || uio
->uio_offset
> INT_MAX
)
98 i
= uio
->uio_offset
; /* offset in directory */
100 error
= ncp_initsearch(vp
, uio
->uio_td
, cred
);
102 NCPVNDEBUG("cannot initialize search, error=%d",error
);
107 for (; !error
&& uio
->uio_resid
> 0; i
++) {
110 d_ino
= np
->n_fid
.f_id
;
112 d_ino
= NWFS_ROOT_INO
;
118 d_ino
= np
->n_parent
.f_id
;
120 d_ino
= NWFS_ROOT_INO
;
126 error
= ncp_search_for_file_or_subdir(nmp
, &np
->n_seq
, &fattr
, uio
->uio_td
, cred
);
127 if (error
&& error
< 0x80)
129 d_ino
= fattr
.dirEntNum
;
130 d_type
= (fattr
.attributes
& aDIR
) ? DT_DIR
: DT_REG
;
131 d_namlen
= fattr
.nameLen
;
132 d_name
= fattr
.entryName
;
134 if (error
&& eofflag
) {
141 if (nwfs_fastlookup
&& !error
&& i
> 1) {
142 fid
.f_id
= fattr
.dirEntNum
;
143 fid
.f_parent
= np
->n_fid
.f_id
;
144 error
= nwfs_nget(vp
->v_mount
, fid
, &fattr
, vp
, &newvp
);
146 VTONW(newvp
)->n_ctime
= VTONW(newvp
)->n_vattr
.va_ctime
.tv_sec
;
155 if (vop_write_dirent(&error
, uio
, d_ino
, d_type
, d_namlen
, d_name
))
164 nwfs_readvnode(struct vnode
*vp
, struct uio
*uiop
, struct ucred
*cred
)
166 struct nwmount
*nmp
= VFSTONWFS(vp
->v_mount
);
167 struct nwnode
*np
= VTONW(vp
);
172 if (vp
->v_type
!= VREG
&& vp
->v_type
!= VDIR
) {
173 kprintf("%s: vn types other than VREG or VDIR are unsupported !\n",__func__
);
176 if (uiop
->uio_resid
== 0) return 0;
177 if (uiop
->uio_offset
< 0) return EINVAL
;
178 /* if (uiop->uio_offset + uiop->uio_resid > nmp->nm_maxfilesize)
181 if (vp
->v_type
== VDIR
) {
182 error
= nwfs_readvdir(vp
, uiop
, cred
);
185 biosize
= NWFSTOCONN(nmp
)->buffer_size
;
186 if (np
->n_flag
& NMODIFIED
) {
187 nwfs_attr_cacheremove(vp
);
188 error
= VOP_GETATTR(vp
, &vattr
);
189 if (error
) return (error
);
190 np
->n_mtime
= vattr
.va_mtime
.tv_sec
;
192 error
= VOP_GETATTR(vp
, &vattr
);
193 if (error
) return (error
);
194 if (np
->n_mtime
!= vattr
.va_mtime
.tv_sec
) {
195 error
= nwfs_vinvalbuf(vp
, V_SAVE
, 1);
196 if (error
) return (error
);
197 np
->n_mtime
= vattr
.va_mtime
.tv_sec
;
200 error
= ncp_read(NWFSTOCONN(nmp
), &np
->n_fh
, uiop
,cred
);
205 nwfs_writevnode(struct vnode
*vp
, struct uio
*uiop
, struct ucred
*cred
,
208 struct nwmount
*nmp
= VTONWFS(vp
);
209 struct nwnode
*np
= VTONW(vp
);
211 /* struct vattr vattr;*/
214 if (vp
->v_type
!= VREG
) {
215 kprintf("%s: vn types other than VREG unsupported !\n",__func__
);
218 NCPVNDEBUG("ofs=%d,resid=%d\n",(int)uiop
->uio_offset
, uiop
->uio_resid
);
219 if (uiop
->uio_offset
< 0) return EINVAL
;
220 /* if (uiop->uio_offset + uiop->uio_resid > nmp->nm_maxfilesize)
223 if (ioflag
& (IO_APPEND
| IO_SYNC
)) {
224 if (np
->n_flag
& NMODIFIED
) {
225 nwfs_attr_cacheremove(vp
);
226 error
= nwfs_vinvalbuf(vp
, V_SAVE
, 1);
227 if (error
) return (error
);
229 if (ioflag
& IO_APPEND
) {
230 /* We can relay only on local information about file size,
231 * because until file is closed NetWare will not return
232 * the correct size. */
234 nwfs_attr_cacheremove(vp
);
235 error
= VOP_GETATTR(vp
, &vattr
);
236 if (error
) return (error
);
238 uiop
->uio_offset
= np
->n_size
;
241 if (uiop
->uio_resid
== 0) return 0;
242 if (td
->td_proc
&& uiop
->uio_offset
+ uiop
->uio_resid
>
243 td
->td_proc
->p_rlimit
[RLIMIT_FSIZE
].rlim_cur
) {
244 lwpsignal(td
->td_proc
, td
->td_lwp
, SIGXFSZ
);
247 error
= ncp_write(NWFSTOCONN(nmp
), &np
->n_fh
, uiop
, cred
);
248 NCPVNDEBUG("after: ofs=%d,resid=%d\n",(int)uiop
->uio_offset
, uiop
->uio_resid
);
250 if (uiop
->uio_offset
> np
->n_size
) {
251 np
->n_vattr
.va_size
= np
->n_size
= uiop
->uio_offset
;
252 vnode_pager_setsize(vp
, np
->n_size
);
259 * Do an I/O operation to/from a cache block.
262 nwfs_doio(struct vnode
*vp
, struct bio
*bio
, struct ucred
*cr
, struct thread
*td
)
264 struct buf
*bp
= bio
->bio_buf
;
273 nmp
= VFSTONWFS(vp
->v_mount
);
276 uiop
->uio_iovcnt
= 1;
277 uiop
->uio_segflg
= UIO_SYSSPACE
;
280 if (bp
->b_cmd
== BUF_CMD_READ
) {
281 io
.iov_len
= uiop
->uio_resid
= bp
->b_bcount
;
282 io
.iov_base
= bp
->b_data
;
283 uiop
->uio_rw
= UIO_READ
;
284 switch (vp
->v_type
) {
286 uiop
->uio_offset
= bio
->bio_offset
;
287 error
= ncp_read(NWFSTOCONN(nmp
), &np
->n_fh
, uiop
, cr
);
290 if (uiop
->uio_resid
) {
291 int left
= uiop
->uio_resid
;
292 int nread
= bp
->b_bcount
- left
;
294 bzero((char *)bp
->b_data
+ nread
, left
);
298 nfsstats.readdir_bios++;
299 uiop->uio_offset = bio->bio_offset;
300 if (nmp->nm_flag & NFSMNT_RDIRPLUS) {
301 error = nfs_readdirplusrpc(vp, uiop, cr);
302 if (error == NFSERR_NOTSUPP)
303 nmp->nm_flag &= ~NFSMNT_RDIRPLUS;
305 if ((nmp->nm_flag & NFSMNT_RDIRPLUS) == 0)
306 error = nfs_readdirrpc(vp, uiop, cr);
307 if (error == 0 && uiop->uio_resid == bp->b_bcount)
308 bp->b_flags |= B_INVAL;
312 kprintf("nwfs_doio: type %x unexpected\n",vp
->v_type
);
316 bp
->b_flags
|= B_ERROR
;
320 KKASSERT(bp
->b_cmd
== BUF_CMD_WRITE
);
321 if (bio
->bio_offset
+ bp
->b_dirtyend
> np
->n_size
)
322 bp
->b_dirtyend
= np
->n_size
- bio
->bio_offset
;
324 if (bp
->b_dirtyend
> bp
->b_dirtyoff
) {
325 io
.iov_len
= uiop
->uio_resid
= bp
->b_dirtyend
- bp
->b_dirtyoff
;
326 uiop
->uio_offset
= bio
->bio_offset
+ bp
->b_dirtyoff
;
327 io
.iov_base
= (char *)bp
->b_data
+ bp
->b_dirtyoff
;
328 uiop
->uio_rw
= UIO_WRITE
;
329 error
= ncp_write(NWFSTOCONN(nmp
), &np
->n_fh
, uiop
, cr
);
332 * For an interrupted write, the buffer is still valid
333 * and the write hasn't been pushed to the server yet,
334 * so we can't set B_ERROR and report the interruption
335 * by setting B_EINTR. For the B_ASYNC case, B_EINTR
336 * is not relevant, so the rpc attempt is essentially
337 * a noop. For the case of a V3 write rpc not being
338 * committed to stable storage, the block is still
339 * dirty and requires either a commit rpc or another
340 * write rpc with iomode == NFSV3WRITE_FILESYNC before
341 * the block is reused. This is indicated by setting
342 * the B_DELWRI and B_NEEDCOMMIT flags.
345 || (!error
&& (bp
->b_flags
& B_NEEDCOMMIT
))) {
348 bp
->b_flags
&= ~(B_INVAL
|B_NOCACHE
);
349 if ((bp
->b_flags
& B_ASYNC
) == 0)
350 bp
->b_flags
|= B_EINTR
;
351 if ((bp
->b_flags
& B_PAGING
) == 0)
353 if ((bp
->b_flags
& B_ASYNC
) == 0)
354 bp
->b_flags
|= B_EINTR
;
358 bp
->b_flags
|= B_ERROR
;
359 bp
->b_error
/*= np->n_error */= error
;
360 /* np->n_flag |= NWRITEERR;*/
362 bp
->b_dirtyoff
= bp
->b_dirtyend
= 0;
370 bp
->b_resid
= uiop
->uio_resid
;
376 * Vnode op for VM getpages.
377 * Wish wish .... get rid from multiple IO routines
379 * nwfs_getpages(struct vnode *a_vp, vm_page_t *a_m, int a_count,
380 * int a_reqpage, vm_ooffset_t a_offset)
383 nwfs_getpages(struct vop_getpages_args
*ap
)
386 return vnode_pager_generic_getpages(ap
->a_vp
, ap
->a_m
, ap
->a_count
,
389 int i
, error
, nextoff
, size
, toff
, npages
, count
;
395 struct thread
*td
= curthread
; /* XXX */
401 KKASSERT(td
->td_proc
);
402 cred
= td
->td_proc
->p_ucred
;
406 nmp
= VFSTONWFS(vp
->v_mount
);
410 if (vp
->v_object
== NULL
) {
411 kprintf("nwfs_getpages: called with non-merged cache vnode??\n");
412 return VM_PAGER_ERROR
;
415 bp
= getpbuf(&nwfs_pbuf_freecnt
);
416 npages
= btoc(count
);
417 kva
= (vm_offset_t
) bp
->b_data
;
418 pmap_qenter(kva
, pages
, npages
);
420 iov
.iov_base
= (caddr_t
) kva
;
424 uio
.uio_offset
= IDX_TO_OFF(pages
[0]->pindex
);
425 uio
.uio_resid
= count
;
426 uio
.uio_segflg
= UIO_SYSSPACE
;
427 uio
.uio_rw
= UIO_READ
;
430 error
= ncp_read(NWFSTOCONN(nmp
), &np
->n_fh
, &uio
,cred
);
431 pmap_qremove(kva
, npages
);
433 relpbuf(bp
, &nwfs_pbuf_freecnt
);
435 if (error
&& (uio
.uio_resid
== count
)) {
436 kprintf("nwfs_getpages: error %d\n",error
);
437 for (i
= 0; i
< npages
; i
++) {
438 if (ap
->a_reqpage
!= i
)
439 vnode_pager_freepage(pages
[i
]);
441 return VM_PAGER_ERROR
;
444 size
= count
- uio
.uio_resid
;
446 for (i
= 0, toff
= 0; i
< npages
; i
++, toff
= nextoff
) {
448 nextoff
= toff
+ PAGE_SIZE
;
451 m
->flags
&= ~PG_ZERO
;
453 if (nextoff
<= size
) {
454 m
->valid
= VM_PAGE_BITS_ALL
;
457 int nvalid
= ((size
+ DEV_BSIZE
- 1) - toff
) & ~(DEV_BSIZE
- 1);
458 vm_page_set_validclean(m
, 0, nvalid
);
461 if (i
!= ap
->a_reqpage
) {
463 * Whether or not to leave the page activated is up in
464 * the air, but we should put the page on a page queue
465 * somewhere (it already is in the object). Result:
466 * It appears that emperical results show that
467 * deactivating pages is best.
471 * Just in case someone was asking for this page we
472 * now tell them that it is ok to use.
475 if (m
->flags
& PG_WANTED
)
478 vm_page_deactivate(m
);
481 vnode_pager_freepage(m
);
486 #endif /* NWFS_RWCACHE */
490 * Vnode op for VM putpages.
491 * possible bug: all IO done in sync mode
492 * Note that vop_close always invalidate pages before close, so it's
493 * not necessary to open vnode.
495 * nwfs_putpages(struct vnode *a_vp, vm_page_t *a_m, int a_count,
496 * int a_sync, int *a_rtvals, vm_ooffset_t a_offset)
499 nwfs_putpages(struct vop_putpages_args
*ap
)
502 struct thread
*td
= curthread
; /* XXX */
503 struct vnode
*vp
= ap
->a_vp
;
507 KKASSERT(td
->td_proc
);
508 cred
= td
->td_proc
->p_ucred
; /* XXX */
509 VOP_OPEN(vp
, FWRITE
, cred
, NULL
);
510 error
= vnode_pager_generic_putpages(ap
->a_vp
, ap
->a_m
, ap
->a_count
,
511 ap
->a_sync
, ap
->a_rtvals
);
512 VOP_CLOSE(vp
, FWRITE
, cred
);
519 int i
, npages
, count
;
525 KKASSERT(td
->td_proc
);
526 cred
= td
->td_proc
->p_ucred
; /* XXX */
528 /* VOP_OPEN(vp, FWRITE, cred, NULL);*/
530 nmp
= VFSTONWFS(vp
->v_mount
);
533 rtvals
= ap
->a_rtvals
;
534 npages
= btoc(count
);
536 for (i
= 0; i
< npages
; i
++) {
537 rtvals
[i
] = VM_PAGER_AGAIN
;
540 bp
= getpbuf(&nwfs_pbuf_freecnt
);
541 kva
= (vm_offset_t
) bp
->b_data
;
542 pmap_qenter(kva
, pages
, npages
);
544 iov
.iov_base
= (caddr_t
) kva
;
548 uio
.uio_offset
= IDX_TO_OFF(pages
[0]->pindex
);
549 uio
.uio_resid
= count
;
550 uio
.uio_segflg
= UIO_SYSSPACE
;
551 uio
.uio_rw
= UIO_WRITE
;
553 NCPVNDEBUG("ofs=%d,resid=%d\n",(int)uio
.uio_offset
, uio
.uio_resid
);
555 error
= ncp_write(NWFSTOCONN(nmp
), &np
->n_fh
, &uio
, cred
);
556 /* VOP_CLOSE(vp, FWRITE, cred);*/
557 NCPVNDEBUG("paged write done: %d\n", error
);
559 pmap_qremove(kva
, npages
);
560 relpbuf(bp
, &nwfs_pbuf_freecnt
);
563 int nwritten
= round_page(count
- uio
.uio_resid
) / PAGE_SIZE
;
564 for (i
= 0; i
< nwritten
; i
++) {
565 rtvals
[i
] = VM_PAGER_OK
;
570 #endif /* NWFS_RWCACHE */
573 * Flush and invalidate all dirty buffers. If another process is already
574 * doing the flush, just wait for completion.
577 nwfs_vinvalbuf(struct vnode
*vp
, int flags
, int intrflg
)
579 struct nwnode
*np
= VTONW(vp
);
580 /* struct nwmount *nmp = VTONWFS(vp);*/
581 int error
= 0, slpflag
, slptimeo
;
583 if (vp
->v_flag
& VRECLAIMED
) {
593 while (np
->n_flag
& NFLUSHINPROG
) {
594 np
->n_flag
|= NFLUSHWANT
;
595 error
= tsleep((caddr_t
)&np
->n_flag
, 0, "nwfsvinv", slptimeo
);
596 error
= ncp_chkintr(NWFSTOCONN(VTONWFS(vp
)), curthread
);
597 if (error
== EINTR
&& intrflg
)
600 np
->n_flag
|= NFLUSHINPROG
;
601 error
= vinvalbuf(vp
, flags
, slpflag
, 0);
603 if (intrflg
&& (error
== ERESTART
|| error
== EINTR
)) {
604 np
->n_flag
&= ~NFLUSHINPROG
;
605 if (np
->n_flag
& NFLUSHWANT
) {
606 np
->n_flag
&= ~NFLUSHWANT
;
607 wakeup((caddr_t
)&np
->n_flag
);
611 error
= vinvalbuf(vp
, flags
, slpflag
, 0);
613 np
->n_flag
&= ~(NMODIFIED
| NFLUSHINPROG
);
614 if (np
->n_flag
& NFLUSHWANT
) {
615 np
->n_flag
&= ~NFLUSHWANT
;
616 wakeup((caddr_t
)&np
->n_flag
);