Add offline extent flag and release ioctl

Add the _OFFLINE flag to indicate offline extents.  The release ioctl
frees extents within the release range and sets their _OFFLINE flag if
the data_version still matches.

We tweak the existing truncate item function just a bit to support
making extents offline.  We make it take an explicit range of blocks to
remove instead of just giving it the size and it learns to mark extents
offline and update them instead of always deleting them.

Reads from offline extents return zeros like reading from a sparse
region (later it will trigger demand staging) and writing to offline
extents clears the offline flag (later only staging can do that).

Signed-off-by: Zach Brown <zab@versity.com>
Reviewed-by: Mark Fasheh <mfasheh@versity.com>
This commit is contained in:
Zach Brown
2016-11-16 14:45:08 -08:00
parent 5d87418925
commit df561bbd19
6 changed files with 142 additions and 28 deletions
+43 -26
View File
@@ -178,31 +178,28 @@ static void return_file_block(struct super_block *sb, u64 blkno)
}
/*
* Free mapped extents whose entire contents are past the new
* specified size. The caller holds a transaction.
* Free extents whose blocks fall inside the specified blocks. The
* caller holds a transaction.
*
* This is the low level extent item truncate code.
* Callers manage higher order truncation and orphan cleanup.
* If 'release' is given then blocks are freed inside i_size but the
* extent items are left behind and their _OFFLINE flag is set.
*
* XXX probably should be a range
* This is the low level extent item truncate code. Callers manage
* higher order truncation and orphan cleanup.
*/
int scoutfs_truncate_extent_items(struct super_block *sb, u64 ino, u64 size)
int scoutfs_truncate_extent_items(struct super_block *sb, u64 ino, u64 iblock,
u64 len, bool offline)
{
struct scoutfs_btree_root *meta = SCOUTFS_META(sb);
struct scoutfs_extent extent;
struct scoutfs_btree_val val;
struct scoutfs_key key;
struct scoutfs_key first;
u64 iblock;
u64 len;
u64 loff;
u64 seq;
int ret;
iblock = DIV_ROUND_UP(size, SCOUTFS_BLOCK_SIZE);
scoutfs_set_key(&first, ino, SCOUTFS_EXTENT_KEY, 0);
scoutfs_set_key(&key, ino, SCOUTFS_EXTENT_KEY, ~0ULL);
scoutfs_set_key(&first, ino, SCOUTFS_EXTENT_KEY, iblock);
scoutfs_set_key(&key, ino, SCOUTFS_EXTENT_KEY, iblock + len - 1);
trace_printk("iblock %llu\n", iblock);
@@ -218,28 +215,43 @@ int scoutfs_truncate_extent_items(struct super_block *sb, u64 ino, u64 size)
break;
}
loff = le64_to_cpu(key.offset);
len = le64_to_cpu(extent.len);
if (WARN_ON_ONCE(len != 1)) {
ret = -EIO;
break;
}
if ((loff + len) <= iblock)
/* XXX corruption: offline and allocation are exclusive */
if (!!extent.blkno ==
!!(extent.flags & SCOUTFS_EXTENT_FLAG_OFFLINE)) {
ret = -EIO;
break;
}
if (offline && (extent.flags & SCOUTFS_EXTENT_FLAG_OFFLINE))
continue;
/* make sure we can delete the extent after freeing */
ret = scoutfs_btree_dirty(sb, meta, &key);
if (ret)
break;
if (extent.blkno) {
ret = scoutfs_btree_dirty(sb, meta, &key);
if (ret)
break;
ret = scoutfs_buddy_free(sb, cpu_to_le64(seq),
le64_to_cpu(extent.blkno), 0);
if (ret)
break;
ret = scoutfs_buddy_free(sb, cpu_to_le64(seq),
le64_to_cpu(extent.blkno), 0);
if (ret)
break;
}
scoutfs_btree_delete(sb, meta, &key);
if (offline) {
extent.blkno = 0;
extent.flags |= SCOUTFS_EXTENT_FLAG_OFFLINE;
scoutfs_btree_update(sb, meta, &key, &val);
} else {
ret = scoutfs_btree_delete(sb, meta, &key);
if (ret)
break;
}
/* XXX sync transaction if it's enormous */
scoutfs_dec_key(&key);
@@ -286,8 +298,12 @@ static int contig_mapped_blocks(struct inode *inode, u64 iblock, u64 *blkno)
ret = scoutfs_btree_lookup(sb, meta, &key, &val);
if (ret == sizeof(extent)) {
*blkno = le64_to_cpu(extent.blkno);
ret = min_t(u64, le64_to_cpu(extent.len), INT_MAX);
if (extent.flags & SCOUTFS_EXTENT_FLAG_OFFLINE) {
ret = 0;
} else {
*blkno = le64_to_cpu(extent.blkno);
ret = min_t(u64, le64_to_cpu(extent.len), INT_MAX);
}
} else if (ret >= 0) {
/* XXX corruption */
ret = -EIO;
@@ -377,6 +393,7 @@ static int map_writable_block(struct inode *inode, u64 iblock, u64 *blkno_ret)
extent.blkno = cpu_to_le64(new_blkno);
extent.len = cpu_to_le64(1);
extent.flags &= ~SCOUTFS_EXTENT_FLAG_OFFLINE;
/* dirtying guarantees success */
err = scoutfs_btree_update(sb, meta, &key, &val);
+2 -1
View File
@@ -5,6 +5,7 @@ extern const struct address_space_operations scoutfs_file_aops;
extern const struct file_operations scoutfs_file_fops;
void scoutfs_filerw_free_alloc(struct super_block *sb);
int scoutfs_truncate_extent_items(struct super_block *sb, u64 ino, u64 size);
int scoutfs_truncate_extent_items(struct super_block *sb, u64 ino, u64 iblock,
u64 len, bool offline);
#endif
+2
View File
@@ -299,6 +299,8 @@ struct scoutfs_extent {
__u8 flags;
} __packed;
#define SCOUTFS_EXTENT_FLAG_OFFLINE (1 << 0)
/*
* link backrefs give us a way to find all the hard links that refer
* to a target inode. They're stored at an offset determined by an
+1 -1
View File
@@ -454,7 +454,7 @@ static int __delete_inode(struct super_block *sb, struct scoutfs_key *key,
if (S_ISLNK(mode))
ret = scoutfs_symlink_drop(sb, ino);
else if (S_ISREG(mode))
ret = scoutfs_truncate_extent_items(sb, ino, 0);
ret = scoutfs_truncate_extent_items(sb, ino, 0, ~0ULL, false);
if (ret)
goto out;
+85
View File
@@ -16,6 +16,8 @@
#include <linux/compiler.h>
#include <linux/uio.h>
#include <linux/slab.h>
#include <linux/mount.h>
#include <linux/mm.h>
#include "format.h"
#include "btree.h"
@@ -25,6 +27,8 @@
#include "ioctl.h"
#include "super.h"
#include "inode.h"
#include "trans.h"
#include "filerw.h"
/*
* Find all the inodes that have had keys of a given type modified since
@@ -290,6 +294,85 @@ static long scoutfs_ioc_data_version(struct file *file, unsigned long arg)
return 0;
}
/*
* The caller has a version of the data available in the given byte
* range in an external archive. As long as the data version still
* matches we free the blocks fully contained in the range and mark them
* offline. Attempts to use the blocks in the future will trigger
* recall from the archive.
*
* XXX permissions?
* XXX a lot of this could be generic file write prep
*/
static long scoutfs_ioc_release(struct file *file, unsigned long arg)
{
struct inode *inode = file_inode(file);
struct super_block *sb = inode->i_sb;
struct scoutfs_ioctl_release args;
loff_t start;
loff_t end_inc;
u64 iblock;
u64 end_block;
u64 len;
int ret;
if (copy_from_user(&args, (void __user *)arg, sizeof(args)))
return -EFAULT;
if (args.count == 0)
return 0;
if ((args.offset + args.count) < args.offset)
return -EINVAL;
start = round_up(args.offset, SCOUTFS_BLOCK_SIZE);
end_inc = round_down(args.offset + args.count, SCOUTFS_BLOCK_SIZE) - 1;
if (end_inc > start)
return 0;
iblock = start >> SCOUTFS_BLOCK_SHIFT;
end_block = end_inc >> SCOUTFS_BLOCK_SHIFT;
len = end_block - iblock + 1;
ret = mnt_want_write_file(file);
if (ret)
return ret;
mutex_lock(&inode->i_mutex);
if (!S_ISREG(inode->i_mode)) {
ret = -EINVAL;
goto out;
}
if (!(file->f_mode & FMODE_WRITE)) {
ret = -EINVAL;
goto out;
}
if (scoutfs_inode_get_data_version(inode) != args.data_version) {
ret = -ESTALE;
goto out;
}
inode_dio_wait(inode);
/* drop all clean and dirty cached blocks in the range */
truncate_inode_pages_range(&inode->i_data, start, end_inc);
ret = scoutfs_hold_trans(sb);
if (ret)
goto out;
ret = scoutfs_truncate_extent_items(sb, scoutfs_ino(inode),
iblock, len, true);
scoutfs_release_trans(sb);
out:
mutex_unlock(&inode->i_mutex);
mnt_drop_write_file(file);
return ret;
}
long scoutfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
switch (cmd) {
@@ -305,6 +388,8 @@ long scoutfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
return scoutfs_ioc_inodes_since(file, arg, SCOUTFS_EXTENT_KEY);
case SCOUTFS_IOC_DATA_VERSION:
return scoutfs_ioc_data_version(file, arg);
case SCOUTFS_IOC_RELEASE:
return scoutfs_ioc_release(file, arg);
}
return -ENOTTY;
+9
View File
@@ -58,4 +58,13 @@ struct scoutfs_ioctl_find_xattr {
#define SCOUTFS_IOC_DATA_VERSION _IOW(SCOUTFS_IOCTL_MAGIC, 6, u64)
struct scoutfs_ioctl_release {
__u64 offset;
__u64 count;
__u64 data_version;
} __packed;
#define SCOUTFS_IOC_RELEASE _IOW(SCOUTFS_IOCTL_MAGIC, 7, \
struct scoutfs_ioctl_release)
#endif