Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions src/bio_helper.c
Original file line number Diff line number Diff line change
Expand Up @@ -325,11 +325,15 @@ struct inode *page_get_inode(struct page *pg)
return pg->mapping->host;
}

int bio_needs_cow(struct bio *bio, struct inode *inode)
int bio_needs_cow(struct bio *bio, struct snap_device *dev)
{
bio_iter_t iter;
bio_iter_bvec_t bvec;

if (!test_bit(SD_FLAG_COW_RESIDENT, &dev->sd_flags)) {
return 1; // if the cow is non-resident, then we don't need to check if the bio is for the cow file.
}

#ifdef HAVE_ENUM_REQ_OPF
//#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0)
if (bio_op(bio) == REQ_OP_WRITE_ZEROES)
Expand All @@ -339,7 +343,7 @@ int bio_needs_cow(struct bio *bio, struct inode *inode)
// check the inode of each page return true if it does not match our cow
// file
bio_for_each_segment (bvec, bio, iter) {
if (page_get_inode(bio_iter_page(bio, iter)) != inode)
if (page_get_inode(bio_iter_page(bio, iter)) != dev->sd_cow_inode)
return 1;
}

Expand Down
2 changes: 1 addition & 1 deletion src/bio_helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ void dattobd_bio_op_clear_flag(struct bio *bio, unsigned int flag);

struct inode *page_get_inode(struct page *pg);

int bio_needs_cow(struct bio *bio, struct inode *inode);
int bio_needs_cow(struct bio *bio, struct snap_device *dev);

void bio_free_clone(struct bio *bio);

Expand Down
1 change: 1 addition & 0 deletions src/ioctl_handlers.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ int __verify_minor(unsigned int minor, int mode)
}

// check that the device is not busy if we care
smp_mb();
if (mode == 1 && atomic_read(&snap_devices[minor]->sd_refs)) {
LOG_ERROR(-EBUSY, "device specified is busy");
return -EBUSY;
Expand Down
5 changes: 4 additions & 1 deletion src/proc_seq_file.c
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ static int dattobd_proc_show(struct seq_file *m, void *v)
seq_printf(m, "\t\t\t\"minor\": %u,\n", dev->sd_minor);
seq_printf(m, "\t\t\t\"cow_file\": \"%s\",\n",
dev->sd_cow_path);
seq_printf(m, "\t\t\t\"full_cow_path\": \"%s\",\n",
dev->sd_cow_full_path);
seq_printf(m, "\t\t\t\"block_device\": \"%s\",\n",
dev->sd_bdev_path);
seq_printf(m, "\t\t\t\"max_cache\": %lu,\n",
Expand Down Expand Up @@ -133,7 +135,8 @@ static int dattobd_proc_show(struct seq_file *m, void *v)
if (error)
seq_printf(m, "\t\t\t\"error\": %d,\n", error);

seq_printf(m, "\t\t\t\"state\": %lu\n", dev->sd_state);
seq_printf(m, "\t\t\t\"state\": %lu,\n", dev->sd_state);
seq_printf(m, "\t\t\t\"resident\": %d\n", test_bit(SD_FLAG_COW_RESIDENT, &dev->sd_flags));
seq_printf(m, "\t\t}");
}

Expand Down
7 changes: 6 additions & 1 deletion src/snap_device.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,13 @@
#define ACTIVE 1
#define UNVERIFIED 2

// macros for defining the flags of a snap_device (bit offsets)
#define SD_FLAG_COW_RESIDENT 0 // the cow file exists on the backing device

struct snap_device {
unsigned int sd_minor; // minor number of the snapshot
unsigned long sd_state; // current state of the snapshot
unsigned long sd_flags; // flags
unsigned long sd_falloc_size; // space allocated to the cow file (in
// megabytes)
unsigned long sd_cache_size; // maximum cache size (in bytes)
Expand All @@ -31,7 +35,8 @@ struct snap_device {
struct block_device *sd_base_dev; // device being snapshot
char *sd_bdev_path; // base device file path
struct cow_manager *sd_cow; // cow manager
char *sd_cow_path; // cow file path
char *sd_cow_path; // deprecated: cow file path
char *sd_cow_full_path; // full cow file path
struct inode *sd_cow_inode; // cow file inode
make_request_fn
*sd_orig_mrf; // block device's original make request function
Expand Down
2 changes: 2 additions & 0 deletions src/snap_ops.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ static int __tracer_add_ref(struct snap_device *dev, int ref_cnt)
goto error;
}

smp_mb();
atomic_add(ref_cnt, &dev->sd_refs);
smp_mb();

error:
return ret;
Expand Down
13 changes: 4 additions & 9 deletions src/system_call_hooking.c
Original file line number Diff line number Diff line change
Expand Up @@ -90,16 +90,11 @@ int __handle_bdev_mount_nowrite(const struct vfsmount *mnt,
dev->sd_base_dev != mnt->mnt_sb->s_bdev)
continue;

// if we are unmounting the vfsmount we are using go to dormant
// state
if (mnt == dattobd_get_mnt(dev->sd_cow->filp)) {
LOG_DEBUG("block device umount detected for device %d",
i);
auto_transition_dormant(i);
LOG_DEBUG("block device umount detected for device %d", i);
auto_transition_dormant(i);

ret = 0;
goto out;
}
ret = 0;
goto out;
}
i = 0;
ret = -ENODEV;
Expand Down
59 changes: 48 additions & 11 deletions src/tracer.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
#include <linux/percpu-refcount.h>
#endif

#define FUSEBLK_MNT_NAME "fuseblk"
#define FUSEBLK_MNT_LEN 7

#if !defined(HAVE_BDEV_STACK_LIMITS) && !defined(HAVE_BLK_SET_DEFAULT_LIMITS)
//#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31)

Expand Down Expand Up @@ -159,7 +162,7 @@ static int snap_trace_bio(struct snap_device *dev, struct bio *bio)
unsigned int bytes, pages;

// if we don't need to cow this bio just call the real mrf normally
if (!bio_needs_cow(bio, dev->sd_cow_inode))
if (!bio_needs_cow(bio, dev))
return dattobd_call_mrf(dev->sd_orig_mrf,
dattobd_bio_get_queue(bio), bio);

Expand Down Expand Up @@ -255,6 +258,14 @@ static int inc_trace_bio(struct snap_device *dev, struct bio *bio)
bio_iter_t iter;
bio_iter_bvec_t bvec;

if (!test_bit(SD_FLAG_COW_RESIDENT, &dev->sd_flags)) {
// if the cow is non-resident, then we don't need to check if
// the bio is for the cow file.
ret = inc_make_sset(dev, bio_sector(bio),
bio_size(bio) / SECTOR_SIZE);
goto out;
}

#ifdef HAVE_ENUM_REQ_OPF
//#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0)
if (bio_op(bio) == REQ_OP_WRITE_ZEROES) {
Expand Down Expand Up @@ -361,7 +372,9 @@ static void minor_range_include(unsigned int minor)
static void __tracer_init(struct snap_device *dev)
{
LOG_DEBUG("initializing tracer");
smp_mb();
atomic_set(&dev->sd_fail_code, 0);
smp_mb();
bio_queue_init(&dev->sd_cow_bios);
bio_queue_init(&dev->sd_orig_bios);
sset_queue_init(&dev->sd_pending_ssets);
Expand Down Expand Up @@ -430,6 +443,7 @@ static int __tracer_setup_cow(struct snap_device *dev,
int ret;
uint64_t max_file_size;
char bdev_name[BDEVNAME_SIZE];
char fuseblk_name[] = FUSEBLK_MNT_NAME;

bdevname(bdev, bdev_name);

Expand Down Expand Up @@ -483,11 +497,23 @@ static int __tracer_setup_cow(struct snap_device *dev,
}
}

// verify that file is on block device
if (!file_is_on_bdev(dev->sd_cow->filp, bdev)) {
ret = -EINVAL;
LOG_ERROR(ret, "'%s' is not on '%s'", cow_path, bdev_name);
goto error;
if (file_is_on_bdev(dev->sd_cow->filp, bdev)) {
set_bit(SD_FLAG_COW_RESIDENT, &dev->sd_flags);
/* Cow file should not be on the same device as a fuse fs.
* This is because the typical setup for a fuse device involves
* calling mount() and /then/ setting its ioctl callbacks (open
* is of specific interest). Dormant- or unverified-to-active
* transitions cause a deadlock because open will be called in
* the mount syscall where the fuse device's callbacks won't
* have been set yet. We prevent this from happening by not
* allowing it in the first place. */
if (0 == strncmp(bdev->bd_super->s_type->name, fuseblk_name,
FUSEBLK_MNT_LEN)) {
ret = -EDEADLOCK;
LOG_ERROR(ret, "Cow file cannot be on same volume for"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feel free to ignore but as a user of this driver would it be helpful to know slightly more about why the cow file cannot be on the same block device without having to consult the code? The comment above is great. I'm just thinking about having a bit more information so that I could at least google an answer to why dattobd is telling me I cannot do what I want to do. We should probably consider adding enough information in our log messages so that steps to correct are at least Googleable. As I said, feel free to ignore, I'm just thinking about the experience of trying to figure out why something, that seems on the surface to be sane, does not work.

" fuse filesystems");
return ret;
}
}

// find the cow file's inode number
Expand Down Expand Up @@ -627,6 +653,12 @@ static void __tracer_destroy_cow_path(struct snap_device *dev)
kfree(dev->sd_cow_path);
dev->sd_cow_path = NULL;
}

if (dev->sd_cow_full_path) {
LOG_DEBUG("freeing full cow path");
kfree(dev->sd_cow_full_path);
dev->sd_cow_full_path = NULL;
}
}

static int __tracer_setup_cow_path(struct snap_device *dev,
Expand All @@ -635,6 +667,11 @@ static int __tracer_setup_cow_path(struct snap_device *dev,
int ret;

// get the pathname of the cow file (relative to the mountpoint)
LOG_DEBUG("getting absolute pathname of cow file");
ret = file_get_absolute_pathname(cow_file, &dev->sd_cow_full_path, NULL);
if (ret)
goto error;

LOG_DEBUG("getting relative pathname of cow file");
ret = dentry_get_relative_pathname(dattobd_get_dentry(cow_file),
&dev->sd_cow_path, NULL);
Expand Down Expand Up @@ -1579,16 +1616,20 @@ void __tracer_dormant_to_active(struct snap_device *dev,
const char __user *user_mount_path)
{
int ret;
char *resident_cow_path;
char *cow_path;

// generate the full pathname
ret = user_mount_pathname_concat(user_mount_path, dev->sd_cow_path,
&cow_path);
&resident_cow_path);
if (ret)
goto error;

cow_path = test_bit(SD_FLAG_COW_RESIDENT, &dev->sd_flags) ? resident_cow_path : dev->sd_cow_full_path;

// setup the cow manager
ret = __tracer_setup_cow_reopen(dev, dev->sd_base_dev, cow_path);
kfree(resident_cow_path);
if (ret)
goto error;

Expand All @@ -1608,13 +1649,9 @@ void __tracer_dormant_to_active(struct snap_device *dev,
set_bit(ACTIVE, &dev->sd_state);
clear_bit(UNVERIFIED, &dev->sd_state);

kfree(cow_path);

return;

error:
LOG_ERROR(ret, "error transitioning tracer to active state");
if (cow_path)
kfree(cow_path);
tracer_set_fail_state(dev, ret);
}
4 changes: 4 additions & 0 deletions src/tracing_params.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,19 @@ int tp_alloc(struct snap_device *dev, struct bio *bio,
tp->orig_bio = bio;
tp->bio_sects.head = NULL;
tp->bio_sects.tail = NULL;
smp_mb();
atomic_set(&tp->refs, 1);
smp_mb();

*tp_out = tp;
return 0;
}

void tp_get(struct tracing_params *tp)
{
smp_mb();
atomic_inc(&tp->refs);
smp_mb();
}

void tp_put(struct tracing_params *tp)
Expand Down
9 changes: 5 additions & 4 deletions tests/test_setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,11 @@ def test_setup_cow_file_path_is_dir(self):
self.assertFalse(os.path.exists(self.snap_device))
self.assertIsNone(dattobd.info(self.minor))

def test_setup_cow_file_on_wrong_device(self):
self.assertEqual(dattobd.setup(self.minor, self.device, "/tmp/{}".format(self.cow_file)), errno.EINVAL)
self.assertFalse(os.path.exists(self.snap_device))
self.assertIsNone(dattobd.info(self.minor))
def test_setup_cow_file_on_other_device(self):
self.assertEqual(dattobd.setup(self.minor, self.device, "/tmp/{}".format(self.cow_file)), 0)
self.addCleanup(dattobd.destroy, self.minor)
self.assertTrue(os.path.exists(self.snap_device))
self.assertIsNotNone(dattobd.info(self.minor))

def test_setup_unmounted_volume(self):
util.unmount(self.mount)
Expand Down