diff options
Diffstat (limited to 'drivers/block/loop/loop_file_fmt_qcow_main.c')
-rw-r--r-- | drivers/block/loop/loop_file_fmt_qcow_main.c | 295 |
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. |