Merge pull request #329 from versity/clk/iomap_ci

iomap changes
This commit is contained in:
Zach Brown
2026-07-28 10:13:27 -07:00
committed by GitHub
21 changed files with 2221 additions and 411 deletions
+1
View File
@@ -25,6 +25,7 @@ scoutfs-y += \
forest.o \
inode.o \
ioctl.o \
iomap.o \
item.o \
kernelcompat.o \
lock.o \
+10
View File
@@ -222,3 +222,13 @@ endif
ifneq (,$(shell grep 'define timer_container_of' include/linux/timer.h))
ccflags-y += -DKC_TIMER_CONTAINER_OF
endif
#
# v6.4-rc1-4f80818b4a58c
#
# iomap needs iterator nofault support in the read/write paths. We can still
# use iomap for fiemap and llseek though.
#
ifneq (,$(shell grep 'copy_page_to_iter_nofault' include/linux/uio.h))
ccflags-y += -DKC_USE_IOMAP_FOR_IO
endif
+240 -249
View File
@@ -21,9 +21,11 @@
#include <linux/log2.h>
#include <linux/falloc.h>
#include <linux/fiemap.h>
#include <linux/iomap.h>
#include <linux/writeback.h>
#include <linux/overflow.h>
#include <linux/iversion.h>
#include <linux/fadvise.h>
#include "format.h"
#include "super.h"
@@ -42,6 +44,7 @@
#include "msg.h"
#include "ext.h"
#include "util.h"
#include "iomap.h"
/*
* We want to amortize work done after dirtying the shared transaction
@@ -63,12 +66,6 @@ struct data_info {
#define DECLARE_DATA_INFO(sb, name) \
struct data_info *name = SCOUTFS_SB(sb)->data_info
struct data_ext_args {
u64 ino;
struct inode *inode;
struct scoutfs_lock *lock;
};
static void item_from_extent(struct scoutfs_key *key,
struct scoutfs_data_extent_val *dv, u64 ino,
u64 start, u64 len, u64 map, u8 flags)
@@ -181,7 +178,7 @@ static int data_ext_remove(struct super_block *sb, void *arg, u64 start,
return ret;
}
static struct scoutfs_ext_ops data_ext_ops = {
struct scoutfs_ext_ops data_ext_ops = {
.next = data_ext_next,
.insert = data_ext_insert,
.remove = data_ext_remove,
@@ -323,10 +320,8 @@ int scoutfs_data_truncate_items(struct super_block *sb, struct inode *inode,
if (WARN_ON_ONCE(last < iblock))
return -EINVAL;
if (inode) {
if (inode)
si = SCOUTFS_I(inode);
down_write(&si->extent_sem);
}
while (iblock <= last) {
if (inode)
@@ -336,10 +331,12 @@ int scoutfs_data_truncate_items(struct super_block *sb, struct inode *inode,
if (ret)
break;
if (inode)
if (inode) {
down_write(&si->extent_sem);
ret = scoutfs_dirty_inode_item(inode, lock);
else
} else {
ret = 0;
}
if (ret == 0)
ret = truncate_extents(sb, inode, ino, iblock, last,
@@ -348,8 +345,10 @@ int scoutfs_data_truncate_items(struct super_block *sb, struct inode *inode,
if (inode)
scoutfs_update_inode_item(inode, lock, &ind_locks);
scoutfs_release_trans(sb);
if (inode)
if (inode) {
up_write(&si->extent_sem);
scoutfs_inode_index_unlock(sb, &ind_locks);
}
if (ret <= 0)
break;
@@ -358,9 +357,6 @@ int scoutfs_data_truncate_items(struct super_block *sb, struct inode *inode,
ret = 0;
}
if (si)
up_write(&si->extent_sem);
return ret;
}
@@ -386,9 +382,9 @@ static inline u64 ext_last(struct scoutfs_extent *ext)
* reasonable when a file population is known to be large and dense but
* known to be written with non-streaming write patterns.
*/
static int alloc_block(struct super_block *sb, struct inode *inode,
struct scoutfs_extent *ext, u64 iblock,
struct scoutfs_lock *lock)
int scoutfs_data_alloc_block(struct super_block *sb, struct inode *inode,
struct scoutfs_extent *ext, u64 iblock,
struct scoutfs_lock *lock)
{
DECLARE_DATA_INFO(sb, datinf);
struct scoutfs_mount_options opts;
@@ -611,6 +607,7 @@ static int scoutfs_get_block(struct inode *inode, sector_t iblock,
un.len = 1;
un.map = ext.map + (iblock - ext.start);
un.flags = ext.flags & ~(SEF_OFFLINE|SEF_UNWRITTEN);
ret = scoutfs_ext_set(sb, &data_ext_ops, &args,
un.start, un.len, un.map, un.flags);
if (ret == 0) {
@@ -622,7 +619,7 @@ static int scoutfs_get_block(struct inode *inode, sector_t iblock,
/* allocate and map blocks containing our logical block */
if (create && !ext.map) {
ret = alloc_block(sb, inode, &ext, iblock, lock);
ret = scoutfs_data_alloc_block(sb, inode, &ext, iblock, lock);
if (ret == 0)
set_buffer_new(bh);
} else {
@@ -653,11 +650,18 @@ static int scoutfs_get_block_read(struct inode *inode, sector_t iblock,
struct buffer_head *bh, int create)
{
struct scoutfs_inode_info *si = SCOUTFS_I(inode);
bool caller_has_sem;
int ret;
down_read(&si->extent_sem);
caller_has_sem = scoutfs_per_task_get(&si->pt_extent_sem) != NULL;
if (!caller_has_sem)
down_read(&si->extent_sem);
ret = scoutfs_get_block(inode, iblock, bh, create);
up_read(&si->extent_sem);
if (!caller_has_sem)
up_read(&si->extent_sem);
return ret;
}
@@ -706,44 +710,55 @@ static int scoutfs_readpage(struct file *file, struct page *page)
struct scoutfs_lock *inode_lock = NULL;
SCOUTFS_DECLARE_PER_TASK_ENTRY(pt_ent);
DECLARE_DATA_WAIT(dw);
bool locked;
int flags;
int ret;
flags = SCOUTFS_LKF_REFRESH_INODE | SCOUTFS_LKF_NONBLOCK;
ret = scoutfs_lock_inode(sb, SCOUTFS_LOCK_READ, flags, inode,
&inode_lock);
if (ret < 0) {
unlock_page(page);
if (ret == -EAGAIN) {
flags &= ~SCOUTFS_LKF_NONBLOCK;
ret = scoutfs_lock_inode(sb, SCOUTFS_LOCK_READ, flags,
inode, &inode_lock);
if (ret == 0) {
scoutfs_unlock(sb, inode_lock,
SCOUTFS_LOCK_READ);
ret = AOP_TRUNCATED_PAGE;
}
}
return ret;
}
if (scoutfs_per_task_add_excl(&si->pt_data_lock, &pt_ent, inode_lock)) {
ret = scoutfs_data_wait_check(inode, page_offset(page),
PAGE_SIZE, SEF_OFFLINE,
SCOUTFS_IOC_DWO_READ, &dw,
inode_lock);
if (ret != 0) {
inode_lock = scoutfs_per_task_get(&si->pt_data_lock);
if (!inode_lock) {
flags = SCOUTFS_LKF_REFRESH_INODE | SCOUTFS_LKF_NONBLOCK;
ret = scoutfs_lock_inode(sb, SCOUTFS_LOCK_READ, flags, inode,
&inode_lock);
if (ret < 0) {
unlock_page(page);
scoutfs_per_task_del(&si->pt_data_lock, &pt_ent);
scoutfs_unlock(sb, inode_lock, SCOUTFS_LOCK_READ);
}
if (ret > 0) {
ret = scoutfs_data_wait(inode, &dw);
if (ret == 0)
ret = AOP_TRUNCATED_PAGE;
}
if (ret != 0)
if (ret == -EAGAIN) {
flags &= ~SCOUTFS_LKF_NONBLOCK;
ret = scoutfs_lock_inode(sb, SCOUTFS_LOCK_READ, flags,
inode, &inode_lock);
if (ret == 0) {
scoutfs_unlock(sb, inode_lock,
SCOUTFS_LOCK_READ);
ret = AOP_TRUNCATED_PAGE;
}
}
return ret;
}
locked = true;
if (scoutfs_per_task_add_excl(&si->pt_data_lock, &pt_ent, inode_lock)) {
ret = scoutfs_data_wait_check(inode, page_offset(page),
PAGE_SIZE, SEF_OFFLINE,
SCOUTFS_IOC_DWO_READ, &dw,
inode_lock);
if (ret != 0) {
unlock_page(page);
scoutfs_per_task_del(&si->pt_data_lock, &pt_ent);
scoutfs_unlock(sb, inode_lock, SCOUTFS_LOCK_READ);
locked = false;
}
if (ret > 0) {
ret = scoutfs_data_wait(inode, &dw);
if (ret == 0)
ret = AOP_TRUNCATED_PAGE;
}
if (ret != 0)
return ret;
} else {
WARN_ON_ONCE(true);
}
} else {
locked = false;
}
#ifdef KC_MPAGE_READ_FOLIO
@@ -752,8 +767,10 @@ static int scoutfs_readpage(struct file *file, struct page *page)
ret = mpage_readpage(page, scoutfs_get_block_read);
#endif
scoutfs_unlock(sb, inode_lock, SCOUTFS_LOCK_READ);
scoutfs_per_task_del(&si->pt_data_lock, &pt_ent);
if (locked) {
scoutfs_unlock(sb, inode_lock, SCOUTFS_LOCK_READ);
scoutfs_per_task_del(&si->pt_data_lock, &pt_ent);
}
return ret;
}
@@ -761,14 +778,22 @@ static int scoutfs_readpage(struct file *file, struct page *page)
static void scoutfs_readahead(struct readahead_control *rac)
{
struct inode *inode = rac->file->f_inode;
struct scoutfs_inode_info *si = SCOUTFS_I(inode);
struct super_block *sb = inode->i_sb;
struct scoutfs_lock *inode_lock = NULL;
bool found_lock;
int ret;
ret = scoutfs_lock_inode(sb, SCOUTFS_LOCK_READ,
SCOUTFS_LKF_REFRESH_INODE, inode, &inode_lock);
if (ret)
return;
inode_lock = scoutfs_per_task_get(&si->pt_data_lock);
if (!inode_lock) {
ret = scoutfs_lock_inode(sb, SCOUTFS_LOCK_READ,
SCOUTFS_LKF_REFRESH_INODE, inode, &inode_lock);
if (ret)
return;
found_lock = false;
} else {
found_lock = true;
}
ret = scoutfs_data_wait_check(inode, readahead_pos(rac),
readahead_length(rac), SEF_OFFLINE,
@@ -777,7 +802,8 @@ static void scoutfs_readahead(struct readahead_control *rac)
if (ret == 0)
mpage_readahead(rac, scoutfs_get_block_read);
scoutfs_unlock(sb, inode_lock, SCOUTFS_LOCK_READ);
if (!found_lock)
scoutfs_unlock(sb, inode_lock, SCOUTFS_LOCK_READ);
}
static int scoutfs_writepage(struct page *page, struct writeback_control *wbc)
@@ -866,8 +892,8 @@ out:
}
/* kinda like __filemap_fdatawrite_range! :P */
static int writepages_sync_none(struct address_space *mapping, loff_t start,
loff_t end)
int scoutfs_writepages_sync_none(struct address_space *mapping,
loff_t start, loff_t end)
{
struct writeback_control wbc = {
.sync_mode = WB_SYNC_NONE,
@@ -879,12 +905,26 @@ static int writepages_sync_none(struct address_space *mapping, loff_t start,
return mapping->a_ops->writepages(mapping, &wbc);
}
void scoutfs_do_write_end(struct inode *inode, struct scoutfs_lock *data_lock,
struct list_head *ind_locks)
{
struct scoutfs_inode_info *si = SCOUTFS_I(inode);
if (!si->staging) {
scoutfs_inode_set_data_seq(inode);
scoutfs_inode_inc_data_version(inode);
}
inode_inc_iversion(inode);
scoutfs_update_inode_item(inode, data_lock, ind_locks);
scoutfs_inode_queue_writeback(inode);
}
static int scoutfs_write_end(struct file *file, struct address_space *mapping,
loff_t pos, unsigned len, unsigned copied,
struct page *page, void *fsdata)
{
struct inode *inode = mapping->host;
struct scoutfs_inode_info *si = SCOUTFS_I(inode);
struct super_block *sb = inode->i_sb;
struct write_begin_data *wbd = fsdata;
int ret;
@@ -893,16 +933,9 @@ static int scoutfs_write_end(struct file *file, struct address_space *mapping,
len, copied);
ret = generic_write_end(file, mapping, pos, len, copied, page, fsdata);
if (ret > 0) {
if (!si->staging) {
scoutfs_inode_set_data_seq(inode);
scoutfs_inode_inc_data_version(inode);
}
if (ret > 0)
scoutfs_do_write_end(inode, wbd->lock, &wbd->ind_locks);
inode_inc_iversion(inode);
scoutfs_update_inode_item(inode, wbd->lock, &wbd->ind_locks);
scoutfs_inode_queue_writeback(inode);
}
scoutfs_release_trans(sb);
scoutfs_inode_index_unlock(sb, &wbd->ind_locks);
kfree(wbd);
@@ -914,18 +947,16 @@ static int scoutfs_write_end(struct file *file, struct address_space *mapping,
* to very long commit latencies with lots of dirty file data.
*
* This hack tries to minimize these writeback latencies while
* keeping concurrent large file strreaming writes from
* keeping concurrent large file streaming writes from
* suffering too terribly. Every N bytes we kick off background
* writbeack on the previous N bytes. By the time transaction
* writeback on the previous N bytes. By the time transaction
* commit comes along it will find that dirty file blocks have
* already been written.
*/
#define BACKGROUND_WRITEBACK_BYTES (16 * 1024 * 1024)
#define BACKGROUND_WRITEBACK_MASK (BACKGROUND_WRITEBACK_BYTES - 1)
if (ret > 0 && ((pos + ret) & BACKGROUND_WRITEBACK_MASK) == 0)
writepages_sync_none(mapping,
pos + ret - BACKGROUND_WRITEBACK_BYTES,
pos + ret - 1);
scoutfs_writepages_sync_none(mapping,
pos + ret - BACKGROUND_WRITEBACK_BYTES,
pos + ret - 1);
return ret;
}
@@ -1079,27 +1110,27 @@ long scoutfs_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
inode_dio_wait(inode);
down_write(&si->extent_sem);
if (!(mode & FALLOC_FL_KEEP_SIZE) &&
(offset + len > i_size_read(inode))) {
ret = inode_newsize_ok(inode, offset + len);
if (ret)
goto out_extent;
goto out_mutex;
}
iblock = offset >> SCOUTFS_BLOCK_SM_SHIFT;
last = (offset + len - 1) >> SCOUTFS_BLOCK_SM_SHIFT;
while(iblock <= last) {
while (iblock <= last) {
ret = scoutfs_quota_check_data(sb, inode);
if (ret)
goto out_extent;
goto out_mutex;
ret = scoutfs_inode_index_lock_hold(inode, &ind_locks, false, true);
if (ret)
goto out_extent;
goto out_mutex;
down_write(&si->extent_sem);
ret = fallocate_extents(sb, inode, iblock, last, lock);
@@ -1116,6 +1147,7 @@ long scoutfs_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
if (ret >= 0)
scoutfs_update_inode_item(inode, lock, &ind_locks);
scoutfs_release_trans(sb);
up_write(&si->extent_sem);
scoutfs_inode_index_unlock(sb, &ind_locks);
/* txn couldn't meet the request. Let's try with a new txn */
@@ -1125,14 +1157,12 @@ long scoutfs_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
}
if (ret <= 0)
goto out_extent;
goto out_mutex;
iblock += ret;
ret = 0;
}
out_extent:
up_write(&si->extent_sem);
out_mutex:
scoutfs_unlock(sb, lock, SCOUTFS_LOCK_WRITE);
inode_unlock(inode);
@@ -1142,6 +1172,39 @@ out:
return ret;
}
#ifdef KC_USE_IOMAP_FOR_IO
static int scoutfs_fadvise(struct file *file, loff_t start, loff_t end, int advice)
{
struct inode *inode = file_inode(file);
struct scoutfs_inode_info *si = SCOUTFS_I(inode);
SCOUTFS_DECLARE_PER_TASK_ENTRY(pt_extent_ent);
bool locked = false;
int ret;
/*
* We need to get the extent_sem now, or we'll be out of order with the
* mapping.invalidate_lock.
*/
if (advice == POSIX_FADV_WILLNEED) {
down_read(&si->extent_sem);
locked = true;
if (!scoutfs_per_task_add_excl(&si->pt_extent_sem, &pt_extent_ent,
&pt_extent_ent))
WARN_ON_ONCE(true);
}
ret = generic_fadvise(file, start, end, advice);
if (locked) {
scoutfs_per_task_del(&si->pt_extent_sem, &pt_extent_ent);
up_read(&si->extent_sem);
}
return ret;
}
#endif
/*
* A special case of initializing a single large offline extent. This
* chooses not to deal with any existing extents. It can only be used
@@ -1439,7 +1502,6 @@ int scoutfs_data_move_blocks(struct inode *from, u64 from_off,
from_start += len;
}
up_write(&from_si->extent_sem);
up_write(&to_si->extent_sem);
@@ -1572,163 +1634,47 @@ out:
return ret;
}
/*
* This copies to userspace :/
*/
static int fill_extent(struct fiemap_extent_info *fieinfo,
struct scoutfs_extent *ext, u32 fiemap_flags)
{
u32 flags;
if (ext->len == 0)
return 0;
flags = fiemap_flags;
if (ext->flags & SEF_OFFLINE)
flags |= FIEMAP_EXTENT_UNKNOWN;
else if (ext->flags & SEF_UNWRITTEN)
flags |= FIEMAP_EXTENT_UNWRITTEN;
return fiemap_fill_next_extent(fieinfo,
ext->start << SCOUTFS_BLOCK_SM_SHIFT,
ext->map << SCOUTFS_BLOCK_SM_SHIFT,
ext->len << SCOUTFS_BLOCK_SM_SHIFT,
flags);
}
/*
* Return all the file's extents whose blocks overlap with the caller's
* byte region. We set _LAST on the last extent and _UNKNOWN on offline
* extents.
*/
int scoutfs_data_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
u64 start, u64 len)
{
struct scoutfs_inode_info *si = SCOUTFS_I(inode);
struct super_block *sb = inode->i_sb;
const u64 ino = scoutfs_ino(inode);
struct scoutfs_lock *lock = NULL;
struct scoutfs_extent *info = NULL;
struct page *page = NULL;
struct scoutfs_extent ext;
struct scoutfs_extent cur;
struct data_ext_args args;
u32 last_flags;
u64 iblock;
u64 last;
int entries = 0;
SCOUTFS_DECLARE_PER_TASK_ENTRY(pt_ent);
int ret;
int complete = 0;
if (len == 0) {
ret = 0;
ret = -EINVAL;
goto out;
}
ret = fiemap_prep(inode, fieinfo, start, &len, FIEMAP_FLAG_SYNC);
/*
* We don't support xattrs stored in extents. The magic incantation
* to communicate this to the caller is to reset fi_flags to
* FIEMAP_FLAG_XATTR and return -EBADR.
*/
if (fieinfo->fi_flags & FIEMAP_FLAG_XATTR) {
fieinfo->fi_flags = FIEMAP_FLAG_XATTR;
ret = -EBADR;
goto out;
}
inode_lock(inode);
ret = scoutfs_lock_inode(sb, SCOUTFS_LOCK_READ, 0, inode, &lock);
if (ret)
goto out;
page = alloc_page(GFP_KERNEL);
if (!page) {
ret = -ENOMEM;
goto out;
}
scoutfs_per_task_add_excl(&si->pt_data_lock, &pt_ent, lock);
/* use a dummy extent to track */
memset(&cur, 0, sizeof(cur));
last_flags = 0;
ret = iomap_fiemap(inode, fieinfo, start, len, &scoutfs_iomap_report_ops);
iblock = start >> SCOUTFS_BLOCK_SM_SHIFT;
last = (start + len - 1) >> SCOUTFS_BLOCK_SM_SHIFT;
scoutfs_per_task_del(&si->pt_data_lock, &pt_ent);
scoutfs_unlock(sb, lock, SCOUTFS_LOCK_READ);
args.ino = ino;
args.inode = inode;
/* outer loop */
while (iblock <= last) {
/* lock */
inode_lock(inode);
down_read(&si->extent_sem);
ret = scoutfs_lock_inode(sb, SCOUTFS_LOCK_READ, 0, inode, &lock);
if (ret) {
up_read(&si->extent_sem);
inode_unlock(inode);
break;
}
args.lock = lock;
/* collect entries */
info = page_address(page);
memset(info, 0, PAGE_SIZE);
while (entries < (PAGE_SIZE / sizeof(struct fiemap_extent)) - 1) {
ret = scoutfs_ext_next(sb, &data_ext_ops, &args,
iblock, 1, &ext);
if (ret < 0) {
if (ret == -ENOENT)
ret = 0;
complete = 1;
last_flags = FIEMAP_EXTENT_LAST;
break;
}
trace_scoutfs_data_fiemap_extent(sb, ino, &ext);
if (ext.start > last) {
/* not setting _LAST, it's for end of file */
ret = 0;
complete = 1;
break;
}
if (scoutfs_ext_can_merge(&cur, &ext)) {
/* merged extents could be greater than input len */
cur.len += ext.len;
} else {
/* fill it */
memcpy(info, &cur, sizeof(cur));
entries++;
info++;
cur = ext;
}
iblock = ext.start + ext.len;
}
/* unlock */
scoutfs_unlock(sb, lock, SCOUTFS_LOCK_READ);
up_read(&si->extent_sem);
inode_unlock(inode);
if (ret)
break;
/* emit entries */
info = page_address(page);
for (; entries > 0; entries--) {
ret = fill_extent(fieinfo, info, 0);
if (ret != 0)
goto out;
info++;
}
if (complete)
break;
}
/* still one left, it's in cur */
if (cur.len)
ret = fill_extent(fieinfo, &cur, last_flags);
inode_unlock(inode);
out:
if (ret == 1)
ret = 0;
if (page)
__free_page(page);
trace_scoutfs_data_fiemap(sb, start, len, ret);
return ret;
@@ -1827,6 +1773,7 @@ int scoutfs_data_wait_check(struct inode *inode, loff_t pos, loff_t len,
DECLARE_DATA_WAIT_ROOT(sb, rt);
DECLARE_DATA_WAITQ(inode, wq);
struct scoutfs_extent ext = {0,};
bool caller_has_sem;
u64 iblock;
u64 last_block;
u64 on;
@@ -1845,7 +1792,7 @@ int scoutfs_data_wait_check(struct inode *inode, loff_t pos, loff_t len,
goto out;
}
if ((sef & SEF_OFFLINE)) {
if (sef & SEF_OFFLINE) {
scoutfs_inode_get_onoff(inode, &on, &off);
if (off == 0) {
ret = 0;
@@ -1853,12 +1800,14 @@ int scoutfs_data_wait_check(struct inode *inode, loff_t pos, loff_t len,
}
}
down_read(&si->extent_sem);
caller_has_sem = scoutfs_per_task_get(&si->pt_extent_sem) != NULL;
if (!caller_has_sem)
down_read(&si->extent_sem);
iblock = pos >> SCOUTFS_BLOCK_SM_SHIFT;
last_block = (pos + len - 1) >> SCOUTFS_BLOCK_SM_SHIFT;
while(iblock <= last_block) {
while (iblock <= last_block) {
ret = scoutfs_ext_next(sb, &data_ext_ops, &args,
iblock, 1, &ext);
if (ret < 0) {
@@ -1891,7 +1840,8 @@ int scoutfs_data_wait_check(struct inode *inode, loff_t pos, loff_t len,
iblock = ext.start + ext.len;
}
up_read(&si->extent_sem);
if (!caller_has_sem)
up_read(&si->extent_sem);
out:
trace_scoutfs_data_wait_check(sb, ino, pos, len, sef, op, &ext, ret);
@@ -2168,40 +2118,77 @@ static vm_fault_t scoutfs_data_filemap_fault(struct vm_fault *vmf)
struct super_block *sb = inode->i_sb;
struct scoutfs_lock *inode_lock = NULL;
SCOUTFS_DECLARE_PER_TASK_ENTRY(pt_ent);
SCOUTFS_DECLARE_PER_TASK_ENTRY(pt_sem);
DECLARE_DATA_WAIT(dw);
#ifdef KC_USE_IOMAP_FOR_IO
bool caller_has_sem;
#endif
loff_t pos;
int err;
vm_fault_t ret = VM_FAULT_SIGBUS;
bool found_lock;
pos = vmf->pgoff;
pos <<= PAGE_SHIFT;
retry:
err = scoutfs_lock_inode(sb, SCOUTFS_LOCK_READ,
SCOUTFS_LKF_REFRESH_INODE, inode, &inode_lock);
if (err < 0)
return vmf_error(err);
inode_lock = scoutfs_per_task_get(&si->pt_data_lock);
if (!inode_lock) {
found_lock = false;
if (scoutfs_per_task_add_excl(&si->pt_data_lock, &pt_ent, inode_lock)) {
/* protect checked extents from stage/release */
atomic_inc(&inode->i_dio_count);
err = scoutfs_lock_inode(sb, SCOUTFS_LOCK_READ,
SCOUTFS_LKF_REFRESH_INODE, inode, &inode_lock);
if (err < 0)
return vmf_error(err);
err = scoutfs_data_wait_check(inode, pos, PAGE_SIZE,
SEF_OFFLINE, SCOUTFS_IOC_DWO_READ,
&dw, inode_lock);
if (err != 0) {
if (err < 0)
ret = vmf_error(err);
goto out;
if (scoutfs_per_task_add_excl(&si->pt_data_lock, &pt_ent, inode_lock)) {
/* protect checked extents from stage/release */
inode_dio_begin(inode);
err = scoutfs_data_wait_check(inode, pos, PAGE_SIZE,
SEF_OFFLINE, SCOUTFS_IOC_DWO_READ,
&dw, inode_lock);
if (err != 0) {
if (err < 0)
ret = vmf_error(err);
goto out;
}
} else {
WARN_ON_ONCE(true);
}
} else {
found_lock = true;
}
#ifdef KC_USE_IOMAP_FOR_IO
caller_has_sem = scoutfs_per_task_get(&si->pt_extent_sem) != NULL;
if (!caller_has_sem) {
down_read(&si->extent_sem);
if (!scoutfs_per_task_add_excl(&si->pt_extent_sem, &pt_sem,
&caller_has_sem))
WARN_ON_ONCE(true);
}
#endif
ret = filemap_fault(vmf);
#ifdef KC_USE_IOMAP_FOR_IO
if (!caller_has_sem) {
up_read(&si->extent_sem);
scoutfs_per_task_del(&si->pt_extent_sem, &pt_sem);
}
#endif
out:
if (scoutfs_per_task_del(&si->pt_data_lock, &pt_ent))
inode_dio_end(inode);
scoutfs_unlock(sb, inode_lock, SCOUTFS_LOCK_READ);
if (!found_lock) {
if (scoutfs_per_task_del(&si->pt_data_lock, &pt_ent))
inode_dio_end(inode);
else
WARN_ON_ONCE(true);
scoutfs_unlock(sb, inode_lock, SCOUTFS_LOCK_READ);
}
if (scoutfs_data_wait_found(&dw)) {
err = scoutfs_data_wait(inode, &dw);
if (err == 0)
@@ -2229,6 +2216,7 @@ static int scoutfs_file_mmap(struct file *file, struct vm_area_struct *vma)
const struct address_space_operations scoutfs_file_aops = {
#ifdef KC_MPAGE_READ_FOLIO
.direct_IO = noop_direct_IO,
.dirty_folio = block_dirty_folio,
.invalidate_folio = block_invalidate_folio,
.read_folio = scoutfs_read_folio,
@@ -2252,6 +2240,9 @@ const struct file_operations scoutfs_file_fops = {
.fsync = scoutfs_file_fsync,
.llseek = scoutfs_file_llseek,
.fallocate = scoutfs_fallocate,
#ifdef KC_USE_IOMAP_FOR_IO
.fadvise = scoutfs_fadvise,
#endif
};
void scoutfs_data_init_btrees(struct super_block *sb,
+20
View File
@@ -38,10 +38,30 @@ struct scoutfs_data_wait {
.err = 0, \
}
struct data_ext_args {
u64 ino;
struct inode *inode;
struct scoutfs_lock *lock;
};
extern const struct address_space_operations scoutfs_file_aops;
extern const struct file_operations scoutfs_file_fops;
extern struct scoutfs_ext_ops data_ext_ops;
struct scoutfs_alloc;
struct scoutfs_block_writer;
struct scoutfs_extent;
#define BACKGROUND_WRITEBACK_BYTES (16 * 1024 * 1024)
#define BACKGROUND_WRITEBACK_MASK (BACKGROUND_WRITEBACK_BYTES - 1)
int scoutfs_writepages_sync_none(struct address_space *mapping, loff_t start, loff_t end);
void scoutfs_do_write_end(struct inode *inode, struct scoutfs_lock *data_lock,
struct list_head *ind_locks);
int scoutfs_data_alloc_block(struct super_block *sb, struct inode *inode,
struct scoutfs_extent *ext, u64 iblock,
struct scoutfs_lock *lock);
int scoutfs_get_block_write(struct inode *inode, sector_t iblock, struct buffer_head *bh,
int create);
+604 -38
View File
@@ -18,6 +18,7 @@
#include <linux/mpage.h>
#include <linux/sched.h>
#include <linux/aio.h>
#include <linux/iomap.h>
#include "format.h"
#include "super.h"
@@ -29,6 +30,544 @@
#include "per_task.h"
#include "omap.h"
#include "quota.h"
#include "iomap.h"
#include "trans.h"
#include "msg.h"
#ifdef KC_USE_IOMAP_FOR_IO
static bool scoutfs_should_use_dio(struct kiocb *iocb, struct iov_iter *iter)
{
/* Current offset must be aligned */
if (iocb->ki_pos & SCOUTFS_BLOCK_SM_MASK)
return false;
if (iov_iter_alignment(iter) & SCOUTFS_BLOCK_SM_MASK)
return false;
return true;
}
/* copied from fs/gfs2/file.c */
static inline bool should_fault_in_pages(struct iov_iter *i,
struct kiocb *iocb,
size_t *prev_count,
size_t *window_size)
{
size_t count = iov_iter_count(i);
size_t size, offs;
if (!count)
return false;
if (!user_backed_iter(i))
return false;
size = PAGE_SIZE;
offs = offset_in_page(iocb->ki_pos);
if (*prev_count != count || !*window_size) {
size_t nr_dirtied;
nr_dirtied = max(current->nr_dirtied_pause -
current->nr_dirtied, 8);
size = min_t(size_t, SZ_1M, nr_dirtied << PAGE_SHIFT);
}
*prev_count = count;
*window_size = size - offs;
return true;
}
static ssize_t scoutfs_file_buffered_read(struct kiocb *iocb, struct iov_iter *to)
{
struct inode *inode = file_inode(iocb->ki_filp);
struct scoutfs_inode_info *si = SCOUTFS_I(inode);
SCOUTFS_DECLARE_PER_TASK_ENTRY(pt_extent_ent);
size_t prev_count = 0;
size_t window_size = 0;
size_t read = 0;
bool locked = false;
ssize_t ret;
pagefault_disable();
iocb->ki_flags |= IOCB_NOIO;
ret = generic_file_read_iter(iocb, to);
iocb->ki_flags &= ~IOCB_NOIO;
pagefault_enable();
if (ret >= 0) {
if (iov_iter_count(to) == 0)
return ret;
read = ret;
} else if (ret != -EFAULT) {
if (ret != -EAGAIN)
return ret;
}
retry:
down_read(&si->extent_sem);
if (!scoutfs_per_task_add_excl(&si->pt_extent_sem, &pt_extent_ent,
&pt_extent_ent))
WARN_ON_ONCE(true);
locked = true;
pagefault_disable();
ret = generic_file_read_iter(iocb, to);
pagefault_enable();
if (ret <= 0 && ret != -EFAULT)
goto out;
if (ret > 0)
read += ret;
if (should_fault_in_pages(to, iocb, &prev_count, &window_size)) {
scoutfs_per_task_del(&si->pt_extent_sem, &pt_extent_ent);
up_read(&si->extent_sem);
locked = false;
window_size -= fault_in_iov_iter_writeable(to, window_size);
if (window_size != 0)
goto retry;
}
out:
if (locked) {
scoutfs_per_task_del(&si->pt_extent_sem, &pt_extent_ent);
up_read(&si->extent_sem);
}
return read ? read : ret;
}
static ssize_t scoutfs_file_direct_read(struct kiocb *iocb, struct iov_iter *to,
bool nowait)
{
struct file *file = iocb->ki_filp;
size_t prev_count = 0;
size_t window_size = 0;
size_t read = 0;
ssize_t ret;
if (!scoutfs_should_use_dio(iocb, to)) {
iocb->ki_flags &= ~IOCB_DIRECT;
return scoutfs_file_buffered_read(iocb, to);
}
retry:
pagefault_disable();
to->nofault = true;
ret = iomap_dio_rw(iocb, to, &scoutfs_iomap_ops, NULL, IOMAP_DIO_PARTIAL, read);
to->nofault = false;
pagefault_enable();
if (ret <= 0 && ret != -EFAULT)
goto out;
if (ret > 0)
read = ret;
if (should_fault_in_pages(to, iocb, &prev_count, &window_size)) {
window_size -= fault_in_iov_iter_writeable(to, window_size);
if (window_size != 0)
goto retry;
}
out:
file_accessed(file);
if (ret < 0)
return ret;
return read;
}
ssize_t scoutfs_file_read_iter(struct kiocb *iocb, struct iov_iter *to)
{
struct file *file = iocb->ki_filp;
struct inode *inode = file_inode(file);
struct scoutfs_inode_info *si = SCOUTFS_I(inode);
struct super_block *sb = inode->i_sb;
struct scoutfs_lock *scoutfs_inode_lock;
SCOUTFS_DECLARE_PER_TASK_ENTRY(pt_data_ent);
DECLARE_DATA_WAIT(dw);
int lock_flags = SCOUTFS_LKF_REFRESH_INODE;
bool is_dio = (iocb->ki_flags & IOCB_DIRECT);
bool nowait = (iocb->ki_flags & IOCB_NOWAIT);
bool inode_locked;
ssize_t ret;
/* IOCB_NOWAIT is only for direct I/O */
if (!is_dio && nowait)
return -EOPNOTSUPP;
retry:
scoutfs_inode_lock = NULL;
inode_locked = false;
if (is_dio) {
if (nowait) {
if (!inode_trylock_shared(inode)) {
ret = -EAGAIN;
goto out;
}
lock_flags |= SCOUTFS_LKF_NONBLOCK;
} else {
inode_lock_shared(inode);
}
inode_dio_begin(inode);
inode_locked = true;
} else {
/* protect checked extents from release */
inode_lock(inode);
inode_dio_begin(inode);
inode_unlock(inode);
}
ret = scoutfs_lock_inode(sb, SCOUTFS_LOCK_READ,
lock_flags, inode, &scoutfs_inode_lock);
if (ret)
goto out;
if (scoutfs_per_task_add_excl(&si->pt_data_lock, &pt_data_ent,
scoutfs_inode_lock)) {
ret = scoutfs_data_wait_check(inode, iocb->ki_pos, iov_iter_count(to),
SEF_OFFLINE, SCOUTFS_IOC_DWO_READ, &dw,
scoutfs_inode_lock);
if (ret != 0)
goto out;
} else {
WARN_ON_ONCE(true);
}
if (is_dio)
ret = scoutfs_file_direct_read(iocb, to, nowait);
else
ret = scoutfs_file_buffered_read(iocb, to);
out:
inode_dio_end(inode);
if (scoutfs_inode_lock) {
scoutfs_per_task_del(&si->pt_data_lock, &pt_data_ent);
scoutfs_unlock(sb, scoutfs_inode_lock, SCOUTFS_LOCK_READ);
}
if (inode_locked)
inode_unlock_shared(inode);
if (scoutfs_data_wait_found(&dw)) {
ret = scoutfs_data_wait(inode, &dw);
if (ret == 0)
goto retry;
}
return ret;
}
static int lock_for_iomap_write(struct inode *inode, struct list_head *ind_locks,
struct scoutfs_lock *scoutfs_inode_lock,
bool nowait)
{
struct super_block *sb = inode->i_sb;
u64 seq;
int ret;
do {
ret = scoutfs_inode_index_start(sb, &seq) ?:
scoutfs_inode_index_prepare(sb, ind_locks, inode, true) ?:
scoutfs_inode_index_try_lock_hold(sb, ind_locks, seq, true);
if (ret < 0)
return ret;
/* A return value > 0 means the seq number changed */
if (ret > 0) {
if (nowait)
return -EAGAIN;
continue;
}
ret = scoutfs_dirty_inode_item(inode, scoutfs_inode_lock);
break;
} while (true);
return ret;
}
static void unlock_for_iomap_write(struct inode *inode,
struct scoutfs_lock *scoutfs_inode_lock,
struct list_head *ind_locks,
size_t written)
{
struct super_block *sb = inode->i_sb;
if (written > 0)
scoutfs_do_write_end(inode, scoutfs_inode_lock, ind_locks);
scoutfs_release_trans(sb);
scoutfs_inode_index_unlock(sb, ind_locks);
}
static ssize_t scoutfs_file_direct_write(struct kiocb *iocb, struct iov_iter *from,
struct scoutfs_lock *scoutfs_inode_lock)
{
struct file *file = iocb->ki_filp;
struct inode *inode = file_inode(file);
bool nowait = (iocb->ki_flags & IOCB_NOWAIT);
LIST_HEAD(ind_locks);
size_t prev_count = 0;
size_t window_size = 0;
size_t written = 0;
ssize_t ret = 0;
bool locked = false;
if (!scoutfs_should_use_dio(iocb, from)) {
iocb->ki_flags &= ~IOCB_DIRECT;
goto out;
}
retry:
ret = lock_for_iomap_write(inode, &ind_locks, scoutfs_inode_lock, nowait);
if (ret < 0)
goto out;
locked = true;
/*
* Due to lock ordering issues, we need to disable page faults while we're
* doing the iomap iterations. Pass IOMAP_DIO_PARTIAL so that the iomap
* code knows it's OK to return a partial result. We will try to fault in
* any needed pages later on.
*/
from->nofault = true;
ret = iomap_dio_rw(iocb, from, &scoutfs_iomap_ops, NULL,
IOMAP_DIO_PARTIAL | IOMAP_DIO_FORCE_WAIT,
written);
from->nofault = false;
if (ret <= 0) {
if (ret == -ENOTBLK)
ret = 0;
if (ret != -EFAULT)
goto out;
}
/* No increment (+=) because iomap returns a cumulative value. */
if (ret > 0)
written = ret;
/*
* We might have skipped some pages that needed to be faulted in. If so, drop
* our locks to avoid deadlock and try to fault them in. Then we can relock
* and try the remaining DIO writes.
*/
if (should_fault_in_pages(from, iocb, &prev_count, &window_size)) {
unlock_for_iomap_write(inode, scoutfs_inode_lock, &ind_locks, written);
locked = false;
window_size -= fault_in_iov_iter_readable(from, window_size);
if (window_size != 0)
goto retry;
}
out:
if (locked) {
unlock_for_iomap_write(inode, scoutfs_inode_lock, &ind_locks, written);
}
return ret < 0 ? ret : written;
}
static ssize_t scoutfs_file_buffered_write(struct kiocb *iocb, struct iov_iter *from,
struct scoutfs_lock *scoutfs_inode_lock)
{
struct file *file = iocb->ki_filp;
struct inode *inode = file_inode(file);
LIST_HEAD(ind_locks);
size_t prev_count = 0;
size_t window_size = 0;
size_t orig_count = iov_iter_count(from);
size_t written = 0;
bool locked;
ssize_t ret;
retry:
locked = false;
/*
* Because of lock ordering issues with the page fault code, we need to
* try to manually fault in any pages before acquiring locks. Then we
* disable page faults while doing the iomap iterations.
*/
if (should_fault_in_pages(from, iocb, &prev_count, &window_size)) {
window_size -= fault_in_iov_iter_readable(from, window_size);
if (window_size == 0) {
ret = -EFAULT;
goto out;
}
from->count = min(from->count, window_size);
}
ret = lock_for_iomap_write(inode, &ind_locks, scoutfs_inode_lock, false);
if (ret < 0)
goto out;
locked = true;
pagefault_disable();
ret = iomap_file_buffered_write(iocb, from, &scoutfs_iomap_ops);
pagefault_enable();
/* Accumulate the count of what's been written so far */
if (ret > 0)
written += ret;
if (ret <= 0 && ret != -EFAULT)
goto out;
from->count = orig_count - written;
if (should_fault_in_pages(from, iocb, &prev_count, &window_size)) {
/*
* There are still some pages to be faulted in. Drop our locks and
* try another pass.
*/
unlock_for_iomap_write(inode, scoutfs_inode_lock, &ind_locks, written);
locked = false;
goto retry;
}
out:
if (locked) {
unlock_for_iomap_write(inode, scoutfs_inode_lock, &ind_locks, written);
}
from->count = orig_count - written;
return written ? written : ret;
}
ssize_t scoutfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
{
struct file *file = iocb->ki_filp;
struct inode *inode = file_inode(file);
struct scoutfs_inode_info *si = SCOUTFS_I(inode);
struct super_block *sb = inode->i_sb;
struct scoutfs_lock *scoutfs_inode_lock = NULL;
SCOUTFS_DECLARE_PER_TASK_ENTRY(pt_data_ent);
DECLARE_DATA_WAIT(dw);
int lock_flags = SCOUTFS_LKF_REFRESH_INODE;
bool added_pt_data;
bool is_dio = (iocb->ki_flags & IOCB_DIRECT);
bool nowait = (iocb->ki_flags & IOCB_NOWAIT);
bool is_sync = (iocb->ki_flags & IOCB_DSYNC);
ssize_t buffered;
ssize_t ret;
ssize_t ret2;
/* We don't support O_DSYNC */
iocb->ki_flags &= ~IOCB_DSYNC;
/* IOCB_NOWAIT is only for direct I/O */
if (!is_dio && nowait)
return -EOPNOTSUPP;
if (nowait)
lock_flags |= SCOUTFS_LKF_NONBLOCK;
retry:
added_pt_data = false;
if (nowait) {
if (!inode_trylock(inode)) {
return -EAGAIN;
}
} else {
inode_lock(inode);
}
ret = scoutfs_lock_inode(sb, SCOUTFS_LOCK_WRITE, lock_flags,
inode, &scoutfs_inode_lock);
if (ret < 0)
goto out;
ret = generic_write_checks(iocb, from);
if (ret <= 0)
goto out;
ret = scoutfs_inode_check_retention(inode);
if (ret < 0)
goto out;
ret = scoutfs_complete_truncate(inode, scoutfs_inode_lock);
if (ret)
goto out;
ret = scoutfs_quota_check_data(sb, inode);
if (ret)
goto out;
if (scoutfs_per_task_add_excl(&si->pt_data_lock, &pt_data_ent,
scoutfs_inode_lock)) {
/* data_version is per inode, whole file must be online */
added_pt_data = true;
ret = scoutfs_data_wait_check(inode, 0, i_size_read(inode), SEF_OFFLINE,
SCOUTFS_IOC_DWO_WRITE, &dw,
scoutfs_inode_lock);
if (ret != 0)
goto out;
}
/* XXX: remove SUID bit */
if (is_dio) {
ret = scoutfs_file_direct_write(iocb, from, scoutfs_inode_lock);
if (ret < 0 || !iov_iter_count(from))
goto out;
buffered = scoutfs_file_buffered_write(iocb, from, scoutfs_inode_lock);
if (buffered <= 0) {
if (ret == 0)
ret = buffered;
goto out;
}
/*
* Ensure all data is persisted. We want the next direct IO read to be
* able to read what was just written. If this fails, just return the
* byte count written by direct I/O, since we don't know if the
* buffered pages made it to disk.
*/
ret2 = generic_write_sync(iocb, buffered);
invalidate_mapping_pages(file->f_mapping,
(iocb->ki_pos - buffered) >> PAGE_SHIFT,
(iocb->ki_pos - 1) >> PAGE_SHIFT);
if (ret == 0 || ret2 >= 0)
ret += ret2;
} else {
ret = scoutfs_file_buffered_write(iocb, from, scoutfs_inode_lock);
if (ret > 0)
ret = generic_write_sync(iocb, ret);
}
out:
if (added_pt_data) {
scoutfs_per_task_del(&si->pt_data_lock, &pt_data_ent);
added_pt_data = false;
}
scoutfs_unlock(sb, scoutfs_inode_lock, SCOUTFS_LOCK_WRITE);
inode_unlock(inode);
if (scoutfs_data_wait_found(&dw)) {
ret = scoutfs_data_wait(inode, &dw);
if (ret == 0)
goto retry;
}
if (is_sync)
iocb->ki_flags |= IOCB_DSYNC;
return ret;
}
#else
ssize_t scoutfs_file_read_iter(struct kiocb *iocb, struct iov_iter *to)
{
@@ -139,6 +678,71 @@ out:
return ret;
}
#endif
loff_t scoutfs_file_llseek(struct file *file, loff_t offset, int whence)
{
struct inode *inode = file->f_mapping->host;
struct scoutfs_inode_info *si = SCOUTFS_I(inode);
struct super_block *sb = inode->i_sb;
struct scoutfs_lock *lock = NULL;
SCOUTFS_DECLARE_PER_TASK_ENTRY(pt_ent);
bool ilocked = false;
int ret = 0;
switch (whence) {
case SEEK_END:
case SEEK_DATA:
case SEEK_HOLE:
/*
* These require a lock and inode refresh as they reference i_size.
*/
inode_lock(inode);
ilocked = true;
ret = scoutfs_lock_inode(sb, SCOUTFS_LOCK_READ,
SCOUTFS_LKF_REFRESH_INODE, inode,
&lock);
if (ret == 0) {
if (!scoutfs_per_task_add_excl(&si->pt_data_lock, &pt_ent, lock))
WARN_ON_ONCE(true);
}
case SEEK_SET:
case SEEK_CUR:
/* No lock required */
break;
default:
ret = -EINVAL;
break;
}
if (ret == 0) {
if (whence == SEEK_DATA) {
offset = iomap_seek_data(inode, offset,
&scoutfs_iomap_report_ops);
} else if (whence == SEEK_HOLE) {
offset = iomap_seek_hole(inode, offset,
&scoutfs_iomap_report_ops);
} else {
offset = generic_file_llseek(file, offset, whence);
}
}
if (ilocked)
inode_unlock(inode);
if (lock) {
scoutfs_per_task_del(&si->pt_data_lock, &pt_ent);
scoutfs_unlock(sb, lock, SCOUTFS_LOCK_READ);
}
/* The iomap functions don't update the file pointer */
if (ret == 0 && offset >= 0)
offset = vfs_setpos(file, offset, sb->s_maxbytes);
return ret ? ret : offset;
}
int scoutfs_permission(KC_VFS_NS_DEF
struct inode *inode, int mask)
{
@@ -161,41 +765,3 @@ int scoutfs_permission(KC_VFS_NS_DEF
return ret;
}
loff_t scoutfs_file_llseek(struct file *file, loff_t offset, int whence)
{
struct inode *inode = file->f_mapping->host;
struct super_block *sb = inode->i_sb;
struct scoutfs_lock *lock = NULL;
int ret = 0;
switch (whence) {
case SEEK_END:
case SEEK_DATA:
case SEEK_HOLE:
/*
* These require a lock and inode refresh as they
* reference i_size.
*
* XXX: SEEK_DATA/SEEK_HOLE can search our extent
* items instead of relying on generic_file_llseek()
* trickery.
*/
ret = scoutfs_lock_inode(sb, SCOUTFS_LOCK_READ,
SCOUTFS_LKF_REFRESH_INODE, inode,
&lock);
case SEEK_SET:
case SEEK_CUR:
/* No lock required, fall through to the generic helper */
break;
default:
ret = -EINVAL;
}
if (ret == 0)
offset = generic_file_llseek(file, offset, whence);
scoutfs_unlock(sb, lock, SCOUTFS_LOCK_READ);
return ret ? ret : offset;
}
+4 -1
View File
@@ -95,6 +95,7 @@ static void scoutfs_inode_ctor(void *obj)
seqlock_init(&si->seqlock);
si->staging = false;
scoutfs_per_task_init(&si->pt_data_lock);
scoutfs_per_task_init(&si->pt_extent_sem);
atomic64_set(&si->data_waitq.changed, 0);
init_waitqueue_head(&si->data_waitq.waitq);
init_rwsem(&si->xattr_rwsem);
@@ -398,7 +399,9 @@ static int set_inode_size(struct inode *inode, struct scoutfs_lock *lock,
if (ret)
return ret;
scoutfs_per_task_add(&si->pt_data_lock, &pt_ent, lock);
if (!scoutfs_per_task_add_excl(&si->pt_data_lock, &pt_ent, lock))
WARN_ON_ONCE(true);
ret = block_truncate_page(inode->i_mapping, new_size, scoutfs_get_block_write);
scoutfs_per_task_del(&si->pt_data_lock, &pt_ent);
if (ret < 0)
+1
View File
@@ -51,6 +51,7 @@ struct scoutfs_inode_info {
seqlock_t seqlock;
bool staging; /* holder of i_mutex is staging */
struct scoutfs_per_task pt_data_lock;
struct scoutfs_per_task pt_extent_sem;
struct scoutfs_data_waitq data_waitq;
struct rw_semaphore xattr_rwsem;
struct list_head writeback_entry;
+3 -1
View File
@@ -49,6 +49,7 @@
#include "quota.h"
#include "scoutfs_trace.h"
#include "util.h"
#include "msg.h"
/*
* We make inode index items coherent by locking fixed size regions of
@@ -529,7 +530,8 @@ static long scoutfs_ioc_stage(struct file *file, unsigned long arg)
if (ret)
goto out;
scoutfs_per_task_add(&si->pt_data_lock, &pt_ent, lock);
if (!scoutfs_per_task_add_excl(&si->pt_data_lock, &pt_ent, lock))
WARN_ON_ONCE(true);
isize = i_size_read(inode);
+316
View File
@@ -0,0 +1,316 @@
/*
* Copyright (C) 2026 Versity Software, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License v2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*/
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/pagemap.h>
#include <linux/iomap.h>
#include "format.h"
#include "super.h"
#include "inode.h"
#include "key.h"
#include "counters.h"
#include "scoutfs_trace.h"
#include "item.h"
#include "btree.h"
#include "lock.h"
#include "ext.h"
#include "iomap.h"
#include "msg.h"
#include "trans.h"
#define KNOWN_FLAGS (IOMAP_REPORT|IOMAP_DIRECT|IOMAP_WRITE|IOMAP_NOWAIT)
static void scoutfs_set_iomap(struct inode *inode, struct iomap *iomap,
struct scoutfs_extent *ext, loff_t offset,
u64 iblock, loff_t length)
{
struct super_block *sb = inode->i_sb;
iomap->flags |= IOMAP_F_BUFFER_HEAD;
iomap->bdev = sb->s_bdev;
if (offset + length > i_size_read(inode))
iomap->flags |= IOMAP_F_DIRTY;
if (ext->len == 0 || ext->start > iblock) {
iomap->type = IOMAP_HOLE;
iomap->addr = IOMAP_NULL_ADDR;
iomap->offset = offset;
if (ext->len > 0) {
/* There's a hole at the starting offset */
iomap->length = min_t(loff_t,
(ext->start - iblock) << SCOUTFS_BLOCK_SM_SHIFT,
length);
} else {
/* There's an implicit hole at EOF */
iomap->length = length;
}
goto out;
}
if (ext->flags & SEF_OFFLINE) {
iomap->type = IOMAP_DELALLOC;
iomap->addr = IOMAP_NULL_ADDR;
} else if (ext->flags & SEF_UNWRITTEN) {
iomap->type = IOMAP_UNWRITTEN;
iomap->addr = (u64) ext->map << SCOUTFS_BLOCK_SM_SHIFT;
iomap->flags |= IOMAP_F_NEW;
} else if (ext->map) {
iomap->type = IOMAP_MAPPED;
iomap->addr = (u64) ext->map << SCOUTFS_BLOCK_SM_SHIFT;
} else {
WARN_ON(true); /* holes should've been handled above */
}
iomap->offset = (u64) ext->start << SCOUTFS_BLOCK_SM_SHIFT;
iomap->length = (u64) ext->len << SCOUTFS_BLOCK_SM_SHIFT;
out:
trace_scoutfs_set_iomap(sb, scoutfs_ino(inode), iblock, length, iomap->type,
iomap->flags, iomap->offset, iomap->length, ext->map);
}
static int scoutfs_iomap_begin_report(struct inode *inode, loff_t offset, loff_t length,
unsigned int flags, struct iomap *iomap,
struct iomap *srcmap)
{
struct scoutfs_inode_info *si = SCOUTFS_I(inode);
struct super_block *sb = inode->i_sb;
const u64 ino = scoutfs_ino(inode);
struct scoutfs_lock *lock = NULL;
struct scoutfs_extent ext;
struct data_ext_args args;
u64 iblock;
int ret;
WARN_ON_ONCE(flags & ~KNOWN_FLAGS);
WARN_ON(!inode_is_locked(inode));
iblock = offset >> SCOUTFS_BLOCK_SM_SHIFT;
/* make sure caller holds a cluster lock */
lock = scoutfs_per_task_get(&si->pt_data_lock);
WARN_ON(!lock);
args.ino = ino;
args.inode = inode;
args.lock = lock;
memset(&ext, 0, sizeof(ext));
down_read(&si->extent_sem);
ret = scoutfs_ext_next(sb, &data_ext_ops, &args, iblock, 1, &ext);
if (ret == -ENOENT)
ret = 0;
if (ret == 0)
scoutfs_set_iomap(inode, iomap, &ext, offset, iblock, length);
up_read(&si->extent_sem);
return ret;
}
const struct iomap_ops scoutfs_iomap_report_ops = {
.iomap_begin = scoutfs_iomap_begin_report,
};
#ifdef KC_USE_IOMAP_FOR_IO
static int scoutfs_iomap_begin(struct inode *inode, loff_t offset, loff_t length,
unsigned int flags, struct iomap *iomap,
struct iomap *srcmap)
{
struct scoutfs_inode_info *si = SCOUTFS_I(inode);
struct super_block *sb = inode->i_sb;
const u64 ino = scoutfs_ino(inode);
struct scoutfs_lock *data_lock = NULL;
struct scoutfs_extent ext;
struct data_ext_args args;
u64 iblock;
bool write_locked = false;
int ret = 0;
WARN_ON_ONCE(flags & ~KNOWN_FLAGS);
iblock = offset >> SCOUTFS_BLOCK_SM_SHIFT;
/* make sure caller holds a cluster lock */
data_lock = scoutfs_per_task_get(&si->pt_data_lock);
WARN_ON(!data_lock);
args.ino = ino;
args.inode = inode;
args.lock = data_lock;
memset(&ext, 0, sizeof(ext));
if (flags & IOMAP_WRITE) {
down_write(&si->extent_sem);
write_locked = true;
ret = scoutfs_ext_next(sb, &data_ext_ops, &args, iblock, 1, &ext);
if (ret == -ENOENT)
ret = 0;
if (ret < 0)
goto out;
if (ext.start > iblock)
memset(&ext, 0, sizeof(ext));
/* non-staging callers should have waited on offline blocks */
if (WARN_ON_ONCE(ext.map && (ext.flags & SEF_OFFLINE) && !si->staging)){
ret = -EIO;
goto out;
}
if (!si->staging) {
ret = scoutfs_inode_check_retention(inode);
if (ret < 0)
goto out;
}
/* No need to allocate space */
if (ext.map) {
trace_scoutfs_data_get_block_found(sb, ino, &ext);
goto out;
}
ret = scoutfs_data_alloc_block(sb, inode, &ext, iblock, data_lock);
if (ret == 0)
iomap->flags |= IOMAP_F_NEW;
} else {
down_read(&si->extent_sem);
ret = scoutfs_ext_next(sb, &data_ext_ops, &args, iblock, 1, &ext);
up_read(&si->extent_sem);
if (ret == -ENOENT)
ret = 0;
if (ext.len)
trace_scoutfs_data_get_block_found(sb, ino, &ext);
}
out:
if (ret == 0)
scoutfs_set_iomap(inode, iomap, &ext, offset, iblock, length);
if (write_locked)
up_write(&si->extent_sem);
return ret;
}
static int scoutfs_iomap_end(struct inode *inode, loff_t offset, loff_t length,
ssize_t written, unsigned flags, struct iomap *iomap)
{
struct scoutfs_inode_info *si = SCOUTFS_I(inode);
struct super_block *sb = inode->i_sb;
struct scoutfs_lock *data_lock = NULL;
struct scoutfs_extent un;
struct scoutfs_extent ext;
struct data_ext_args args;
u64 blks_remaining;
u64 start_blk;
u64 end_blk;
int ret = 0;
if (!(flags & IOMAP_WRITE))
return 0;
/*
* If we hit an error during a direct I/O write, tell the iomap layer to fall
* back to buffered I/O by returning the magic value -ENOTBLK.
*/
if ((flags & IOMAP_DIRECT) && written == 0)
return -ENOTBLK;
data_lock = scoutfs_per_task_get(&si->pt_data_lock);
WARN_ON(!data_lock);
down_write(&si->extent_sem);
/* convert unwritten to written, could be staging */
if (iomap->type == IOMAP_UNWRITTEN) {
args.ino = scoutfs_ino(inode);
args.inode = inode;
args.lock = data_lock;
/* This is the file block where this write began */
start_blk = offset >> SCOUTFS_BLOCK_SM_SHIFT;
/* This is the file block where this write ended */
end_blk = (offset + written - 1) >> SCOUTFS_BLOCK_SM_SHIFT;
/* We wrote this many blocks */
blks_remaining = (end_blk - start_blk) + 1;
while (blks_remaining > 0) {
ret = scoutfs_ext_next(sb, &data_ext_ops, &args, start_blk,
1, &ext);
/*
* The extent with the original starting block(s) has to exist.
* It's possible that it has been split by page_mkwrite
* converting one or more of the blocks from unwritten to
* written while we weren't holding the extent_sem in the gap
* between iomap_begin and iomap_end. The starting block
* still has to be contained within the extent we found.
*
* But we hold the vfs inode write lock, so we don't have to
* worry about truncate removing the extent(s) that cover our
* iomap.
*/
BUG_ON(ret);
BUG_ON(start_blk < ext.start);
un.start = start_blk;
un.len = min_t(u64, blks_remaining,
(ext.len - (un.start - ext.start)));
un.map = (iomap->addr >> SCOUTFS_BLOCK_SM_SHIFT) +
(un.start - (iomap->offset >> SCOUTFS_BLOCK_SM_SHIFT));
WARN_ON(un.map != (ext.map + (un.start - ext.start)));
un.flags = ext.flags & ~SEF_UNWRITTEN;
ret = scoutfs_ext_set(sb, &data_ext_ops, &args, un.start, un.len,
un.map, un.flags);
WARN_ON(ret); /* uh-oh, we already wrote those blocks */
start_blk += un.len;
blks_remaining -= un.len;
}
}
if (offset + written > i_size_read(inode))
i_size_write(inode, offset + written);
up_write(&si->extent_sem);
return ret;
}
const struct iomap_ops scoutfs_iomap_ops = {
.iomap_begin = scoutfs_iomap_begin,
.iomap_end = scoutfs_iomap_end,
};
#endif
+8
View File
@@ -0,0 +1,8 @@
#ifndef _SCOUTFS_IOMAP_H
#define _SCOUTFS_IOMAP_H
extern const struct iomap_ops scoutfs_iomap_report_ops;
extern const struct iomap_ops scoutfs_iomap_ops;
extern const struct iomap_page_ops scoutfs_iomap_page_ops;
#endif
+34
View File
@@ -3269,6 +3269,40 @@ TRACE_EVENT(scoutfs_trigger_fired,
TP_printk(SCSBF" %s", SCSB_TRACE_ARGS, __entry->name)
);
TRACE_EVENT(scoutfs_set_iomap,
TP_PROTO(struct super_block *sb, u64 ino, u64 iblock, loff_t length,
unsigned int type, unsigned int iflags, loff_t ioffset, loff_t ilength,
u64 map),
TP_ARGS(sb, ino, iblock, length, type, iflags, ioffset, ilength, map),
TP_STRUCT__entry(
SCSB_TRACE_FIELDS
__field(u64, ino)
__field(u64, iblock)
__field(loff_t, length)
__field(unsigned int, type)
__field(loff_t, ioffset)
__field(loff_t, ilength)
__field(u64, map)
),
TP_fast_assign(
SCSB_TRACE_ASSIGN(sb);
__entry->ino = ino;
__entry->iblock = iblock;
__entry->length = length;
__entry->type = type;
__entry->ioffset = ioffset;
__entry->ilength = ilength;
__entry->map = map;
),
TP_printk(SCSBF" ino %llu iblock %llu len %llu type %d ioff %llu ilen %llu map %llu", SCSB_TRACE_ARGS,
__entry->ino, __entry->iblock, __entry->length, __entry->type,
__entry->ioffset, __entry->ilength, __entry->map)
);
#endif /* _TRACE_SCOUTFS_H */
/* This part must be outside protection */
-1
View File
@@ -330,7 +330,6 @@ int scoutfs_trans_sync(struct super_block *sb, int wait)
struct write_attempt attempt = { .ret = 0 };
int ret;
if (!wait) {
queue_trans_work(sb);
return 0;
+1 -33
View File
@@ -65,7 +65,6 @@ generic/088
generic/090
generic/091
generic/092
generic/094
generic/096
generic/097
generic/098
@@ -168,7 +167,6 @@ generic/220
generic/221
generic/222
generic/223
generic/225
generic/227
generic/228
generic/229
@@ -430,7 +428,6 @@ generic/584
generic/586
generic/587
generic/588
generic/591
generic/592
generic/593
generic/594
@@ -537,13 +534,9 @@ generic/078
generic/079
generic/081
generic/082
generic/091
generic/094
generic/096
generic/110
generic/111
generic/113
generic/114
generic/115
generic/116
generic/118
@@ -552,9 +545,7 @@ generic/121
generic/122
generic/123
generic/128
generic/130
generic/134
generic/135
generic/136
generic/138
generic/139
@@ -602,7 +593,6 @@ generic/194
generic/195
generic/196
generic/197
generic/198
generic/199
generic/200
generic/201
@@ -610,11 +600,6 @@ generic/202
generic/203
generic/205
generic/206
generic/207
generic/210
generic/211
generic/212
generic/214
generic/216
generic/217
generic/218
@@ -622,13 +607,11 @@ generic/219
generic/220
generic/222
generic/223
generic/225
generic/227
generic/229
generic/230
generic/235
generic/238
generic/240
generic/244
generic/250
generic/252
@@ -640,7 +623,6 @@ generic/259
generic/260
generic/261
generic/262
generic/263
generic/264
generic/265
generic/266
@@ -709,7 +691,6 @@ generic/383
generic/384
generic/385
generic/386
generic/391
generic/392
generic/395
generic/396
@@ -718,17 +699,14 @@ generic/398
generic/400
generic/402
generic/404
generic/406
generic/407
generic/408
generic/412
generic/413
generic/414
generic/417
generic/419
generic/420
generic/421
generic/422
generic/424
generic/425
generic/427
@@ -736,15 +714,12 @@ generic/439
generic/440
generic/446
generic/449
generic/450
generic/451
generic/453
generic/454
generic/456
generic/458
generic/462
generic/463
generic/465
generic/466
generic/468
generic/469
@@ -776,7 +751,6 @@ generic/528
generic/530
generic/536
generic/537
generic/538
generic/539
generic/540
generic/541
@@ -788,7 +762,6 @@ generic/546
generic/548
generic/549
generic/550
generic/552
generic/553
generic/555
generic/556
@@ -806,10 +779,8 @@ generic/581
generic/582
generic/583
generic/584
generic/586
generic/587
generic/588
generic/591
generic/592
generic/593
generic/594
@@ -826,7 +797,6 @@ generic/605
generic/606
generic/607
generic/608
generic/609
generic/610
generic/612
generic/613
@@ -842,7 +812,6 @@ generic/635
generic/644
generic/645
generic/646
generic/647
generic/651
generic/652
generic/653
@@ -864,7 +833,6 @@ generic/669
generic/673
generic/674
generic/675
generic/677
generic/678
generic/679
generic/680
@@ -879,4 +847,4 @@ generic/688
generic/689
shared/002
shared/032
Passed all 512 tests
Passed all 509 tests
+876
View File
@@ -0,0 +1,876 @@
Ran:
generic/001
generic/002
generic/004
generic/005
generic/006
generic/007
generic/008
generic/009
generic/011
generic/012
generic/013
generic/014
generic/015
generic/016
generic/018
generic/020
generic/021
generic/022
generic/023
generic/024
generic/025
generic/026
generic/028
generic/029
generic/030
generic/031
generic/032
generic/033
generic/034
generic/035
generic/037
generic/039
generic/040
generic/041
generic/050
generic/052
generic/053
generic/056
generic/057
generic/058
generic/059
generic/060
generic/061
generic/062
generic/063
generic/064
generic/065
generic/066
generic/067
generic/069
generic/070
generic/071
generic/073
generic/076
generic/078
generic/079
generic/080
generic/081
generic/082
generic/084
generic/086
generic/087
generic/088
generic/090
generic/091
generic/092
generic/096
generic/097
generic/098
generic/099
generic/101
generic/104
generic/105
generic/106
generic/107
generic/110
generic/111
generic/113
generic/114
generic/115
generic/116
generic/117
generic/118
generic/119
generic/120
generic/121
generic/122
generic/123
generic/124
generic/126
generic/128
generic/129
generic/130
generic/131
generic/134
generic/135
generic/136
generic/138
generic/139
generic/140
generic/141
generic/142
generic/143
generic/144
generic/145
generic/146
generic/147
generic/148
generic/149
generic/150
generic/151
generic/152
generic/153
generic/154
generic/155
generic/156
generic/157
generic/158
generic/159
generic/160
generic/161
generic/162
generic/163
generic/169
generic/171
generic/172
generic/173
generic/174
generic/177
generic/178
generic/179
generic/180
generic/181
generic/182
generic/183
generic/184
generic/185
generic/188
generic/189
generic/190
generic/191
generic/193
generic/194
generic/195
generic/196
generic/197
generic/198
generic/199
generic/200
generic/201
generic/202
generic/203
generic/205
generic/206
generic/207
generic/210
generic/211
generic/212
generic/214
generic/215
generic/216
generic/217
generic/218
generic/219
generic/220
generic/221
generic/222
generic/223
generic/227
generic/228
generic/229
generic/230
generic/235
generic/236
generic/237
generic/238
generic/240
generic/244
generic/245
generic/246
generic/247
generic/248
generic/249
generic/250
generic/252
generic/253
generic/254
generic/255
generic/256
generic/257
generic/258
generic/259
generic/260
generic/261
generic/262
generic/263
generic/264
generic/265
generic/266
generic/267
generic/268
generic/271
generic/272
generic/276
generic/277
generic/278
generic/279
generic/281
generic/282
generic/283
generic/284
generic/286
generic/287
generic/288
generic/289
generic/290
generic/291
generic/292
generic/293
generic/294
generic/295
generic/296
generic/301
generic/302
generic/303
generic/304
generic/305
generic/306
generic/307
generic/308
generic/309
generic/312
generic/313
generic/314
generic/315
generic/316
generic/317
generic/319
generic/322
generic/324
generic/325
generic/326
generic/327
generic/328
generic/329
generic/330
generic/331
generic/332
generic/335
generic/336
generic/337
generic/341
generic/342
generic/343
generic/346
generic/348
generic/353
generic/355
generic/358
generic/359
generic/360
generic/361
generic/362
generic/363
generic/364
generic/365
generic/366
generic/367
generic/368
generic/369
generic/370
generic/371
generic/372
generic/373
generic/374
generic/375
generic/376
generic/377
generic/378
generic/379
generic/380
generic/381
generic/382
generic/383
generic/384
generic/385
generic/386
generic/389
generic/391
generic/392
generic/393
generic/394
generic/395
generic/396
generic/397
generic/398
generic/400
generic/401
generic/402
generic/403
generic/404
generic/406
generic/407
generic/408
generic/412
generic/413
generic/414
generic/417
generic/419
generic/420
generic/421
generic/422
generic/424
generic/425
generic/426
generic/427
generic/428
generic/436
generic/437
generic/439
generic/440
generic/443
generic/445
generic/446
generic/448
generic/449
generic/450
generic/451
generic/452
generic/453
generic/454
generic/456
generic/458
generic/460
generic/462
generic/463
generic/465
generic/466
generic/468
generic/469
generic/470
generic/471
generic/474
generic/477
generic/478
generic/479
generic/480
generic/481
generic/483
generic/485
generic/486
generic/487
generic/488
generic/489
generic/490
generic/491
generic/492
generic/498
generic/499
generic/501
generic/502
generic/503
generic/504
generic/505
generic/506
generic/507
generic/508
generic/509
generic/510
generic/511
generic/512
generic/513
generic/514
generic/515
generic/516
generic/517
generic/518
generic/519
generic/520
generic/523
generic/524
generic/525
generic/526
generic/527
generic/528
generic/529
generic/530
generic/531
generic/533
generic/534
generic/535
generic/536
generic/537
generic/538
generic/539
generic/540
generic/541
generic/542
generic/543
generic/544
generic/545
generic/546
generic/547
generic/548
generic/549
generic/550
generic/552
generic/553
generic/555
generic/556
generic/557
generic/566
generic/567
generic/571
generic/572
generic/573
generic/574
generic/575
generic/576
generic/577
generic/578
generic/580
generic/581
generic/582
generic/583
generic/584
generic/586
generic/587
generic/588
generic/592
generic/593
generic/594
generic/595
generic/596
generic/597
generic/598
generic/599
generic/600
generic/601
generic/602
generic/603
generic/604
generic/605
generic/606
generic/607
generic/608
generic/609
generic/610
generic/611
generic/612
generic/613
generic/614
generic/618
generic/621
generic/623
generic/624
generic/625
generic/626
generic/628
generic/629
generic/630
generic/632
generic/634
generic/635
generic/637
generic/638
generic/639
generic/640
generic/644
generic/645
generic/646
generic/647
generic/651
generic/652
generic/653
generic/654
generic/655
generic/657
generic/658
generic/659
generic/660
generic/661
generic/662
generic/663
generic/664
generic/665
generic/666
generic/667
generic/668
generic/669
generic/673
generic/674
generic/675
generic/676
generic/677
generic/678
generic/679
generic/680
generic/681
generic/682
generic/683
generic/684
generic/685
generic/686
generic/687
generic/688
generic/689
shared/002
shared/032
Not
run:
generic/008
generic/009
generic/012
generic/015
generic/016
generic/018
generic/021
generic/022
generic/025
generic/026
generic/031
generic/033
generic/050
generic/052
generic/058
generic/059
generic/060
generic/061
generic/063
generic/064
generic/078
generic/079
generic/081
generic/082
generic/091
generic/096
generic/110
generic/111
generic/113
generic/114
generic/115
generic/116
generic/118
generic/119
generic/121
generic/122
generic/123
generic/128
generic/130
generic/134
generic/135
generic/136
generic/138
generic/139
generic/140
generic/142
generic/143
generic/144
generic/145
generic/146
generic/147
generic/148
generic/149
generic/150
generic/151
generic/152
generic/153
generic/154
generic/155
generic/156
generic/157
generic/158
generic/159
generic/160
generic/161
generic/162
generic/163
generic/171
generic/172
generic/173
generic/174
generic/177
generic/178
generic/179
generic/180
generic/181
generic/182
generic/183
generic/185
generic/188
generic/189
generic/190
generic/191
generic/193
generic/194
generic/195
generic/196
generic/197
generic/198
generic/199
generic/200
generic/201
generic/202
generic/203
generic/205
generic/206
generic/207
generic/210
generic/211
generic/212
generic/214
generic/216
generic/217
generic/218
generic/219
generic/220
generic/222
generic/223
generic/227
generic/229
generic/230
generic/235
generic/238
generic/240
generic/244
generic/250
generic/252
generic/253
generic/254
generic/255
generic/256
generic/259
generic/260
generic/261
generic/262
generic/263
generic/264
generic/265
generic/266
generic/267
generic/268
generic/271
generic/272
generic/276
generic/277
generic/278
generic/279
generic/281
generic/282
generic/283
generic/284
generic/287
generic/288
generic/289
generic/290
generic/291
generic/292
generic/293
generic/295
generic/296
generic/301
generic/302
generic/303
generic/304
generic/305
generic/312
generic/314
generic/316
generic/317
generic/324
generic/326
generic/327
generic/328
generic/329
generic/330
generic/331
generic/332
generic/353
generic/355
generic/358
generic/359
generic/361
generic/362
generic/363
generic/364
generic/365
generic/366
generic/367
generic/368
generic/369
generic/370
generic/371
generic/372
generic/373
generic/374
generic/378
generic/379
generic/380
generic/381
generic/382
generic/383
generic/384
generic/385
generic/386
generic/391
generic/392
generic/395
generic/396
generic/397
generic/398
generic/400
generic/402
generic/404
generic/406
generic/407
generic/408
generic/412
generic/413
generic/414
generic/417
generic/419
generic/420
generic/421
generic/422
generic/424
generic/425
generic/427
generic/439
generic/440
generic/446
generic/449
generic/450
generic/451
generic/453
generic/454
generic/456
generic/458
generic/462
generic/463
generic/465
generic/466
generic/468
generic/469
generic/470
generic/471
generic/474
generic/485
generic/487
generic/488
generic/491
generic/492
generic/499
generic/501
generic/503
generic/505
generic/506
generic/507
generic/508
generic/511
generic/513
generic/514
generic/515
generic/516
generic/517
generic/518
generic/519
generic/520
generic/528
generic/530
generic/536
generic/537
generic/538
generic/539
generic/540
generic/541
generic/542
generic/543
generic/544
generic/545
generic/546
generic/548
generic/549
generic/550
generic/552
generic/553
generic/555
generic/556
generic/566
generic/567
generic/572
generic/573
generic/574
generic/575
generic/576
generic/577
generic/578
generic/580
generic/581
generic/582
generic/583
generic/584
generic/586
generic/587
generic/588
generic/592
generic/593
generic/594
generic/595
generic/596
generic/597
generic/598
generic/599
generic/600
generic/601
generic/602
generic/603
generic/605
generic/606
generic/607
generic/608
generic/609
generic/610
generic/612
generic/613
generic/621
generic/623
generic/624
generic/625
generic/626
generic/628
generic/629
generic/630
generic/635
generic/644
generic/645
generic/646
generic/647
generic/651
generic/652
generic/653
generic/654
generic/655
generic/657
generic/658
generic/659
generic/660
generic/661
generic/662
generic/663
generic/664
generic/665
generic/666
generic/667
generic/668
generic/669
generic/673
generic/674
generic/675
generic/677
generic/678
generic/679
generic/680
generic/681
generic/682
generic/683
generic/684
generic/685
generic/686
generic/687
generic/688
generic/689
shared/002
shared/032
Passed all 509 tests
+3
View File
@@ -1,9 +1,11 @@
generic/003 # missing atime update in buffered read
generic/075 # file content mismatch failures (fds, etc)
generic/094 # preallocation generates an unwritten block where a hole is expected
generic/103 # enospc causes trans commit failures
generic/108 # mount fails on failing device?
generic/112 # file content mismatch failures (fds, etc)
generic/213 # enospc causes trans commit failures
generic/225 # preallocation generates an unwritten block where a hole is expected
generic/318 # can't support user namespaces until v5.11
generic/321 # requires selinux enabled for '+' in ls?
generic/338 # BUG_ON update inode error handling
@@ -37,6 +39,7 @@ generic/565 # xfs_io copy_range missing in el7
generic/568 # falloc not resulting in block count increase
generic/569 # swap
generic/570 # swap
generic/591 # do we care if splice() and O_DIRECT works on pipes?
generic/620 # dm-hugedisk
generic/633 # id-mapped mounts missing in el7
generic/636 # swap
+5
View File
@@ -231,6 +231,11 @@ t_filter_dmesg()
# lockdep or kasan warnings can cause this
re="$re|Disabling lock debugging due to kernel taint"
# mixing mmap and direct I/O can generate a warning
re="$re|Page cache invalidation failure on direct I/O. Possible data corruption due to collision with buffered I/O!"
re="$re|File: /mnt/test.* PID:.* Comm: dd"
re="$re|dio_warn_stale_pagecache*"
egrep -v "($re)" | \
ignore_harmless_unwind_kasan_stack_oob | \
ignore_harmless_xfs_lockdep_warning
-2
View File
@@ -10,14 +10,12 @@ thread 4 complete
extents: 1
1
00000200: ea ea ea ea ea ea ea ea ea ea ea ea ea ea ea ea ................
0
0: offset: 0 length: 2 flags: ..L
extents: 1
== mmap write to an offline extent
0: offset: 0 length: 2 flags: O.L
extents: 1
1
0
0: offset: 0 length: 2 flags: ..L
extents: 1
00000000 ea ea ea ea ea ea ea ea ea ea ea ea ea ea ea ea |................|
+84 -78
View File
@@ -90,7 +90,7 @@ done
# set some T_ defaults
T_TRACE_DUMP="0"
T_TRACE_PRINTK="0"
T_TRACE_PRINTK=""
T_PORT_START="19700"
T_LOOP_ITER="1"
@@ -137,6 +137,9 @@ while true; do
test -n "$2" || die "-l must have a nr iterations argument"
test "$2" -eq "$2" 2>/dev/null || die "-l <nr> argument must be an integer"
T_LOOP_ITER="$2"
# when looping, break after first failure
T_ABORT="1"
shift
;;
-M)
@@ -399,31 +402,44 @@ if [ -n "$T_INSMOD" ]; then
cmd insmod "$T_MODULE"
fi
if [ -n "$T_TRACE_MULT" ]; then
# orig_trace_size=$(cat /sys/kernel/debug/tracing/buffer_size_kb)
orig_trace_size=1408
mult_trace_size=$((orig_trace_size * T_TRACE_MULT))
msg "increasing trace buffer size from $orig_trace_size KiB to $mult_trace_size KiB"
echo $mult_trace_size > /sys/kernel/debug/tracing/buffer_size_kb
fi
start_tracing() {
if [ -n "$T_TRACE_MULT" ]; then
orig_trace_size=1408
mult_trace_size=$((orig_trace_size * T_TRACE_MULT))
msg "increasing trace buffer size from $orig_trace_size KiB to $mult_trace_size KiB"
echo $mult_trace_size > /sys/kernel/debug/tracing/buffer_size_kb
fi
nr_globs=${#T_TRACE_GLOB[@]}
if [ $nr_globs -gt 0 ]; then
echo 0 > /sys/kernel/debug/tracing/events/scoutfs/enable
nr_globs=${#T_TRACE_GLOB[@]}
if [ $nr_globs -gt 0 ]; then
echo 0 > /sys/kernel/debug/tracing/events/scoutfs/enable
for g in "${T_TRACE_GLOB[@]}"; do
for e in /sys/kernel/debug/tracing/events/scoutfs/$g/enable; do
if test -w "$e"; then
echo 1 > "$e"
else
die "-t glob '$g' matched no scoutfs events"
fi
for g in "${T_TRACE_GLOB[@]}"; do
for e in /sys/kernel/debug/tracing/events/scoutfs/$g/enable; do
if test -w "$e"; then
echo 1 > "$e"
else
die "-t glob '$g' matched no scoutfs events"
fi
done
done
done
nr_events=$(cat /sys/kernel/debug/tracing/set_event | wc -l)
msg "enabled $nr_events trace events from $nr_globs -t globs"
fi
nr_events=$(cat /sys/kernel/debug/tracing/set_event | wc -l)
msg "enabled $nr_events trace events from $nr_globs -t globs"
fi
}
stop_tracing() {
if [ -n "$T_TRACE_GLOB" -o -n "$T_TRACE_PRINTK" ]; then
msg "saving traces and disabling tracing"
echo 0 > /sys/kernel/debug/tracing/events/scoutfs/enable
echo 0 > /sys/kernel/debug/tracing/options/trace_printk
cat /sys/kernel/debug/tracing/trace | gzip > "$T_RESULTS/traces.gz"
if [ -n "$orig_trace_size" ]; then
echo $orig_trace_size > /sys/kernel/debug/tracing/buffer_size_kb
fi
fi
}
if [ -n "$T_TRACE_PRINTK" ]; then
echo "$T_TRACE_PRINTK" > /sys/kernel/debug/tracing/options/trace_printk
@@ -603,24 +619,26 @@ passed=0
skipped=0
failed=0
skipped_permitted=0
for t in $tests; do
# tests has basenames from sequence, get path and name
t="tests/$t"
test_name=$(basename "$t" | sed -e 's/.sh$//')
for iter in $(seq 1 $T_LOOP_ITER); do
# get stats from previous pass
last="$T_RESULTS/last-passed-test-stats"
stats=$(grep -s "^$test_name " "$last" | cut -d " " -f 2-)
test -n "$stats" && stats="last: $stats"
printf " %-30s $stats" "$test_name"
start_tracing
# mark in dmesg as to what test we are running
echo "run scoutfs test $test_name" > /dev/kmsg
for t in $tests; do
# tests has basenames from sequence, get path and name
t="tests/$t"
test_name=$(basename "$t" | sed -e 's/.sh$//')
# let the test get at its extra files
T_EXTRA="$T_TESTS/extra/$test_name"
# get stats from previous pass
last="$T_RESULTS/last-passed-test-stats"
stats=$(grep -s "^$test_name " "$last" | cut -d " " -f 2-)
test -n "$stats" && stats="last: $stats"
printf " %-30s $stats" "$test_name"
for iter in $(seq 1 $T_LOOP_ITER); do
# mark in dmesg as to what test we are running
echo "run scoutfs test $test_name" > /dev/kmsg
# let the test get at its extra files
T_EXTRA="$T_TESTS/extra/$test_name"
# create a temporary dir and file path for the test
T_TMPDIR="$T_RESULTS/tmp/$test_name"
@@ -710,55 +728,43 @@ for t in $tests; do
sts=$T_FAIL_STATUS
fi
# stop looping if we didn't pass
if [ "$sts" != "$T_PASS_STATUS" ]; then
break;
# show and record the result of the test
if [ "$sts" == "$T_PASS_STATUS" ]; then
echo " passed: $stats"
((passed++))
# save stats for passed test
grep -s -v "^$test_name " "$last" > "$last.tmp"
echo "$test_name $stats" >> "$last.tmp"
mv -f "$last.tmp" "$last"
elif [ "$sts" == "$T_SKIP_PERMITTED_STATUS" ]; then
echo " [ skipped (permitted): $message ]"
echo "$test_name skipped (permitted) $message " >> "$T_RESULTS/skip.log"
((skipped_permitted++))
elif [ "$sts" == "$T_SKIP_STATUS" ]; then
echo " [ skipped: $message ]"
echo "$test_name $message" >> "$T_RESULTS/skip.log"
((skipped++))
elif [ "$sts" == "$T_FAIL_STATUS" ]; then
echo " [ failed: $message ]"
echo "$test_name $message" >> "$T_RESULTS/fail.log"
((failed++))
if [ -n "$T_ABORT" ]; then
stop_tracing
die "aborting after first failure"
fi
fi
# record results for TAP format output
t_tap_progress $test_name $sts
((testcount++))
done
# show and record the result of the test
if [ "$sts" == "$T_PASS_STATUS" ]; then
echo " passed: $stats"
((passed++))
# save stats for passed test
grep -s -v "^$test_name " "$last" > "$last.tmp"
echo "$test_name $stats" >> "$last.tmp"
mv -f "$last.tmp" "$last"
elif [ "$sts" == "$T_SKIP_PERMITTED_STATUS" ]; then
echo " [ skipped (permitted): $message ]"
echo "$test_name skipped (permitted) $message " >> "$T_RESULTS/skip.log"
((skipped_permitted++))
elif [ "$sts" == "$T_SKIP_STATUS" ]; then
echo " [ skipped: $message ]"
echo "$test_name $message" >> "$T_RESULTS/skip.log"
((skipped++))
elif [ "$sts" == "$T_FAIL_STATUS" ]; then
echo " [ failed: $message ]"
echo "$test_name $message" >> "$T_RESULTS/fail.log"
((failed++))
test -n "$T_ABORT" && die "aborting after first failure"
fi
# record results for TAP format output
t_tap_progress $test_name $sts
((testcount++))
stop_tracing
done
msg "all tests run: $passed passed, $skipped skipped, $skipped_permitted skipped (permitted), $failed failed"
if [ -n "$T_TRACE_GLOB" -o -n "$T_TRACE_PRINTK" ]; then
msg "saving traces and disabling tracing"
echo 0 > /sys/kernel/debug/tracing/events/scoutfs/enable
echo 0 > /sys/kernel/debug/tracing/options/trace_printk
cat /sys/kernel/debug/tracing/trace > "$T_RESULTS/traces"
if [ -n "$orig_trace_size" ]; then
echo $orig_trace_size > /sys/kernel/debug/tracing/buffer_size_kb
fi
fi
if [ "$skipped" == 0 -a "$failed" == 0 ]; then
msg "all tests passed"
unmount_all
+1 -1
View File
@@ -80,7 +80,7 @@ static void *run_test_func(void *ptr)
for (read = 0; read < size;) {
ret = pread(fd, buf, size - read, read);
if (ret < 0) {
perror("pwrite");
perror("pread");
exit(-1);
}
read += ret;
+2 -6
View File
@@ -27,9 +27,7 @@ sleep 1
jobs | wc -l
scoutfs stage "${F}-stage" "$F" -V "$vers" -o 0 -l 8192
# xfs_io thread <here> will output 16 bytes of read data
sleep 1
# should be 0 - no more waiting jobs, xfs_io should have exited
jobs | wc -l
wait
scoutfs get-fiemap -L "$F"
echo "== mmap write to an offline extent"
@@ -43,9 +41,7 @@ sleep 1
jobs | wc -l
scoutfs stage "${F}-stage" "$F" -V "$vers" -o 0 -l 8192
# no output here from write
sleep 1
# should be 0 - no more waiting jobs, xfs_io should have exited
jobs | wc -l
wait
scoutfs get-fiemap -L "$F"
# read back contents to assure write changed the file
dd status=none if="$F" bs=1 count=48 skip=512 | hexdump -C
+8 -1
View File
@@ -116,7 +116,14 @@ awk '
grep -E "^(Ran|Not run|Failures):" "$T_TMPDIR/results" | fmt -w 1 > "$T_TMPDIR/results.fmt"
grep -E "^(Passed|Failed).*tests$" "$T_TMPDIR/results" >> "$T_TMPDIR/results.fmt"
diff -u "$T_EXTRA/expected-results" "$T_TMPDIR/results.fmt" > "$T_TMPDIR/results.diff"
if grep -q copy_page_to_iter_nofault /lib/modules/$(uname -r)/build/include/linux/uio.h
then
diff -u "$T_EXTRA/expected-results" "$T_TMPDIR/results.fmt" > "$T_TMPDIR/results.diff"
else
# no direct I/O
diff -u "$T_EXTRA/expected-results.nodio" "$T_TMPDIR/results.fmt" > "$T_TMPDIR/results.diff"
fi
if [ -s "$T_TMPDIR/results.diff" ]; then
echo "tests that were skipped/run differed from expected:"
cat "$T_TMPDIR/results.diff"