scoutfs: add data_wait_err for reporting errors

Add support for reporting errors to data waiters via a new
SCOUTFS_IOC_DATA_WAIT_ERR ioctl.  This allows waiters to return an error
to readers when staging fails.

Signed-off-by: Benjamin LaHaise <bcrl@kvack.org>
[zab: renamed to data_wait_err, took ino arg]
Signed-off-by: Zach Brown <zab@versity.com>
This commit is contained in:
Benjamin LaHaise
2020-05-20 16:19:03 -04:00
committed by Zach Brown
parent d16b18562d
commit f5863142be
5 changed files with 145 additions and 0 deletions

View File

@@ -1923,6 +1923,8 @@ int scoutfs_data_wait(struct inode *inode, struct scoutfs_data_wait *dw)
spin_lock(&rt->lock);
rb_erase(&dw->node, &rt->root);
RB_CLEAR_NODE(&dw->node);
if (!ret && dw->err)
ret = dw->err;
spin_unlock(&rt->lock);
return ret;
@@ -1936,6 +1938,36 @@ void scoutfs_data_wait_changed(struct inode *inode)
wake_up(&wq->waitq);
}
long scoutfs_data_wait_err(struct inode *inode, u64 sblock, u64 eblock,
u64 op, long err)
{
struct super_block *sb = inode->i_sb;
const u64 ino = scoutfs_ino(inode);
DECLARE_DATA_WAIT_ROOT(sb, rt);
struct scoutfs_data_wait *dw;
long nr = 0;
if (!err)
return 0;
spin_lock(&rt->lock);
for (dw = next_data_wait(&rt->root, ino, sblock);
dw; dw = dw_next(dw)) {
if (dw->ino != ino || dw->iblock > eblock)
break;
if ((dw->op & op) && !dw->err) {
dw->err = err;
nr++;
}
}
spin_unlock(&rt->lock);
if (nr)
scoutfs_data_wait_changed(inode);
return nr;
}
int scoutfs_data_waiting(struct super_block *sb, u64 ino, u64 iblock,
struct scoutfs_ioctl_data_waiting_entry *dwe,
unsigned int nr)

View File

@@ -28,12 +28,14 @@ struct scoutfs_data_wait {
u64 chg;
u64 ino;
u64 iblock;
long err;
u8 op;
};
#define DECLARE_DATA_WAIT(nm) \
struct scoutfs_data_wait nm = { \
.node.__rb_parent_color = (unsigned long)(&nm.node), \
.err = 0, \
}
struct scoutfs_traced_extent {
@@ -68,6 +70,8 @@ bool scoutfs_data_wait_found(struct scoutfs_data_wait *ow);
int scoutfs_data_wait(struct inode *inode,
struct scoutfs_data_wait *ow);
void scoutfs_data_wait_changed(struct inode *inode);
long scoutfs_data_wait_err(struct inode *inode, u64 sblock, u64 eblock, u64 op,
long err);
int scoutfs_data_waiting(struct super_block *sb, u64 ino, u64 iblock,
struct scoutfs_ioctl_data_waiting_entry *dwe,
unsigned int nr);

View File

@@ -348,6 +348,65 @@ out:
return ret;
}
static long scoutfs_ioc_data_wait_err(struct file *file, unsigned long arg)
{
struct super_block *sb = file_inode(file)->i_sb;
struct scoutfs_ioctl_data_wait_err args;
struct scoutfs_lock *lock = NULL;
struct inode *inode = NULL;
u64 sblock;
u64 eblock;
long ret;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
if (copy_from_user(&args, (void __user *)arg, sizeof(args)))
return -EFAULT;
if (args.count == 0)
return 0;
if ((args.op & SCOUTFS_IOC_DWO_UNKNOWN) || !IS_ERR_VALUE(args.err))
return -EINVAL;
if ((args.op & SCOUTFS_IOC_DWO_UNKNOWN) || !IS_ERR_VALUE(args.err))
return -EINVAL;
trace_scoutfs_ioc_data_wait_err(sb, &args);
sblock = args.offset >> SCOUTFS_BLOCK_SHIFT;
eblock = (args.offset + args.count - 1) >> SCOUTFS_BLOCK_SHIFT;
if (sblock > eblock)
return -EINVAL;
inode = scoutfs_ilookup(sb, args.ino);
if (!inode) {
ret = -ESTALE;
goto out;
}
mutex_lock(&inode->i_mutex);
ret = scoutfs_lock_inode(sb, SCOUTFS_LOCK_READ,
SCOUTFS_LKF_REFRESH_INODE, inode, &lock);
if (ret)
goto unlock;
if (!S_ISREG(inode->i_mode)) {
ret = -EINVAL;
} else if (scoutfs_inode_data_version(inode) != args.data_version) {
ret = -ESTALE;
} else {
ret = scoutfs_data_wait_err(inode, sblock, eblock, args.op,
args.err);
}
scoutfs_unlock(sb, lock, SCOUTFS_LOCK_READ);
unlock:
mutex_unlock(&inode->i_mutex);
iput(inode);
out:
return ret;
}
/*
* Write the archived contents of the file back if the data_version
* still matches.
@@ -832,6 +891,8 @@ long scoutfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
return scoutfs_ioc_find_xattrs(file, arg);
case SCOUTFS_IOC_STATFS_MORE:
return scoutfs_ioc_statfs_more(file, arg);
case SCOUTFS_IOC_DATA_WAIT_ERR:
return scoutfs_ioc_data_wait_err(file, arg);
}
return -ENOTTY;

View File

@@ -346,5 +346,22 @@ struct scoutfs_ioctl_statfs_more {
#define SCOUTFS_IOC_STATFS_MORE _IOR(SCOUTFS_IOCTL_MAGIC, 10, \
struct scoutfs_ioctl_statfs_more)
/*
* Cause matching waiters to return an error.
*
* Find current waiters that match the inode, op, and block range to wake
* up and return an error.
*/
struct scoutfs_ioctl_data_wait_err {
__u64 ino;
__u64 data_version;
__u64 offset;
__u64 count;
__u64 op;
__s64 err;
};
#define SCOUTFS_IOC_DATA_WAIT_ERR _IOR(SCOUTFS_IOCTL_MAGIC, 11, \
struct scoutfs_ioctl_data_wait_err)
#endif

View File

@@ -556,6 +556,37 @@ TRACE_EVENT(scoutfs_ioc_stage,
__entry->offset, __entry->count)
);
TRACE_EVENT(scoutfs_ioc_data_wait_err,
TP_PROTO(struct super_block *sb,
struct scoutfs_ioctl_data_wait_err *args),
TP_ARGS(sb, args),
TP_STRUCT__entry(
SCSB_TRACE_FIELDS
__field(__u64, ino)
__field(__u64, vers)
__field(__u64, offset)
__field(__u64, count)
__field(__u64, op)
__field(__s64, err)
),
TP_fast_assign(
SCSB_TRACE_ASSIGN(sb);
__entry->ino = args->ino;
__entry->vers = args->data_version;
__entry->offset = args->offset;
__entry->count = args->count;
__entry->op = args->op;
__entry->err = args->err;
),
TP_printk(SCSBF" ino %llu vers %llu offset %llu count %llu op %llx err %lld",
SCSB_TRACE_ARGS, __entry->ino, __entry->vers,
__entry->offset, __entry->count, __entry->op, __entry->err)
);
DEFINE_EVENT(scoutfs_ino_ret_class, scoutfs_ioc_stage_ret,
TP_PROTO(struct super_block *sb, u64 ino, int ret),
TP_ARGS(sb, ino, ret)