X-Git-Url: https://git.camperquake.de/gitweb.cgi?a=blobdiff_plain;f=module%2Fzfs%2Fzfs_vnops.c;h=2da5fec8672c454759bc05dd94497888f77a3be8;hb=ebe7e575eae1e03b1faa545a424f008faeac589d;hp=53d3194788092836b6358a3114cd929e87f901dc;hpb=f0b2486034f6a4d8cd1a139ff4604e6b9629a64e;p=zfs.git diff --git a/module/zfs/zfs_vnops.c b/module/zfs/zfs_vnops.c index 53d3194..2da5fec 100644 --- a/module/zfs/zfs_vnops.c +++ b/module/zfs/zfs_vnops.c @@ -63,6 +63,7 @@ #include #include #include "fs/fs_subr.h" +#include #include #include #include @@ -399,18 +400,14 @@ zfs_read(struct inode *ip, uio_t *uio, int ioflag, cred_t *cr) return (0); } -#ifdef HAVE_MANDLOCKS /* * Check for mandatory locks */ - if (MANDMODE(zp->z_mode)) { - if (error = chklock(ip, FREAD, - uio->uio_loffset, uio->uio_resid, uio->uio_fmode, ct)) { - ZFS_EXIT(zsb); - return (error); - } + if (mandatory_lock(ip) && + !lock_may_read(ip, uio->uio_loffset, uio->uio_resid)) { + ZFS_EXIT(zsb); + return (EAGAIN); } -#endif /* HAVE_MANDLOCK */ /* * If we're in FRSYNC mode, sync out this znode before reading it. @@ -581,17 +578,14 @@ zfs_write(struct inode *ip, uio_t *uio, int ioflag, cred_t *cr) return (EINVAL); } -#ifdef HAVE_MANDLOCKS /* * Check for mandatory locks before calling zfs_range_lock() * in order to prevent a deadlock with locks set via fcntl(). */ - if (MANDMODE((mode_t)zp->z_mode) && - (error = chklock(ip, FWRITE, woff, n, uio->uio_fmode, ct)) != 0) { + if (mandatory_lock(ip) && !lock_may_write(ip, woff, n)) { ZFS_EXIT(zsb); - return (error); + return (EAGAIN); } -#endif /* HAVE_MANDLOCKS */ #ifdef HAVE_UIO_ZEROCOPY /* @@ -1120,14 +1114,6 @@ zfs_lookup(struct inode *dip, char *nm, struct inode **ipp, int flags, if (flags & LOOKUP_XATTR) { /* - * If the xattr property is off, refuse the lookup request. - */ - if (!(zsb->z_flags & ZSB_XATTR_USER)) { - ZFS_EXIT(zsb); - return (EINVAL); - } - - /* * We don't allow recursive attributes.. * Maybe someday we will. */ @@ -2027,12 +2013,22 @@ zfs_readdir(struct inode *ip, void *dirent, filldir_t filldir, goto update; } + /* + * Allow multiple entries provided the first entry is + * the object id. Non-zpl consumers may safely make + * use of the additional space. + * + * XXX: This should be a feature flag for compatibility + */ if (zap.za_integer_length != 8 || - zap.za_num_integers != 1) { + zap.za_num_integers == 0) { cmn_err(CE_WARN, "zap_readdir: bad directory " - "entry, obj = %lld, offset = %lld\n", + "entry, obj = %lld, offset = %lld, " + "length = %d, num = %lld\n", (u_longlong_t)zp->z_id, - (u_longlong_t)*pos); + (u_longlong_t)*pos, + zap.za_integer_length, + (u_longlong_t)zap.za_num_integers); error = ENXIO; goto update; } @@ -2050,7 +2046,7 @@ zfs_readdir(struct inode *ip, void *dirent, filldir_t filldir, dmu_prefetch(os, objnum, 0, 0); } - if (*pos >= 2) { + if (*pos > 2 || (*pos == 2 && !zfs_show_ctldir(zp))) { zap_cursor_advance(&zc); *pos = zap_cursor_serialize(&zc); } else { @@ -2298,6 +2294,49 @@ zfs_getattr(struct inode *ip, vattr_t *vap, int flags, cred_t *cr) EXPORT_SYMBOL(zfs_getattr); /* + * Get the basic file attributes and place them in the provided kstat + * structure. The inode is assumed to be the authoritative source + * for most of the attributes. However, the znode currently has the + * authoritative atime, blksize, and block count. + * + * IN: ip - inode of file. + * + * OUT: sp - kstat values. + * + * RETURN: 0 (always succeeds) + */ +/* ARGSUSED */ +int +zfs_getattr_fast(struct inode *ip, struct kstat *sp) +{ + znode_t *zp = ITOZ(ip); + zfs_sb_t *zsb = ITOZSB(ip); + + ZFS_ENTER(zsb); + ZFS_VERIFY_ZP(zp); + + mutex_enter(&zp->z_lock); + + generic_fillattr(ip, sp); + ZFS_TIME_DECODE(&sp->atime, zp->z_atime); + + sa_object_size(zp->z_sa_hdl, (uint32_t *)&sp->blksize, &sp->blocks); + if (unlikely(zp->z_blksz == 0)) { + /* + * Block size hasn't been set; suggest maximal I/O transfers. + */ + sp->blksize = zsb->z_max_blksz; + } + + mutex_exit(&zp->z_lock); + + ZFS_EXIT(zsb); + + return (0); +} +EXPORT_SYMBOL(zfs_getattr_fast); + +/* * Set the file attributes to the values contained in the * vattr structure. * @@ -2420,7 +2459,7 @@ top: aclp = NULL; /* Can this be moved to before the top label? */ - if (zsb->z_vfs->mnt_flags & MNT_READONLY) { + if (zfs_is_readonly(zsb)) { err = EROFS; goto out3; } @@ -3712,141 +3751,123 @@ top: } EXPORT_SYMBOL(zfs_link); +static void +zfs_putpage_commit_cb(void *arg, int error) +{ + struct page *pp = arg; + + if (error) { + __set_page_dirty_nobuffers(pp); + + if (error != ECANCELED) + SetPageError(pp); + } else { + ClearPageError(pp); + } + + end_page_writeback(pp); +} + /* - * Push a page out to disk - * - * IN: vp - file to push page to. - * pp - page to push. - * off - start of range pushed. - * len - len of range pushed. + * Push a page out to disk, once the page is on stable storage the + * registered commit callback will be run as notification of completion. * + * IN: ip - page mapped for inode. + * pp - page to push (page is locked) + * wbc - writeback control data * * RETURN: 0 if success * error code if failure * - * NOTE: callers must have locked the page to be pushed. + * Timestamps: + * ip - ctime|mtime updated */ /* ARGSUSED */ -static int -zfs_putapage(struct inode *ip, struct page *pp, u_offset_t off, size_t len) +int +zfs_putpage(struct inode *ip, struct page *pp, struct writeback_control *wbc) { - znode_t *zp = ITOZ(ip); - zfs_sb_t *zsb = ITOZSB(ip); - dmu_tx_t *tx; - caddr_t va; - int err; + znode_t *zp = ITOZ(ip); + zfs_sb_t *zsb = ITOZSB(ip); + loff_t offset; + loff_t pgoff; + unsigned int pglen; + dmu_tx_t *tx; + caddr_t va; + int err = 0; + uint64_t mtime[2], ctime[2]; + sa_bulk_attr_t bulk[3]; + int cnt = 0; + + + ASSERT(PageLocked(pp)); + pgoff = page_offset(pp); /* Page byte-offset in file */ + offset = i_size_read(ip); /* File length in bytes */ + pglen = MIN(PAGE_CACHE_SIZE, /* Page length in bytes */ + P2ROUNDUP(offset, PAGE_CACHE_SIZE)-pgoff); + + /* Page is beyond end of file */ + if (pgoff >= offset) { + unlock_page(pp); + return (0); + } + + /* Truncate page length to end of file */ + if (pgoff + pglen > offset) + pglen = offset - pgoff; + +#if 0 /* - * Can't push pages past end-of-file. + * FIXME: Allow mmap writes past its quota. The correct fix + * is to register a page_mkwrite() handler to count the page + * against its quota when it is about to be dirtied. */ - if (off >= zp->z_size) { - /* ignore all pages */ - err = 0; - goto out; - } else if (off + len > zp->z_size) - len = zp->z_size - off; - if (zfs_owner_overquota(zsb, zp, B_FALSE) || zfs_owner_overquota(zsb, zp, B_TRUE)) { err = EDQUOT; - goto out; } -top: +#endif + + set_page_writeback(pp); + unlock_page(pp); + tx = dmu_tx_create(zsb->z_os); - dmu_tx_hold_write(tx, zp->z_id, off, len); + + dmu_tx_callback_register(tx, zfs_putpage_commit_cb, pp); + + dmu_tx_hold_write(tx, zp->z_id, pgoff, pglen); dmu_tx_hold_sa(tx, zp->z_sa_hdl, B_FALSE); zfs_sa_upgrade_txholds(tx, zp); err = dmu_tx_assign(tx, TXG_NOWAIT); if (err != 0) { - if (err == ERESTART) { + if (err == ERESTART) dmu_tx_wait(tx); - dmu_tx_abort(tx); - goto top; - } + dmu_tx_abort(tx); - goto out; + return (err); } va = kmap(pp); - ASSERT3U(len, <=, PAGESIZE); - dmu_write(zsb->z_os, zp->z_id, off, len, va, tx); + ASSERT3U(pglen, <=, PAGE_CACHE_SIZE); + dmu_write(zsb->z_os, zp->z_id, pgoff, pglen, va, tx); kunmap(pp); - if (err == 0) { - uint64_t mtime[2], ctime[2]; - sa_bulk_attr_t bulk[3]; - int count = 0; + SA_ADD_BULK_ATTR(bulk, cnt, SA_ZPL_MTIME(zsb), NULL, &mtime, 16); + SA_ADD_BULK_ATTR(bulk, cnt, SA_ZPL_CTIME(zsb), NULL, &ctime, 16); + SA_ADD_BULK_ATTR(bulk, cnt, SA_ZPL_FLAGS(zsb), NULL, &zp->z_pflags, 8); + zfs_tstamp_update_setup(zp, CONTENT_MODIFIED, mtime, ctime, B_TRUE); + zfs_log_write(zsb->z_log, tx, TX_WRITE, zp, pgoff, pglen, 0); - SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_MTIME(zsb), NULL, - &mtime, 16); - SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_CTIME(zsb), NULL, - &ctime, 16); - SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_FLAGS(zsb), NULL, - &zp->z_pflags, 8); - zfs_tstamp_update_setup(zp, CONTENT_MODIFIED, mtime, ctime, - B_TRUE); - zfs_log_write(zsb->z_log, tx, TX_WRITE, zp, off, len, 0); - } dmu_tx_commit(tx); + ASSERT3S(err, ==, 0); -out: - return (err); -} - -/* - * Copy the portion of the file indicated from page into the file. - * - * IN: ip - inode of file to push page data to. - * wbc - Unused parameter - * data - pointer to address_space - * - * RETURN: 0 if success - * error code if failure - * - * Timestamps: - * vp - ctime|mtime updated - */ -/*ARGSUSED*/ -int -zfs_putpage(struct page *page, struct writeback_control *wbc, void *data) -{ - struct address_space *mapping = data; - struct inode *ip = mapping->host; - znode_t *zp = ITOZ(ip); - zfs_sb_t *zsb = ITOZSB(ip); - rl_t *rl; - u_offset_t io_off; - size_t io_len; - size_t len; - int error; - - io_off = page_offset(page); - io_len = PAGESIZE; - - ZFS_ENTER(zsb); - ZFS_VERIFY_ZP(zp); - - rl = zfs_range_lock(zp, io_off, io_len, RL_WRITER); - - if (io_off > zp->z_size) { - /* past end of file */ - zfs_range_unlock(rl); - ZFS_EXIT(zsb); - return (0); - } - - len = MIN(io_len, P2ROUNDUP(zp->z_size, PAGESIZE) - io_off); - - error = zfs_putapage(ip, page, io_off, len); - zfs_range_unlock(rl); - - if (zsb->z_os->os_sync == ZFS_SYNC_ALWAYS) + if ((zsb->z_os->os_sync == ZFS_SYNC_ALWAYS) || + (wbc->sync_mode == WB_SYNC_ALL)) zil_commit(zsb->z_log, zp->z_id); - ZFS_EXIT(zsb); - return (error); + + return (err); } -EXPORT_SYMBOL(zfs_putpage); /*ARGSUSED*/ void @@ -3856,9 +3877,10 @@ zfs_inactive(struct inode *ip) zfs_sb_t *zsb = ITOZSB(ip); int error; -#ifdef HAVE_SNAPSHOT - /* Early return for snapshot inode? */ -#endif /* HAVE_SNAPSHOT */ + if (zfsctl_is_node(ip)) { + zfsctl_inode_inactive(ip); + return; + } rw_enter(&zsb->z_teardown_inactive_lock, RW_READER); if (zp->z_sa_hdl == NULL) { @@ -3995,144 +4017,50 @@ zfs_getpage(struct inode *ip, struct page *pl[], int nr_pages) } EXPORT_SYMBOL(zfs_getpage); -#ifdef HAVE_MMAP /* - * Request a memory map for a section of a file. This code interacts - * with common code and the VM system as follows: - * - * common code calls mmap(), which ends up in smmap_common() + * Check ZFS specific permissions to memory map a section of a file. * - * this calls VOP_MAP(), which takes you into (say) zfs + * IN: ip - inode of the file to mmap + * off - file offset + * addrp - start address in memory region + * len - length of memory region + * vm_flags- address flags * - * zfs_map() calls as_map(), passing segvn_create() as the callback - * - * segvn_create() creates the new segment and calls VOP_ADDMAP() - * - * zfs_addmap() updates z_mapcnt + * RETURN: 0 if success + * error code if failure */ /*ARGSUSED*/ -static int -zfs_map(vnode_t *vp, offset_t off, struct as *as, caddr_t *addrp, - size_t len, uchar_t prot, uchar_t maxprot, uint_t flags, cred_t *cr) +int +zfs_map(struct inode *ip, offset_t off, caddr_t *addrp, size_t len, + unsigned long vm_flags) { - znode_t *zp = VTOZ(vp); - zfsvfs_t *zfsvfs = zp->z_zfsvfs; - segvn_crargs_t vn_a; - int error; + znode_t *zp = ITOZ(ip); + zfs_sb_t *zsb = ITOZSB(ip); - ZFS_ENTER(zfsvfs); + ZFS_ENTER(zsb); ZFS_VERIFY_ZP(zp); - if ((prot & PROT_WRITE) && (zp->z_pflags & + if ((vm_flags & VM_WRITE) && (zp->z_pflags & (ZFS_IMMUTABLE | ZFS_READONLY | ZFS_APPENDONLY))) { - ZFS_EXIT(zfsvfs); + ZFS_EXIT(zsb); return (EPERM); } - if ((prot & (PROT_READ | PROT_EXEC)) && + if ((vm_flags & (VM_READ | VM_EXEC)) && (zp->z_pflags & ZFS_AV_QUARANTINED)) { - ZFS_EXIT(zfsvfs); + ZFS_EXIT(zsb); return (EACCES); } - if (vp->v_flag & VNOMAP) { - ZFS_EXIT(zfsvfs); - return (ENOSYS); - } - if (off < 0 || len > MAXOFFSET_T - off) { - ZFS_EXIT(zfsvfs); + ZFS_EXIT(zsb); return (ENXIO); } - if (vp->v_type != VREG) { - ZFS_EXIT(zfsvfs); - return (ENODEV); - } - - /* - * If file is locked, disallow mapping. - */ - if (MANDMODE(zp->z_mode) && vn_has_flocks(vp)) { - ZFS_EXIT(zfsvfs); - return (EAGAIN); - } - - as_rangelock(as); - error = choose_addr(as, addrp, len, off, ADDR_VACALIGN, flags); - if (error != 0) { - as_rangeunlock(as); - ZFS_EXIT(zfsvfs); - return (error); - } - - vn_a.vp = vp; - vn_a.offset = (u_offset_t)off; - vn_a.type = flags & MAP_TYPE; - vn_a.prot = prot; - vn_a.maxprot = maxprot; - vn_a.cred = cr; - vn_a.amp = NULL; - vn_a.flags = flags & ~MAP_TYPE; - vn_a.szc = 0; - vn_a.lgrp_mem_policy_flags = 0; - - error = as_map(as, *addrp, len, segvn_create, &vn_a); - - as_rangeunlock(as); - ZFS_EXIT(zfsvfs); - return (error); -} - -/* ARGSUSED */ -static int -zfs_addmap(vnode_t *vp, offset_t off, struct as *as, caddr_t addr, - size_t len, uchar_t prot, uchar_t maxprot, uint_t flags, cred_t *cr) -{ - uint64_t pages = btopr(len); - - atomic_add_64(&VTOZ(vp)->z_mapcnt, pages); - return (0); -} - -/* - * The reason we push dirty pages as part of zfs_delmap() is so that we get a - * more accurate mtime for the associated file. Since we don't have a way of - * detecting when the data was actually modified, we have to resort to - * heuristics. If an explicit msync() is done, then we mark the mtime when the - * last page is pushed. The problem occurs when the msync() call is omitted, - * which by far the most common case: - * - * open() - * mmap() - * - * munmap() - * close() - *