Changeset View
Changeset View
Standalone View
Standalone View
sys/contrib/openzfs/module/os/linux/zfs/zfs_vnops_os.c
Show First 20 Lines • Show All 81 Lines • ▼ Show 20 Lines | |||||
* to freed memory. The example below illustrates the following Big Rules: | * to freed memory. The example below illustrates the following Big Rules: | ||||
* | * | ||||
* (1) A check must be made in each zfs thread for a mounted file system. | * (1) A check must be made in each zfs thread for a mounted file system. | ||||
* This is done avoiding races using ZFS_ENTER(zfsvfs). | * This is done avoiding races using ZFS_ENTER(zfsvfs). | ||||
* A ZFS_EXIT(zfsvfs) is needed before all returns. Any znodes | * A ZFS_EXIT(zfsvfs) is needed before all returns. Any znodes | ||||
* must be checked with ZFS_VERIFY_ZP(zp). Both of these macros | * must be checked with ZFS_VERIFY_ZP(zp). Both of these macros | ||||
* can return EIO from the calling function. | * can return EIO from the calling function. | ||||
* | * | ||||
* (2) zrele() should always be the last thing except for zil_commit() | * (2) zrele() should always be the last thing except for zil_commit() (if | ||||
* (if necessary) and ZFS_EXIT(). This is for 3 reasons: | * necessary) and ZFS_EXIT(). This is for 3 reasons: First, if it's the | ||||
* First, if it's the last reference, the vnode/znode | * last reference, the vnode/znode can be freed, so the zp may point to | ||||
* can be freed, so the zp may point to freed memory. Second, the last | * freed memory. Second, the last reference will call zfs_zinactive(), | ||||
* reference will call zfs_zinactive(), which may induce a lot of work -- | * which may induce a lot of work -- pushing cached pages (which acquires | ||||
* pushing cached pages (which acquires range locks) and syncing out | * range locks) and syncing out cached atime changes. Third, | ||||
* cached atime changes. Third, zfs_zinactive() may require a new tx, | * zfs_zinactive() may require a new tx, which could deadlock the system | ||||
* which could deadlock the system if you were already holding one. | * if you were already holding one. This deadlock occurs because the tx | ||||
* If you must call zrele() within a tx then use zfs_zrele_async(). | * currently being operated on prevents a txg from syncing, which | ||||
* prevents the new tx from progressing, resulting in a deadlock. If you | |||||
* must call zrele() within a tx, use zfs_zrele_async(). Note that iput() | |||||
* is a synonym for zrele(). | |||||
* | * | ||||
* (3) All range locks must be grabbed before calling dmu_tx_assign(), | * (3) All range locks must be grabbed before calling dmu_tx_assign(), | ||||
* as they can span dmu_tx_assign() calls. | * as they can span dmu_tx_assign() calls. | ||||
* | * | ||||
* (4) If ZPL locks are held, pass TXG_NOWAIT as the second argument to | * (4) If ZPL locks are held, pass TXG_NOWAIT as the second argument to | ||||
* dmu_tx_assign(). This is critical because we don't want to block | * dmu_tx_assign(). This is critical because we don't want to block | ||||
* while holding locks. | * while holding locks. | ||||
* | * | ||||
▲ Show 20 Lines • Show All 186 Lines • ▼ Show 20 Lines | |||||
* | * | ||||
* On Read: We "read" preferentially from memory mapped pages, | * On Read: We "read" preferentially from memory mapped pages, | ||||
* else we default from the dmu buffer. | * else we default from the dmu buffer. | ||||
* | * | ||||
* NOTE: We will always "break up" the IO into PAGESIZE uiomoves when | * NOTE: We will always "break up" the IO into PAGESIZE uiomoves when | ||||
* the file is memory mapped. | * the file is memory mapped. | ||||
*/ | */ | ||||
int | int | ||||
mappedread(znode_t *zp, int nbytes, uio_t *uio) | mappedread(znode_t *zp, int nbytes, zfs_uio_t *uio) | ||||
{ | { | ||||
struct inode *ip = ZTOI(zp); | struct inode *ip = ZTOI(zp); | ||||
struct address_space *mp = ip->i_mapping; | struct address_space *mp = ip->i_mapping; | ||||
struct page *pp; | struct page *pp; | ||||
int64_t start, off; | int64_t start, off; | ||||
uint64_t bytes; | uint64_t bytes; | ||||
int len = nbytes; | int len = nbytes; | ||||
int error = 0; | int error = 0; | ||||
void *pb; | void *pb; | ||||
start = uio->uio_loffset; | start = uio->uio_loffset; | ||||
off = start & (PAGE_SIZE-1); | off = start & (PAGE_SIZE-1); | ||||
for (start &= PAGE_MASK; len > 0; start += PAGE_SIZE) { | for (start &= PAGE_MASK; len > 0; start += PAGE_SIZE) { | ||||
bytes = MIN(PAGE_SIZE - off, len); | bytes = MIN(PAGE_SIZE - off, len); | ||||
pp = find_lock_page(mp, start >> PAGE_SHIFT); | pp = find_lock_page(mp, start >> PAGE_SHIFT); | ||||
if (pp) { | if (pp) { | ||||
ASSERT(PageUptodate(pp)); | ASSERT(PageUptodate(pp)); | ||||
unlock_page(pp); | unlock_page(pp); | ||||
pb = kmap(pp); | pb = kmap(pp); | ||||
error = uiomove(pb + off, bytes, UIO_READ, uio); | error = zfs_uiomove(pb + off, bytes, UIO_READ, uio); | ||||
kunmap(pp); | kunmap(pp); | ||||
if (mapping_writably_mapped(mp)) | if (mapping_writably_mapped(mp)) | ||||
flush_dcache_page(pp); | flush_dcache_page(pp); | ||||
mark_page_accessed(pp); | mark_page_accessed(pp); | ||||
put_page(pp); | put_page(pp); | ||||
} else { | } else { | ||||
Show All 35 Lines | |||||
{ | { | ||||
fstrans_cookie_t cookie; | fstrans_cookie_t cookie; | ||||
int error; | int error; | ||||
struct iovec iov; | struct iovec iov; | ||||
iov.iov_base = (void *)data; | iov.iov_base = (void *)data; | ||||
iov.iov_len = len; | iov.iov_len = len; | ||||
uio_t uio; | zfs_uio_t uio; | ||||
uio_iovec_init(&uio, &iov, 1, pos, UIO_SYSSPACE, len, 0); | zfs_uio_iovec_init(&uio, &iov, 1, pos, UIO_SYSSPACE, len, 0); | ||||
cookie = spl_fstrans_mark(); | cookie = spl_fstrans_mark(); | ||||
error = zfs_write(zp, &uio, 0, kcred); | error = zfs_write(zp, &uio, 0, kcred); | ||||
spl_fstrans_unmark(cookie); | spl_fstrans_unmark(cookie); | ||||
if (error == 0) { | if (error == 0) { | ||||
if (residp != NULL) | if (residp != NULL) | ||||
*residp = uio_resid(&uio); | *residp = zfs_uio_resid(&uio); | ||||
else if (uio_resid(&uio) != 0) | else if (zfs_uio_resid(&uio) != 0) | ||||
error = SET_ERROR(EIO); | error = SET_ERROR(EIO); | ||||
} | } | ||||
return (error); | return (error); | ||||
} | } | ||||
void | void | ||||
zfs_zrele_async(znode_t *zp) | zfs_zrele_async(znode_t *zp) | ||||
{ | { | ||||
struct inode *ip = ZTOI(zp); | struct inode *ip = ZTOI(zp); | ||||
objset_t *os = ITOZSB(ip)->z_os; | objset_t *os = ITOZSB(ip)->z_os; | ||||
ASSERT(atomic_read(&ip->i_count) > 0); | ASSERT(atomic_read(&ip->i_count) > 0); | ||||
ASSERT(os != NULL); | ASSERT(os != NULL); | ||||
if (atomic_read(&ip->i_count) == 1) | /* | ||||
* If decrementing the count would put us at 0, we can't do it inline | |||||
* here, because that would be synchronous. Instead, dispatch an iput | |||||
* to run later. | |||||
* | |||||
* For more information on the dangers of a synchronous iput, see the | |||||
* header comment of this file. | |||||
*/ | |||||
if (!atomic_add_unless(&ip->i_count, -1, 1)) { | |||||
VERIFY(taskq_dispatch(dsl_pool_zrele_taskq(dmu_objset_pool(os)), | VERIFY(taskq_dispatch(dsl_pool_zrele_taskq(dmu_objset_pool(os)), | ||||
(task_func_t *)iput, ip, TQ_SLEEP) != TASKQID_INVALID); | (task_func_t *)iput, ip, TQ_SLEEP) != TASKQID_INVALID); | ||||
else | |||||
zrele(zp); | |||||
} | } | ||||
} | |||||
/* | /* | ||||
* Lookup an entry in a directory, or an extended attribute directory. | * Lookup an entry in a directory, or an extended attribute directory. | ||||
* If it exists, return a held inode reference for it. | * If it exists, return a held inode reference for it. | ||||
* | * | ||||
* IN: zdp - znode of directory to search. | * IN: zdp - znode of directory to search. | ||||
* nm - name of entry to lookup. | * nm - name of entry to lookup. | ||||
▲ Show 20 Lines • Show All 96 Lines • ▼ Show 20 Lines | zfs_lookup(znode_t *zdp, char *nm, znode_t **zpp, int flags, cred_t *cr, | ||||
if (zfsvfs->z_utf8 && u8_validate(nm, strlen(nm), | if (zfsvfs->z_utf8 && u8_validate(nm, strlen(nm), | ||||
NULL, U8_VALIDATE_ENTIRE, &error) < 0) { | NULL, U8_VALIDATE_ENTIRE, &error) < 0) { | ||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (SET_ERROR(EILSEQ)); | return (SET_ERROR(EILSEQ)); | ||||
} | } | ||||
error = zfs_dirlook(zdp, nm, zpp, flags, direntflags, realpnp); | error = zfs_dirlook(zdp, nm, zpp, flags, direntflags, realpnp); | ||||
if ((error == 0) && (*zpp)) | if ((error == 0) && (*zpp)) | ||||
zfs_inode_update(*zpp); | zfs_znode_update_vfs(*zpp); | ||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (error); | return (error); | ||||
} | } | ||||
/* | /* | ||||
* Attempt to create a new entry in a directory. If the entry | * Attempt to create a new entry in a directory. If the entry | ||||
* already exists, truncate the file if permissible, else return | * already exists, truncate the file if permissible, else return | ||||
▲ Show 20 Lines • Show All 246 Lines • ▼ Show 20 Lines | out: | ||||
if (dl) | if (dl) | ||||
zfs_dirent_unlock(dl); | zfs_dirent_unlock(dl); | ||||
if (error) { | if (error) { | ||||
if (zp) | if (zp) | ||||
zrele(zp); | zrele(zp); | ||||
} else { | } else { | ||||
zfs_inode_update(dzp); | zfs_znode_update_vfs(dzp); | ||||
zfs_inode_update(zp); | zfs_znode_update_vfs(zp); | ||||
*zpp = zp; | *zpp = zp; | ||||
} | } | ||||
if (zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS) | if (zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS) | ||||
zil_commit(zilog, 0); | zil_commit(zilog, 0); | ||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (error); | return (error); | ||||
▲ Show 20 Lines • Show All 105 Lines • ▼ Show 20 Lines | top: | ||||
zfs_acl_ids_free(&acl_ids); | zfs_acl_ids_free(&acl_ids); | ||||
dmu_tx_commit(tx); | dmu_tx_commit(tx); | ||||
out: | out: | ||||
if (error) { | if (error) { | ||||
if (zp) | if (zp) | ||||
zrele(zp); | zrele(zp); | ||||
} else { | } else { | ||||
zfs_inode_update(dzp); | zfs_znode_update_vfs(dzp); | ||||
zfs_inode_update(zp); | zfs_znode_update_vfs(zp); | ||||
*ipp = ZTOI(zp); | *ipp = ZTOI(zp); | ||||
} | } | ||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (error); | return (error); | ||||
} | } | ||||
/* | /* | ||||
▲ Show 20 Lines • Show All 209 Lines • ▼ Show 20 Lines | top: | ||||
zfs_log_remove(zilog, tx, txtype, dzp, name, obj, unlinked); | zfs_log_remove(zilog, tx, txtype, dzp, name, obj, unlinked); | ||||
dmu_tx_commit(tx); | dmu_tx_commit(tx); | ||||
out: | out: | ||||
if (realnmp) | if (realnmp) | ||||
pn_free(realnmp); | pn_free(realnmp); | ||||
zfs_dirent_unlock(dl); | zfs_dirent_unlock(dl); | ||||
zfs_inode_update(dzp); | zfs_znode_update_vfs(dzp); | ||||
zfs_inode_update(zp); | zfs_znode_update_vfs(zp); | ||||
if (delete_now) | if (delete_now) | ||||
zrele(zp); | zrele(zp); | ||||
else | else | ||||
zfs_zrele_async(zp); | zfs_zrele_async(zp); | ||||
if (xzp) { | if (xzp) { | ||||
zfs_inode_update(xzp); | zfs_znode_update_vfs(xzp); | ||||
zfs_zrele_async(xzp); | zfs_zrele_async(xzp); | ||||
} | } | ||||
if (zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS) | if (zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS) | ||||
zil_commit(zilog, 0); | zil_commit(zilog, 0); | ||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (error); | return (error); | ||||
▲ Show 20 Lines • Show All 180 Lines • ▼ Show 20 Lines | out: | ||||
zfs_dirent_unlock(dl); | zfs_dirent_unlock(dl); | ||||
if (zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS) | if (zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS) | ||||
zil_commit(zilog, 0); | zil_commit(zilog, 0); | ||||
if (error != 0) { | if (error != 0) { | ||||
zrele(zp); | zrele(zp); | ||||
} else { | } else { | ||||
zfs_inode_update(dzp); | zfs_znode_update_vfs(dzp); | ||||
zfs_inode_update(zp); | zfs_znode_update_vfs(zp); | ||||
} | } | ||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (error); | return (error); | ||||
} | } | ||||
/* | /* | ||||
* Remove a directory subdir entry. If the current working | * Remove a directory subdir entry. If the current working | ||||
* directory is the same as the subdir to be removed, the | * directory is the same as the subdir to be removed, the | ||||
▲ Show 20 Lines • Show All 108 Lines • ▼ Show 20 Lines | top: | ||||
dmu_tx_commit(tx); | dmu_tx_commit(tx); | ||||
rw_exit(&zp->z_parent_lock); | rw_exit(&zp->z_parent_lock); | ||||
rw_exit(&zp->z_name_lock); | rw_exit(&zp->z_name_lock); | ||||
out: | out: | ||||
zfs_dirent_unlock(dl); | zfs_dirent_unlock(dl); | ||||
zfs_inode_update(dzp); | zfs_znode_update_vfs(dzp); | ||||
zfs_inode_update(zp); | zfs_znode_update_vfs(zp); | ||||
zrele(zp); | zrele(zp); | ||||
if (zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS) | if (zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS) | ||||
zil_commit(zilog, 0); | zil_commit(zilog, 0); | ||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (error); | return (error); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 1,053 Lines • ▼ Show 20 Lines | if (err) { | ||||
if (count > 0) | if (count > 0) | ||||
err2 = sa_bulk_update(zp->z_sa_hdl, bulk, count, tx); | err2 = sa_bulk_update(zp->z_sa_hdl, bulk, count, tx); | ||||
dmu_tx_commit(tx); | dmu_tx_commit(tx); | ||||
if (attrzp) { | if (attrzp) { | ||||
if (err2 == 0 && handle_eadir) | if (err2 == 0 && handle_eadir) | ||||
err2 = zfs_setattr_dir(attrzp); | err2 = zfs_setattr_dir(attrzp); | ||||
zrele(attrzp); | zrele(attrzp); | ||||
} | } | ||||
zfs_inode_update(zp); | zfs_znode_update_vfs(zp); | ||||
} | } | ||||
out2: | out2: | ||||
if (os->os_sync == ZFS_SYNC_ALWAYS) | if (os->os_sync == ZFS_SYNC_ALWAYS) | ||||
zil_commit(zilog, 0); | zil_commit(zilog, 0); | ||||
out3: | out3: | ||||
kmem_free(xattr_bulk, sizeof (sa_bulk_attr_t) * bulks); | kmem_free(xattr_bulk, sizeof (sa_bulk_attr_t) * bulks); | ||||
▲ Show 20 Lines • Show All 441 Lines • ▼ Show 20 Lines | top: | ||||
dmu_tx_commit(tx); | dmu_tx_commit(tx); | ||||
out: | out: | ||||
if (zl != NULL) | if (zl != NULL) | ||||
zfs_rename_unlock(&zl); | zfs_rename_unlock(&zl); | ||||
zfs_dirent_unlock(sdl); | zfs_dirent_unlock(sdl); | ||||
zfs_dirent_unlock(tdl); | zfs_dirent_unlock(tdl); | ||||
zfs_inode_update(sdzp); | zfs_znode_update_vfs(sdzp); | ||||
if (sdzp == tdzp) | if (sdzp == tdzp) | ||||
rw_exit(&sdzp->z_name_lock); | rw_exit(&sdzp->z_name_lock); | ||||
if (sdzp != tdzp) | if (sdzp != tdzp) | ||||
zfs_inode_update(tdzp); | zfs_znode_update_vfs(tdzp); | ||||
zfs_inode_update(szp); | zfs_znode_update_vfs(szp); | ||||
zrele(szp); | zrele(szp); | ||||
if (tzp) { | if (tzp) { | ||||
zfs_inode_update(tzp); | zfs_znode_update_vfs(tzp); | ||||
zrele(tzp); | zrele(tzp); | ||||
} | } | ||||
if (zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS) | if (zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS) | ||||
zil_commit(zilog, 0); | zil_commit(zilog, 0); | ||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (error); | return (error); | ||||
▲ Show 20 Lines • Show All 142 Lines • ▼ Show 20 Lines | top: | ||||
if (error != 0) { | if (error != 0) { | ||||
zfs_znode_delete(zp, tx); | zfs_znode_delete(zp, tx); | ||||
remove_inode_hash(ZTOI(zp)); | remove_inode_hash(ZTOI(zp)); | ||||
} else { | } else { | ||||
if (flags & FIGNORECASE) | if (flags & FIGNORECASE) | ||||
txtype |= TX_CI; | txtype |= TX_CI; | ||||
zfs_log_symlink(zilog, tx, txtype, dzp, zp, name, link); | zfs_log_symlink(zilog, tx, txtype, dzp, zp, name, link); | ||||
zfs_inode_update(dzp); | zfs_znode_update_vfs(dzp); | ||||
zfs_inode_update(zp); | zfs_znode_update_vfs(zp); | ||||
} | } | ||||
zfs_acl_ids_free(&acl_ids); | zfs_acl_ids_free(&acl_ids); | ||||
dmu_tx_commit(tx); | dmu_tx_commit(tx); | ||||
zfs_dirent_unlock(dl); | zfs_dirent_unlock(dl); | ||||
Show All 21 Lines | |||||
* RETURN: 0 if success | * RETURN: 0 if success | ||||
* error code if failure | * error code if failure | ||||
* | * | ||||
* Timestamps: | * Timestamps: | ||||
* ip - atime updated | * ip - atime updated | ||||
*/ | */ | ||||
/* ARGSUSED */ | /* ARGSUSED */ | ||||
int | int | ||||
zfs_readlink(struct inode *ip, uio_t *uio, cred_t *cr) | zfs_readlink(struct inode *ip, zfs_uio_t *uio, cred_t *cr) | ||||
{ | { | ||||
znode_t *zp = ITOZ(ip); | znode_t *zp = ITOZ(ip); | ||||
zfsvfs_t *zfsvfs = ITOZSB(ip); | zfsvfs_t *zfsvfs = ITOZSB(ip); | ||||
int error; | int error; | ||||
ZFS_ENTER(zfsvfs); | ZFS_ENTER(zfsvfs); | ||||
ZFS_VERIFY_ZP(zp); | ZFS_VERIFY_ZP(zp); | ||||
▲ Show 20 Lines • Show All 194 Lines • ▼ Show 20 Lines | top: | ||||
zfs_dirent_unlock(dl); | zfs_dirent_unlock(dl); | ||||
if (!is_tmpfile && zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS) | if (!is_tmpfile && zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS) | ||||
zil_commit(zilog, 0); | zil_commit(zilog, 0); | ||||
if (is_tmpfile && zfsvfs->z_os->os_sync != ZFS_SYNC_DISABLED) | if (is_tmpfile && zfsvfs->z_os->os_sync != ZFS_SYNC_DISABLED) | ||||
txg_wait_synced(dmu_objset_pool(zfsvfs->z_os), txg); | txg_wait_synced(dmu_objset_pool(zfsvfs->z_os), txg); | ||||
zfs_inode_update(tdzp); | zfs_znode_update_vfs(tdzp); | ||||
zfs_inode_update(szp); | zfs_znode_update_vfs(szp); | ||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (error); | return (error); | ||||
} | } | ||||
static void | static void | ||||
zfs_putpage_commit_cb(void *arg) | zfs_putpage_commit_cb(void *arg) | ||||
{ | { | ||||
struct page *pp = arg; | struct page *pp = arg; | ||||
▲ Show 20 Lines • Show All 579 Lines • Show Last 20 Lines |