summaryrefslogtreecommitdiffstats
path: root/drivers/block/loop/loop_file_fmt_qcow_main.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/block/loop/loop_file_fmt_qcow_main.c')
-rw-r--r--drivers/block/loop/loop_file_fmt_qcow_main.c295
1 files changed, 228 insertions, 67 deletions
diff --git a/drivers/block/loop/loop_file_fmt_qcow_main.c b/drivers/block/loop/loop_file_fmt_qcow_main.c
index 3b40d90d0a1e..70436c8fc076 100644
--- a/drivers/block/loop/loop_file_fmt_qcow_main.c
+++ b/drivers/block/loop/loop_file_fmt_qcow_main.c
@@ -16,6 +16,7 @@
#include <linux/blkdev.h>
#include <linux/bio.h>
#include <linux/bvec.h>
+#include <linux/mutex.h>
#include <linux/uio.h>
#include <linux/string.h>
#include <linux/vmalloc.h>
@@ -100,70 +101,6 @@ static int __qcow_file_fmt_header_read(struct loop_file_fmt *lo_fmt,
return ret;
}
-#ifdef CONFIG_DEBUG_DRIVER
-static void __qcow_file_fmt_header_print(
- struct loop_file_fmt_qcow_header *header)
-{
- printk(KERN_DEBUG "loop_file_fmt_qcow: "
- "header.magic=%d",
- header->magic);
- printk(KERN_DEBUG "loop_file_fmt_qcow: "
- "header.version=%d",
- header->version);
- printk(KERN_DEBUG "loop_file_fmt_qcow: "
- "header.backing_file_offset=%lld",
- header->backing_file_offset);
- printk(KERN_DEBUG "loop_file_fmt_qcow: "
- "header.backing_file_size=%d",
- header->backing_file_size);
- printk(KERN_DEBUG "loop_file_fmt_qcow: "
- "header.cluster_bits=%d",
- header->cluster_bits);
- printk(KERN_DEBUG "loop_file_fmt_qcow: "
- "header.size=%lld",
- header->size);
- printk(KERN_DEBUG "loop_file_fmt_qcow: "
- "header.crypt_method=%d",
- header->crypt_method);
- printk(KERN_DEBUG "loop_file_fmt_qcow: "
- "header.l1_size=%d",
- header->l1_size);
- printk(KERN_DEBUG "loop_file_fmt_qcow: "
- "header.l1_table_offset=%lld",
- header->l1_table_offset);
- printk(KERN_DEBUG "loop_file_fmt_qcow: "
- "header.refcount_table_offset=%lld",
- header->refcount_table_offset);
- printk(KERN_DEBUG "loop_file_fmt_qcow: "
- "header.refcount_table_clusters=%d",
- header->refcount_table_clusters);
- printk(KERN_DEBUG "loop_file_fmt_qcow: "
- "header.nb_snapshots=%d",
- header->nb_snapshots);
- printk(KERN_DEBUG "loop_file_fmt_qcow: "
- "header.snapshots_offset=%lld",
- header->snapshots_offset);
-
- if (header->version == 3) {
- printk(KERN_DEBUG "loop_file_fmt_qcow: "
- "header.incompatible_features=%lld",
- header->incompatible_features);
- printk(KERN_DEBUG "loop_file_fmt_qcow: "
- "header.compatible_features=%lld",
- header->compatible_features);
- printk(KERN_DEBUG "loop_file_fmt_qcow: "
- "header.autoclear_features=%lld",
- header->autoclear_features);
- printk(KERN_DEBUG "loop_file_fmt_qcow: "
- "header.refcount_order=%d",
- header->refcount_order);
- printk(KERN_DEBUG "loop_file_fmt_qcow: "
- "header.header_length=%d",
- header->header_length);
- }
-}
-#endif
-
static int __qcow_file_fmt_validate_table(struct loop_file_fmt *lo_fmt,
u64 offset, u64 entries, size_t entry_len, s64 max_size_bytes,
const char *table_name)
@@ -242,6 +179,217 @@ static void __qcow_file_fmt_compression_exit(struct loop_file_fmt *lo_fmt)
kfree(qcow_data->strm);
}
+#ifdef CONFIG_DEBUG_FS
+static void __qcow_file_fmt_header_to_buf(struct loop_file_fmt* lo_fmt,
+ const struct loop_file_fmt_qcow_header *header)
+{
+ struct loop_file_fmt_qcow_data *qcow_data = lo_fmt->private_data;
+ char *header_buf = qcow_data->dbgfs_file_qcow_header_buf;
+ ssize_t len = 0;
+
+ len += sprintf(header_buf + len, "magic: %d\n",
+ header->magic);
+ len += sprintf(header_buf + len, "version: %d\n",
+ header->version);
+ len += sprintf(header_buf + len, "backing_file_offset: %lld\n",
+ header->backing_file_offset);
+ len += sprintf(header_buf + len, "backing_file_size: %d\n",
+ header->backing_file_size);
+ len += sprintf(header_buf + len, "cluster_bits: %d\n",
+ header->cluster_bits);
+ len += sprintf(header_buf + len, "size: %lld\n",
+ header->size);
+ len += sprintf(header_buf + len, "crypt_method: %d\n",
+ header->crypt_method);
+ len += sprintf(header_buf + len, "l1_size: %d\n",
+ header->l1_size);
+ len += sprintf(header_buf + len, "l1_table_offset: %lld\n",
+ header->l1_table_offset);
+ len += sprintf(header_buf + len, "refcount_table_offset: %lld\n",
+ header->refcount_table_offset);
+ len += sprintf(header_buf + len, "refcount_table_clusters: %d\n",
+ header->refcount_table_clusters);
+ len += sprintf(header_buf + len, "nb_snapshots: %d\n",
+ header->nb_snapshots);
+ len += sprintf(header_buf + len, "snapshots_offset: %lld\n",
+ header->snapshots_offset);
+
+ if (header->version == 3) {
+ len += sprintf(header_buf + len,
+ "incompatible_features: %lld\n",
+ header->incompatible_features);
+ len += sprintf(header_buf + len,
+ "compatible_features: %lld\n",
+ header->compatible_features);
+ len += sprintf(header_buf + len,
+ "autoclear_features: %lld\n",
+ header->autoclear_features);
+ len += sprintf(header_buf + len,
+ "refcount_order: %d\n",
+ header->refcount_order);
+ len += sprintf(header_buf + len,
+ "header_length: %d\n",
+ header->header_length);
+ }
+
+ ASSERT(len < QCOW_HEADER_BUF_LEN);
+}
+
+static ssize_t __qcow_file_fmt_dbgfs_hdr_read(struct file *file,
+ char __user *buf, size_t size, loff_t *ppos)
+{
+ struct loop_file_fmt *lo_fmt = file->private_data;
+ struct loop_file_fmt_qcow_data *qcow_data = lo_fmt->private_data;
+ char *header_buf = qcow_data->dbgfs_file_qcow_header_buf;
+
+ return simple_read_from_buffer(buf, size, ppos, header_buf,
+ strlen(header_buf));
+}
+
+static const struct file_operations qcow_file_fmt_dbgfs_hdr_fops = {
+ .open = simple_open,
+ .read = __qcow_file_fmt_dbgfs_hdr_read
+};
+
+static ssize_t __qcow_file_fmt_dbgfs_ofs_read(struct file *file,
+ char __user *buf, size_t size, loff_t *ppos)
+{
+ struct loop_file_fmt *lo_fmt = file->private_data;
+ struct loop_file_fmt_qcow_data *qcow_data = lo_fmt->private_data;
+ unsigned int cur_bytes = 1;
+ u64 offset = 0;
+ u64 cluster_offset = 0;
+ s64 offset_in_cluster = 0;
+ ssize_t len = 0;
+ int ret = 0;
+
+ /* read the share debugfs offset */
+ ret = mutex_lock_interruptible(&qcow_data->dbgfs_qcow_offset_mutex);
+ if (ret)
+ return ret;
+
+ offset = qcow_data->dbgfs_qcow_offset;
+ mutex_unlock(&qcow_data->dbgfs_qcow_offset_mutex);
+
+ /* calculate and print the cluster offset */
+ ret = loop_file_fmt_qcow_cluster_get_offset(lo_fmt,
+ offset, &cur_bytes, &cluster_offset);
+ if (ret < 0)
+ return -EINVAL;
+
+ offset_in_cluster = loop_file_fmt_qcow_offset_into_cluster(qcow_data,
+ offset);
+
+ len = sprintf(qcow_data->dbgfs_file_qcow_cluster_buf,
+ "offset: %lld\ncluster_offset: %lld\noffset_in_cluster: %lld\n",
+ offset, cluster_offset, offset_in_cluster);
+
+ ASSERT(len < QCOW_CLUSTER_BUF_LEN);
+
+ return simple_read_from_buffer(buf, size, ppos,
+ qcow_data->dbgfs_file_qcow_cluster_buf, len);
+}
+
+static ssize_t __qcow_file_fmt_dbgfs_ofs_write(struct file *file,
+ const char __user *buf, size_t size, loff_t *ppos)
+{
+ struct loop_file_fmt *lo_fmt = file->private_data;
+ struct loop_file_fmt_qcow_data *qcow_data = lo_fmt->private_data;
+ ssize_t len = 0;
+ int ret = 0;
+
+ if (*ppos > QCOW_OFFSET_BUF_LEN || size > QCOW_OFFSET_BUF_LEN)
+ return -EINVAL;
+
+ len = simple_write_to_buffer(qcow_data->dbgfs_file_qcow_offset_buf,
+ QCOW_OFFSET_BUF_LEN, ppos, buf, size);
+ if (len < 0)
+ return len;
+
+ qcow_data->dbgfs_file_qcow_offset_buf[len] = '\0';
+
+ ret = mutex_lock_interruptible(&qcow_data->dbgfs_qcow_offset_mutex);
+ if (ret)
+ return ret;
+
+ ret = kstrtou64(qcow_data->dbgfs_file_qcow_offset_buf, 10,
+ &qcow_data->dbgfs_qcow_offset);
+ if (ret < 0)
+ goto out;
+
+ ret = len;
+out:
+ mutex_unlock(&qcow_data->dbgfs_qcow_offset_mutex);
+ return ret;
+}
+
+static const struct file_operations qcow_file_fmt_dbgfs_ofs_fops = {
+ .open = simple_open,
+ .read = __qcow_file_fmt_dbgfs_ofs_read,
+ .write = __qcow_file_fmt_dbgfs_ofs_write
+};
+
+static int __qcow_file_fmt_dbgfs_init(struct loop_file_fmt *lo_fmt)
+{
+ struct loop_file_fmt_qcow_data *qcow_data = lo_fmt->private_data;
+ struct loop_device *lo = loop_file_fmt_get_lo(lo_fmt);
+ int ret = 0;
+
+ qcow_data->dbgfs_dir = debugfs_create_dir("QCOW", lo->lo_dbgfs_dir);
+ if (IS_ERR_OR_NULL(qcow_data->dbgfs_dir)) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ qcow_data->dbgfs_file_qcow_header = debugfs_create_file("header",
+ S_IRUGO, qcow_data->dbgfs_dir, lo_fmt,
+ &qcow_file_fmt_dbgfs_hdr_fops);
+ if (IS_ERR_OR_NULL(qcow_data->dbgfs_file_qcow_header)) {
+ ret = -ENODEV;
+ goto out_free_dbgfs_dir;
+ }
+
+ qcow_data->dbgfs_file_qcow_offset = debugfs_create_file("offset",
+ S_IRUGO | S_IWUSR, qcow_data->dbgfs_dir, lo_fmt,
+ &qcow_file_fmt_dbgfs_ofs_fops);
+ if (IS_ERR_OR_NULL(qcow_data->dbgfs_file_qcow_offset)) {
+ qcow_data->dbgfs_file_qcow_offset = NULL;
+ ret = -ENODEV;
+ goto out_free_dbgfs_hdr;
+ }
+
+ qcow_data->dbgfs_qcow_offset = 0;
+ mutex_init(&qcow_data->dbgfs_qcow_offset_mutex);
+
+ return ret;
+
+out_free_dbgfs_hdr:
+ debugfs_remove(qcow_data->dbgfs_file_qcow_header);
+ qcow_data->dbgfs_file_qcow_header = NULL;
+out_free_dbgfs_dir:
+ debugfs_remove(qcow_data->dbgfs_dir);
+ qcow_data->dbgfs_dir = NULL;
+out:
+ return ret;
+}
+
+static void __qcow_file_fmt_dbgfs_exit(struct loop_file_fmt *lo_fmt)
+{
+ struct loop_file_fmt_qcow_data *qcow_data = lo_fmt->private_data;
+
+ if (qcow_data->dbgfs_file_qcow_offset)
+ debugfs_remove(qcow_data->dbgfs_file_qcow_offset);
+
+ mutex_destroy(&qcow_data->dbgfs_qcow_offset_mutex);
+
+ if (qcow_data->dbgfs_file_qcow_header)
+ debugfs_remove(qcow_data->dbgfs_file_qcow_header);
+
+ if (qcow_data->dbgfs_dir)
+ debugfs_remove(qcow_data->dbgfs_dir);
+}
+#endif
+
static int qcow_file_fmt_init(struct loop_file_fmt *lo_fmt)
{
struct loop_file_fmt_qcow_data *qcow_data;
@@ -266,8 +414,10 @@ static int qcow_file_fmt_init(struct loop_file_fmt *lo_fmt)
if (ret)
goto free_qcow_data;
-#ifdef CONFIG_DEBUG_DRIVER
- __qcow_file_fmt_header_print(&header);
+ /* save information of the header fields in human readable format in
+ * a file buffer to access it with debugfs */
+#ifdef CONFIG_DEBUG_FS
+ __qcow_file_fmt_header_to_buf(lo_fmt, &header);
#endif
qcow_data->qcow_version = header.version;
@@ -493,6 +643,13 @@ static int qcow_file_fmt_init(struct loop_file_fmt *lo_fmt)
if (ret < 0)
goto free_l2_cache;
+ /* initialize debugfs entries */
+#ifdef CONFIG_DEBUG_FS
+ ret = __qcow_file_fmt_dbgfs_init(lo_fmt);
+ if (ret < 0)
+ goto free_l2_cache;
+#endif
+
return ret;
free_l2_cache:
@@ -509,6 +666,10 @@ static void qcow_file_fmt_exit(struct loop_file_fmt *lo_fmt)
{
struct loop_file_fmt_qcow_data *qcow_data = lo_fmt->private_data;
+#ifdef CONFIG_DEBUG_FS
+ __qcow_file_fmt_dbgfs_exit(lo_fmt);
+#endif
+
__qcow_file_fmt_compression_exit(lo_fmt);
if (qcow_data->l1_table) {
@@ -752,7 +913,7 @@ static loff_t qcow_file_fmt_sector_size(struct loop_file_fmt *lo_fmt)
if (lo->lo_sizelimit > 0 && lo->lo_sizelimit < loopsize)
loopsize = lo->lo_sizelimit;
- printk(KERN_INFO "loop_file_fmt_qcow: sector_size=%lld", loopsize);
+
/*
* Unfortunately, if we want to do I/O on the device,
* the number of 512-byte sectors has to fit into a sector_t.