|
@@ -75,6 +75,7 @@
|
|
|
#include <linux/kthread.h>
|
|
|
#include <linux/splice.h>
|
|
|
#include <linux/sysfs.h>
|
|
|
+#include <linux/falloc.h>
|
|
|
|
|
|
#include <asm/uaccess.h>
|
|
|
|
|
@@ -484,6 +485,29 @@ static int do_bio_filebacked(struct loop_device *lo, struct bio *bio)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /*
|
|
|
+ * We use punch hole to reclaim the free space used by the
|
|
|
+ * image a.k.a. discard. However we do support discard if
|
|
|
+ * encryption is enabled, because it may give an attacker
|
|
|
+ * useful information.
|
|
|
+ */
|
|
|
+ if (bio->bi_rw & REQ_DISCARD) {
|
|
|
+ struct file *file = lo->lo_backing_file;
|
|
|
+ int mode = FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE;
|
|
|
+
|
|
|
+ if ((!file->f_op->fallocate) ||
|
|
|
+ lo->lo_encrypt_key_size) {
|
|
|
+ ret = -EOPNOTSUPP;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+ ret = file->f_op->fallocate(file, mode, pos,
|
|
|
+ bio->bi_size);
|
|
|
+ if (unlikely(ret && ret != -EINVAL &&
|
|
|
+ ret != -EOPNOTSUPP))
|
|
|
+ ret = -EIO;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
ret = lo_send(lo, bio, pos);
|
|
|
|
|
|
if ((bio->bi_rw & REQ_FUA) && !ret) {
|
|
@@ -814,6 +838,35 @@ static void loop_sysfs_exit(struct loop_device *lo)
|
|
|
&loop_attribute_group);
|
|
|
}
|
|
|
|
|
|
+static void loop_config_discard(struct loop_device *lo)
|
|
|
+{
|
|
|
+ struct file *file = lo->lo_backing_file;
|
|
|
+ struct inode *inode = file->f_mapping->host;
|
|
|
+ struct request_queue *q = lo->lo_queue;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * We use punch hole to reclaim the free space used by the
|
|
|
+ * image a.k.a. discard. However we do support discard if
|
|
|
+ * encryption is enabled, because it may give an attacker
|
|
|
+ * useful information.
|
|
|
+ */
|
|
|
+ if ((!file->f_op->fallocate) ||
|
|
|
+ lo->lo_encrypt_key_size) {
|
|
|
+ q->limits.discard_granularity = 0;
|
|
|
+ q->limits.discard_alignment = 0;
|
|
|
+ q->limits.max_discard_sectors = 0;
|
|
|
+ q->limits.discard_zeroes_data = 0;
|
|
|
+ queue_flag_clear_unlocked(QUEUE_FLAG_DISCARD, q);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ q->limits.discard_granularity = inode->i_sb->s_blocksize;
|
|
|
+ q->limits.discard_alignment = inode->i_sb->s_blocksize;
|
|
|
+ q->limits.max_discard_sectors = UINT_MAX >> 9;
|
|
|
+ q->limits.discard_zeroes_data = 1;
|
|
|
+ queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, q);
|
|
|
+}
|
|
|
+
|
|
|
static int loop_set_fd(struct loop_device *lo, fmode_t mode,
|
|
|
struct block_device *bdev, unsigned int arg)
|
|
|
{
|
|
@@ -1090,6 +1143,7 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info)
|
|
|
if (figure_loop_size(lo))
|
|
|
return -EFBIG;
|
|
|
}
|
|
|
+ loop_config_discard(lo);
|
|
|
|
|
|
memcpy(lo->lo_file_name, info->lo_file_name, LO_NAME_SIZE);
|
|
|
memcpy(lo->lo_crypt_name, info->lo_crypt_name, LO_NAME_SIZE);
|