[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [PATCH 5 of 6] qcow plugin for dm-userspace userspace tool



Signed-off-by: Ryan Grimm <grimm@xxxxxxxxxx>
Signed-off-by: Dan Smith <danms@xxxxxxxxxx>

# HG changeset patch
# User Ryan Grimm <grimm@xxxxxxxxxx>
# Date 1156536096 18000
# Node ID be4574d288030b64d4623dd33505d0990185a6b9
# Parent  a3656acd770b4f21ad54fa961032ff39562058eb
qcow plugin for dm-userspace userspace tool

diff -r a3656acd770b -r be4574d28803 tools/cowd/configure.in
--- a/tools/cowd/configure.in   Fri Aug 25 15:01:35 2006 -0500
+++ b/tools/cowd/configure.in   Fri Aug 25 15:01:36 2006 -0500
@@ -95,11 +95,13 @@ AC_SUBST(GLOBAL_CFLAGS)
 
 AC_CONFIG_FILES([Makefile
                 plugins/Makefile
+                plugins/qcow/Makefile
                 plugins/dscow/Makefile])
 
 # This just makes it easier to run cowd from the source directory
 # for testing
 mkdir -p lib
+ln -sf ../plugins/qcow/.libs/libcowd_qcow.so.0 lib/libcowd_qcow.so
 ln -sf ../plugins/dscow/.libs/libcowd_dscow.so.0 lib/libcowd_dscow.so
 ln -sf ../plugins/dscow/.libs/libcowd_dscow.la lib/libcowd_dscow.la
 
diff -r a3656acd770b -r be4574d28803 tools/cowd/plugins/Makefile.am
--- a/tools/cowd/plugins/Makefile.am    Fri Aug 25 15:01:35 2006 -0500
+++ b/tools/cowd/plugins/Makefile.am    Fri Aug 25 15:01:36 2006 -0500
@@ -1,1 +1,1 @@ SUBDIRS = dscow
-SUBDIRS = dscow
+SUBDIRS = dscow qcow
diff -r a3656acd770b -r be4574d28803 tools/cowd/plugins/qcow/Makefile.am
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/cowd/plugins/qcow/Makefile.am       Fri Aug 25 15:01:36 2006 -0500
@@ -0,0 +1,3 @@
+lib_LTLIBRARIES = libcowd_qcow.la
+libcowd_qcow_la_CFLAGS = -I../.. -I../../../../module @GLOBAL_CFLAGS@
+libcowd_qcow_la_SOURCES = qcow_plugin.c qcow_ops.c qcow.h qcow_ops.h
diff -r a3656acd770b -r be4574d28803 tools/cowd/plugins/qcow/qcow.h
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/cowd/plugins/qcow/qcow.h    Fri Aug 25 15:01:36 2006 -0500
@@ -0,0 +1,167 @@
+#ifndef __QCOW_H
+#define __QCOW_H
+
+#include <sys/types.h>
+#include <stdint.h>
+
+#define NAME "qcow: "
+#define L2_CACHE_SIZE 16
+
+#define QCOW_MAGIC (('Q' << 24) | ('F' << 16) | ('I' << 8) | 0xFB)
+
+typedef uint64_t qcow_te ;
+
+struct qcow_header {
+       uint32_t magic;
+       uint32_t version;
+       uint64_t backing_filename_offset;
+       uint32_t backing_filename_size;
+       uint32_t mtime;
+       uint64_t size;
+       uint8_t  cluster_bits;
+       uint8_t  l2_bits;
+       uint32_t crypto_method;
+       uint64_t l1_table_offset;
+};
+
+struct l1_entry {
+       int       dirty;
+       uint64_t  loc_on_disk;
+       qcow_te  *table;
+};
+
+struct l2_cache {
+       uint64_t         l1_index;
+       struct l1_entry *l2;
+       uint32_t         hits;
+};
+
+struct qcow {
+       struct qcow_header   header;
+       struct l1_entry     *table;
+       int                  l1_dirty;
+
+       char                *filename;
+
+       struct l2_cache     *l2_cache[L2_CACHE_SIZE];
+       int                  l2_cache_counter;
+
+       uint64_t             next_avail_block;
+
+       /* This is the offset we use for the file.  The problem is,
+        the kernel module expects to remap whole aligned blocks.
+        Since we can interleave L2 blocks with differently-sized */
+       uint64_t             offset;
+
+       int                  fd;
+
+};
+
+static inline uint64_t qcow_num_l1(struct qcow_header *h)
+{
+       uint64_t nonl1 = (1 << h->cluster_bits) * (1 << h->l2_bits);
+
+       if (h->size % nonl1)
+               return (h->size / nonl1) + 1;
+       else 
+               return (h->size / nonl1);
+}
+
+static inline uint64_t qcow_num_l2(struct qcow_header *h)
+{
+       return 1 << h->l2_bits;
+}
+
+static inline uint64_t qcow_block_size(struct qcow_header *h)
+{
+       return 1 << h->cluster_bits;
+}
+
+static inline uint64_t qcow_cmask(struct qcow_header *h)
+{
+       return (1 << h->cluster_bits) - 1;
+}
+
+static inline uint64_t qcow_l2mask(struct qcow_header *h)
+{
+       return ((1 << h->l2_bits) - 1) << h->cluster_bits;
+}
+
+static inline uint64_t qcow_l1mask(struct qcow_header *h)
+{
+       return ~(qcow_cmask(h) | qcow_l2mask(h));
+}
+
+/* These are i386, little-endian.  
+ *   Clearly this needs to be generalized.
+ */
+static inline uint64_t ntohll(uint64_t value)
+{
+       uint32_t a, b;
+
+       a = value >> 32;
+       b = value & 0xFFFFFFFF;
+       
+       return (((uint64_t)ntohl(b)) << 32) | ntohl(a);
+}
+
+static inline uint64_t htonll(uint64_t value)
+{
+       uint32_t a, b;
+
+       a = value >> 32;
+       b = value & 0xFFFFFFFF;
+       
+       return (((uint64_t)htonl(b)) << 32) | htonl(a);
+}
+
+static inline uint64_t qcow_get_l1_entry(struct qcow *qcow, uint64_t sector)
+{
+       uint64_t entry;
+
+       entry = sector & qcow_l1mask(&qcow->header);
+       entry = entry >> 
+               (qcow->header.l2_bits + qcow->header.cluster_bits);
+       
+//     printf("L1 entry: %llx %llx\n",
+//            sector & qcow_l1mask(&qcow->header),
+//            entry);
+
+       return entry;
+}
+
+static inline uint64_t qcow_get_l2_entry(struct qcow *qcow, uint64_t sector)
+{
+       return (sector & qcow_l2mask(&qcow->header)) >>
+               (qcow->header.cluster_bits);
+}
+
+static inline uint64_t qcow_make_te(struct qcow *qcow,
+                                    uint64_t l1_entry,
+                                    uint64_t l2_entry,
+                                    uint64_t index)
+{
+       uint64_t te = 0;
+       
+       te |= (l1_entry << (qcow->header.l2_bits + qcow->header.cluster_bits));
+       te |= (l2_entry << (qcow->header.cluster_bits));
+       te |= (index & qcow_cmask(&qcow->header));
+       
+       return te;
+}
+
+static inline uint64_t qcow_calc_offset(struct qcow *qcow,
+                                        uint64_t sector)
+{
+       return sector % qcow_block_size(&qcow->header);
+}
+
+static inline uint64_t qcow_align_to_block(struct qcow *qcow,
+                                           uint64_t sector)
+{
+       return sector / qcow_block_size(&qcow->header);
+}
+
+
+
+#endif
diff -r a3656acd770b -r be4574d28803 tools/cowd/plugins/qcow/qcow_ops.c
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/cowd/plugins/qcow/qcow_ops.c        Fri Aug 25 15:01:36 2006 -0500
@@ -0,0 +1,603 @@
+/*
+ * Copyright (C) International Business Machines Corp., 2006
+ * Author: Dan Smith <danms@xxxxxxxxxx>
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License. See the file COPYING in the main directory
+ * of this archive for more details.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <netinet/in.h> /* for endian ops */
+
+#include "qcow.h"
+#include "qcow_ops.h"
+
+#define OPS_DEBUG 0
+
+#define LOG_ABOVE (128 << 20)
+
+/*
+ * Update our pointer to the end of the highest-block, if appropriate.
+ * This is used to allocate new blocks and tables, because it's the
+ * end of the last chunk of real data.
+ */
+static int qcow_seen_block(struct qcow *qcow,
+                                 qcow_te sector,
+                                 qcow_te block_size)
+{
+       uint64_t end;
+
+       end = sector + block_size;
+
+       if (end > qcow->header.size) {
+               fprintf(stderr,
+                       "*** ERROR: Saw block %llu beyond end of %llu\n",
+                       end, qcow->header.size);
+               return -1;
+       }
+
+       if (qcow->next_avail_block < end) {
+               qcow->next_avail_block = end;
+               return 1;
+       } else {
+               return 0;
+       }
+}
+
+void qcow_init_qcow(struct qcow *qcow)
+{
+       qcow->table            =  NULL;
+       qcow->filename         =  NULL;
+       qcow->l1_dirty         =  0;
+       qcow->next_avail_block =  0;
+}
+
+/*
+ * Read in the qcow header from @fd, storing it in @header
+ * If @w!=0, then write instead. (Although not yet :)
+ */
+int qcow_rw_header(int fd, struct qcow_header *header, int w)
+{
+       int ret;
+       loff_t offset = 0;
+
+       if (dio_lseek(fd, offset, SEEK_SET) != offset) {
+               fprintf(stderr, "Offset was %lli instead of 0\n", offset);
+               perror("dio_lseek");
+               return 0;
+       }
+
+       if (w)
+               ret = dio_write(fd, header, sizeof(*header));
+       else
+               ret = dio_read(fd, header, sizeof(*header));
+
+       if (ret == 0) {
+               fprintf(stderr, "Read 0 bytes for header (%i)!\n",
+                       sizeof(*header));
+               return 0;
+       } else if (ret < 0) {
+               perror("qcow-header");
+               return 0;
+       }
+
+       /* Convert endianess */
+       header->magic                   = ntohl(header->magic);
+       header->version                 = ntohl(header->version);
+       header->backing_filename_offset =
+               ntohll(header->backing_filename_offset);
+       header->backing_filename_size   = ntohl(header->backing_filename_size);
+       header->mtime                   = ntohl(header->mtime);
+       header->size                    = ntohll(header->size);
+       header->crypto_method           = ntohl(header->crypto_method);
+       header->l1_table_offset         = ntohll(header->l1_table_offset);
+
+       return 1;
+}
+
+/*
+ * Read the backing filename information
+ */
+int qcow_read_backing_info(struct qcow *qcow)
+{
+       int ret;
+
+       qcow->filename = (char *)malloc(qcow->header.backing_filename_size+1);
+
+       ret = dio_lseek(qcow->fd, 
+                       qcow->header.backing_filename_offset, 
+                       SEEK_SET);
+       if (ret != qcow->header.backing_filename_offset) {
+               fprintf(stderr, "lseek to %llu\n",
+                       qcow->header.backing_filename_offset);
+               perror("qcow-lseek");
+               return 0;
+       }
+
+       ret = dio_read(qcow->fd, qcow->filename,
+                      qcow->header.backing_filename_size);
+       if (ret != qcow->header.backing_filename_size) {
+               fprintf(stderr, "qcow-read: EOF while reading filename\n");
+               return 0;
+       }
+       qcow->filename[qcow->header.backing_filename_size] = '\0';
+
+       return 1;
+}
+
+/*
+ * Allocate memory for the L1 table
+ */
+int qcow_init_from_header(struct qcow *qcow)
+{
+       qcow->table = (struct l1_entry *)calloc(qcow_num_l1(&qcow->header),
+                                               sizeof(struct l1_entry));
+
+       if (qcow->table == NULL) {
+               fprintf(stderr,
+                       NAME "*** failed to calloc %llu x %i for table\n",
+                       qcow_num_l1(&qcow->header), sizeof(qcow_te*));
+               return 0;
+       }
+
+       return 1;
+}
+
+/*
+ * Write @table to @fd:@pos, @length entries
+ */
+static int qcow_write_table(int fd, uint64_t pos,
+                           uint64_t length, qcow_te *table)
+{
+       qcow_te   *disk_table;
+       int64_t   i;
+       
+       disk_table = (qcow_te *)calloc(length,
+                                      sizeof(qcow_te));
+       if (!disk_table)
+               goto bad;
+
+       for (i=0; i < length; i++)
+               disk_table[i] = htonll(table[i]);
+
+       if (dio_lseek(fd, pos, SEEK_SET) != pos) {
+               perror("write_table lseek");
+               goto bad;
+       }
+
+       if (OPS_DEBUG)
+               fprintf(stderr, "Writing %llu bytes @ %llu\n",
+                       length * sizeof(qcow_te), pos);
+
+       i = dio_write(fd, disk_table, length * sizeof(qcow_te));
+
+       if (i != (length * sizeof(qcow_te))) {
+               fprintf(stderr,
+                       "Short write: %lli/%llu\n",
+                       i,
+                       length * sizeof(qcow_te));
+
+               goto bad;
+       } else if (i < 0) {
+               perror("write");
+               goto bad;
+       }
+
+       return 1;
+
+ bad:
+       return 0;
+}
+
+/*
+ * Read @table from @fd:@pos, @length entries
+ */
+static int qcow_read_table(int fd, uint64_t pos,
+                           uint64_t length, qcow_te *table)
+{
+       qcow_te *disk_table;
+       int64_t  i;
+
+       disk_table = (qcow_te *)calloc(length,
+                                      sizeof(qcow_te));
+       if (!disk_table)
+               goto bad;
+
+       if (dio_lseek(fd, pos, SEEK_SET) != pos) {
+               perror("lseek");
+               goto bad;
+       }
+
+       i = dio_read(fd, disk_table, length * sizeof(qcow_te));
+
+       if (i != (length * sizeof(qcow_te))) {
+               fprintf(stderr,
+                       "Short read: %lli/%llu\n",
+                       i,
+                       length * sizeof(qcow_te));
+               goto bad;
+       } else if (i < 0) {
+               perror("read");
+               goto bad;
+       }
+
+       for (i=0; i < length; i++)
+               table[i] = htonll(disk_table[i]);
+
+       return 1;
+
+ bad:
+       return 0;
+}
+
+static int qcow_load_l2(struct qcow *qcow, qcow_te index)
+{
+       int ret;
+       struct l1_entry *entry;
+
+       entry = &qcow->table[index];
+
+       if (entry->loc_on_disk == 0) {
+               fprintf(stderr,
+                       "*** ERROR: Trying to load non-existent L2 %llu\n",
+                       index);
+               return 0;
+       }
+
+       if (entry->table != NULL)
+               fprintf(stderr,
+                       "*** WARNING: Reloading L2 table %llu\n",
+                       index);
+
+       entry->table = calloc(qcow_num_l2(&qcow->header),
+                             sizeof(qcow_te));
+
+       if (entry->table == NULL)
+               return 0;
+
+       ret = qcow_read_table(qcow->fd,
+                             entry->loc_on_disk,
+                             qcow_num_l2(&qcow->header),
+                             entry->table);
+
+       return ret;
+}
+
+/*
+ * Allocate a new L2 table on disk
+ */
+static int alloc_new_l2_on_disk(struct qcow *qcow, uint64_t l1_entry)
+{
+       uint64_t num, count, j;
+       struct l1_entry *entry;
+
+       if (qcow_num_l2(&qcow->header) < qcow_block_size(&qcow->header))
+               num = qcow_block_size(&qcow->header) /
+                       qcow_num_l2(&qcow->header);
+
+       else
+               num = 1;
+
+       qcow->l1_dirty = 1;
+
+       entry = &qcow->table[l1_entry];
+
+       entry->dirty = 1;
+       entry->table = calloc(qcow_num_l2(&qcow->header), sizeof(qcow_te));
+       entry->loc_on_disk = qcow->next_avail_block;
+       qcow_seen_block(qcow, entry->loc_on_disk,
+                       qcow_num_l2(&qcow->header) * sizeof(qcow_te));
+
+       printf("New L2 entry %llu @ %llu\n",
+              l1_entry, entry->loc_on_disk);
+
+       /* We try to distribute the extra to other non-allocated L2s,
+          so that we don't get too far away from our block
+          alignment */
+       count = num - 1;
+
+       /* Right now, we ignore the case that we might've allocated
+          too much space and didn't use it all.  In the normal case
+          of 512-byte blocks, this isn't an issue */
+       return 1; /* FIXME */
+
+       j = 0;
+       while ((count > 0) && (j < qcow_num_l1(&qcow->header))) {
+               if (qcow->table[j].loc_on_disk == 0) {
+                       qcow->table[j].loc_on_disk = qcow->next_avail_block;
+                       qcow_seen_block(qcow,
+                                       qcow->table[j].loc_on_disk,
+                                       qcow_num_l2(&qcow->header) *
+                                       sizeof(qcow_te));
+                       count--;
+               }
+               j++;
+       }
+
+       return 1;
+}
+
+/* Write out any dirty L2 tables, and the L1 table if needed */
+int qcow_write_tables(struct qcow *qcow)
+{
+       uint64_t  l1_size;
+       uint64_t  l2_size;
+       qcow_te  *l1_table;
+       uint64_t  i;
+       int       ret;
+
+       l1_size = qcow_num_l1(&qcow->header);
+       l2_size = qcow_num_l2(&qcow->header);
+
+       for (i=0; i < l1_size; i++) {
+
+               if (qcow->table[i].dirty && qcow->table[i].loc_on_disk) {
+                       qcow->table[i].dirty = 0;
+                       if (OPS_DEBUG)
+                               fprintf(stderr, "writing L2 %llu @ %llu\n",
+                                       i, qcow->table[i].loc_on_disk);
+                       ret = qcow_write_table(qcow->fd,
+                                              qcow->table[i].loc_on_disk,
+                                              l2_size,
+                                              qcow->table[i].table);
+
+                       if (ret != 1) {
+                               fprintf(stderr,
+                                       NAME "failed to write L2 %i\n", i);
+                               return 0;
+                       }
+               }
+       }
+
+       if (qcow->l1_dirty) {
+               l1_table = (qcow_te *)calloc(l1_size,
+                                            sizeof(qcow_te));
+
+               for (i=0; i < l1_size; i++) {
+                       l1_table[i] = qcow->table[i].loc_on_disk;
+                       if (l1_table[i])
+                               printf("Writing L1 entry %llu: %llu\n",
+                                      i, l1_table[i]);
+               }
+               ret = qcow_write_table(qcow->fd, qcow->header.l1_table_offset,
+                                      l1_size, l1_table);
+
+               free(l1_table);
+
+               qcow->l1_dirty = 0;
+
+               if (ret != 1) {
+                       fprintf(stderr, NAME "failed to write L1\n");
+                       return 0;
+               }
+       }
+
+       return 1;
+}
+
+/*
+ * Read in the L1 table.  We read L2 tables as needed based on the
+ * information in the l1.
+ */
+int qcow_read_tables(struct qcow *qcow)
+{
+       int         i;
+       uint64_t    numL1;
+       qcow_te    *disk_table;
+
+       numL1 = qcow_num_l1(&qcow->header);
+
+       disk_table = (qcow_te *)calloc(numL1,
+                                      sizeof(qcow_te));
+
+       dio_lseek(qcow->fd, qcow->header.l1_table_offset, SEEK_SET);
+       dio_read(qcow->fd, disk_table, numL1 * sizeof(qcow_te));
+
+       for (i=0; i < numL1; i++) {
+
+               qcow->table[i].table = NULL;
+               qcow->table[i].dirty = 0;
+
+               qcow->table[i].loc_on_disk = ntohll(disk_table[i]);
+       }
+
+       if (OPS_DEBUG)
+               qcow_print_header(qcow);
+
+       return 1;
+}
+
+
+/* Map the block of logical to physical */
+int qcow_make_mapping(struct qcow *qcow, uint64_t logical, uint64_t physical)
+{
+       uint64_t    l1_entry;
+       uint64_t    l2_entry;
+       
+       l1_entry = qcow_get_l1_entry(qcow, logical);
+       l2_entry = qcow_get_l2_entry(qcow, logical);
+
+       if (l1_entry >= qcow_num_l1(&qcow->header)) {
+               fprintf(stderr, "*** ERROR: L1 of %llu >= %llu, max\n",
+                       l1_entry,
+                       qcow_num_l1(&qcow->header));
+               fprintf(stderr, "    Offending new map: %llu -> %llu\n",
+                       logical, physical);
+               return 0;
+       }
+
+       if (l2_entry >= qcow_num_l2(&qcow->header)) {
+               fprintf(stderr, "*** ERRROR: L2 of %llu >= %llu, max\n",
+                       l2_entry,
+                       qcow_num_l2(&qcow->header));
+               return 0;
+       }
+
+       if (logical >= LOG_ABOVE) {
+               printf("Making mapping for %llu -> %llu\n"
+                      "  l1e: %llu   l2e: %llu\n",
+                      logical, physical, l1_entry, l2_entry);
+       }
+
+       if (qcow->table[l1_entry].table == NULL) {
+               if (qcow->table[l1_entry].loc_on_disk == 0) {
+
+                       if (!alloc_new_l2_on_disk(qcow, l1_entry)) {
+                               fprintf(stderr,
+                                       "Failed to alloc table for %llu\n",
+                                       l1_entry);
+                               return 0;
+                       }
+
+                       qcow->l1_dirty = 1;
+               } else {
+                       if (!qcow_load_l2(qcow, l1_entry)) {
+                               fprintf(stderr,
+                                       "Failed to load L2 %llu\n",
+                                       l1_entry);
+                               return 0;
+                       }
+               }
+       }
+
+       qcow->table[l1_entry].table[l2_entry] = physical;
+       qcow->table[l1_entry].dirty = 1;
+
+       return 1;
+}
+
+/*
+ * Make a new mapping for @logical by selecting the next available
+ * block
+ */
+uint64_t qcow_make_new_mapping(struct qcow *qcow, uint64_t logical)
+{
+       uint64_t new_block;
+
+       new_block = qcow->next_avail_block;
+
+       /* Align to the next 512-byte boundary */
+       /* FIXME: Do we want to leak space like this? */
+       if (new_block % 512) {
+               new_block += (512 - (new_block % 512));
+       }
+
+       qcow_seen_block(qcow, new_block, qcow_block_size(&qcow->header));
+
+       if (qcow_make_mapping(qcow, logical, new_block))
+               return new_block;
+       else
+               return 0; /* Block 0 is error, because it contains the
+                            header and L1, etc*/
+
+}
+
+/* Return the location that @logical maps to, 0 if unmapped */
+uint64_t qcow_get_mapping(struct qcow *qcow, uint64_t logical)
+{
+       uint64_t    l1_entry;
+       uint64_t    l2_entry;
+       uint64_t    physical;
+       qcow_te    *l2_table;
+
+       l1_entry = qcow_get_l1_entry(qcow, logical);
+       l2_entry = qcow_get_l2_entry(qcow, logical);
+
+       if (l1_entry >= qcow_num_l1(&qcow->header)) {
+               fprintf(stderr, "*** ERROR: L1 of %llu >= %llu, max\n",
+                       l1_entry,
+                       qcow_num_l1(&qcow->header));
+               fprintf(stderr, "    Offending map request: %llu (%llx)\n",
+                       logical, logical);
+               fprintf(stderr, "    l1_entry: %llu\n", l1_entry);
+               fprintf(stderr, "    l2_entry: %llu\n", l2_entry);
+               
+               return 0;
+       }
+       
+       if (l2_entry >= qcow_num_l2(&qcow->header)) {
+               fprintf(stderr, "*** ERROR: L2 of %llu >= %llu, max\n",
+                       l2_entry,
+                       qcow_num_l2(&qcow->header));
+               return 0;
+       }
+       
+       if (qcow->table[l1_entry].table == NULL) {
+               if (qcow->table[l1_entry].loc_on_disk == 0) {
+                       /* Not mapped if no L2 table */
+                       return 0;
+               } else {
+                       /* Need to load it in */
+                       if (!qcow_load_l2(qcow, l1_entry))
+                               return 0;
+               }
+       }
+
+       l2_table = qcow->table[l1_entry].table;
+
+       physical = l2_table[l2_entry];
+
+       if (OPS_DEBUG && 0) {
+               printf("L1 entry:       %llu\n", l1_entry);
+               printf("L2 entry:       %llu\n", l2_entry);
+               printf("Physical Block: %llu (%llx)\n", physical, physical);
+               printf("\n");
+       }
+
+       return physical;
+}
+
+/*
+ * Return 1 if any of the tables need flushing, 0 otherwise
+ */
+int qcow_is_anything_dirty(struct qcow *qcow)
+{
+       uint64_t i;
+
+       if (qcow->l1_dirty)
+               return 1;
+
+       for (i=0; i < qcow_num_l1(&qcow->header); i++) {
+               if (qcow->table[i].dirty)
+                       return 1;
+       }
+
+       return 0;
+}
+
+/*
+ * Print out the header in human-readable format
+ */
+void qcow_print_header(struct qcow *qcow)
+{
+       printf("=== QCOW HEADER ===\n");
+       printf("Magic:          %x (should be %x)\n", qcow->header.magic,
+              QCOW_MAGIC);
+       printf("Version:        %x\n",   qcow->header.version);
+       printf("File Offset:    %llu\n", qcow->header.backing_filename_offset);
+       printf("File Size:      %u\n",   qcow->header.backing_filename_size);
+       printf("Mod Time:       %u\n",   qcow->header.mtime);
+       printf("Size:           %llu\n", qcow->header.size);
+       printf("Cluster Bits:   %hhu\n", qcow->header.cluster_bits);
+       printf("L2 Bits:        %hhu\n", qcow->header.l2_bits);
+       printf("Crypto Method:  %x\n",   qcow->header.crypto_method);
+       printf("L1 Table @:     %llu\n",   qcow->header.l1_table_offset);
+       printf("Backing File:   %s\n",   qcow->filename);
+
+       printf("\nCalculated Information:\n");
+
+       printf("Next Avail:     %llu\n", qcow->next_avail_block);
+       printf("Cluster Mask:   %016llx\n", qcow_cmask(&qcow->header));
+       printf("L2 Mask:        %016llx\n", qcow_l2mask(&qcow->header));
+       printf("L1 Mask:        %016llx\n", qcow_l1mask(&qcow->header));
+       printf("Num L1:         %llu\n", qcow_num_l1(&qcow->header));
+       printf("Num L2:         %llu\n", qcow_num_l2(&qcow->header));
+}
diff -r a3656acd770b -r be4574d28803 tools/cowd/plugins/qcow/qcow_ops.h
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/cowd/plugins/qcow/qcow_ops.h        Fri Aug 25 15:01:36 2006 -0500
@@ -0,0 +1,15 @@
+#ifndef __QCOW_OPS_H
+#define __QCOW_OPS_H
+
+#include <stdint.h>
+
+#include "qcow.h"
+
+int qcow_rw_header(int fd, struct qcow_header *header, int write);
+int qcow_read_backing_info(struct qcow *qcow);
+int qcow_read_l1_table(struct qcow *qcow);
+uint64_t qcow_get_mapping(struct qcow *qcow, uint64_t logical);
+
+void qcow_print_header(struct qcow *qcow);
+
+#endif
diff -r a3656acd770b -r be4574d28803 tools/cowd/plugins/qcow/qcow_plugin.c
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/cowd/plugins/qcow/qcow_plugin.c     Fri Aug 25 15:01:36 2006 -0500
@@ -0,0 +1,519 @@
+/*
+ * Copyright (C) International Business Machines Corp., 2006
+ * Author: Dan Smith <danms@xxxxxxxxxx>
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License. See the file COPYING in the main directory
+ * of this archive for more details.
+ *
+ */
+
+#define _LARGEFILE64_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdint.h>
+
+#include <cowd_plugin.h>
+
+#include <libdevmapper.h>
+
+#include "qcow.h"
+#include "qcow_ops.h"
+
+#define MAX_PATH 256
+#define ERR_LEN  256
+
+#define LOOP_SET_FD 0x4C00
+#define LOOP_CLR_FD 0x4C01
+
+char errmsg[ERR_LEN];
+
+struct qcow_private {
+       struct qcow  qcow;
+       int          init;
+
+       char         base_path[MAX_PATH];
+       char         qcow_path[MAX_PATH];
+
+       char         base_dev[MAX_PATH];
+       char         qcow_dev[MAX_PATH];
+
+       dev_t        base_dev_t;
+       dev_t        qcow_dev_t;
+
+       unsigned long qcow_size;
+
+       int          debug;
+};
+
+static dev_t *qcow_get_devs(struct cow_device *dev, int *count)
+{
+       struct qcow_private *prv = dev->plugin_private;
+       struct stat s;
+       dev_t *devs;
+
+       devs = malloc(sizeof(*dev) * 2);
+       if (!devs) {
+               count = 0;
+               return NULL;
+       }
+
+       devs[0] = prv->base_dev_t;
+       devs[1] = prv->qcow_dev_t;
+
+       *count = 2;
+       return devs;    
+}
+
+/*
+ * We use the loop driver to make the base and cow files available to
+ * the kernel.  The problem is, we can't grow the cow file underneath
+ * the loop device.  This function "inflates" the cow file to the
+ * maximum size it could be before we hook it up to the loop driver.
+ * This isn't *too* bad because it's just a sparse file.
+ */
+static int qcow_inflate(struct qcow_private *prv)
+{
+       struct qcow_header header;
+       int fd;
+       uint64_t offset;
+
+       fd = open(prv->qcow_path, O_RDWR | O_LARGEFILE);
+       if (fd < 0) {
+               perror("open");
+               return PLUGIN_FAIL;
+       }
+
+       if (read(fd, &header, sizeof(header)) != sizeof(header)) {
+               perror("read");
+               return PLUGIN_FAIL;
+       }
+
+       header.size = ntohll(header.size);
+
+       offset = lseek64(fd, header.size, SEEK_SET);
+
+       if (prv->debug)
+               printf("## Inflating %s to %llu (%llu)\n",
+                      prv->qcow_path, offset, header.size);
+
+       if (offset != header.size) {
+               fprintf(stderr, "Failed to lseek to %llu\n", header.size);
+               return PLUGIN_FAIL;
+       }
+
+       if (write(fd, &fd, 1) != 1) {
+               perror("write");
+               close(fd);
+               return PLUGIN_FAIL;
+       }
+
+       close(fd);
+       return PLUGIN_OK;
+}
+
+/*
+ * This "deflates" the sparse cow file back to the appropriate size
+ */
+static void qcow_deflate(struct qcow_private *prv)
+{
+       printf("### Truncating %s to %llu\n",
+              prv->qcow_path, prv->qcow.next_avail_block);
+       truncate(prv->qcow_path, prv->qcow.next_avail_block);
+}
+
+/*
+ * Process the subset of arguments we were given
+ */
+static void qcow_process_args(int argc, char **argv, struct qcow_private *prv)
+{
+       int c, optidx = 0;
+       int i;
+       static struct option lopts[] = {
+               {"qcow",  1, 0, 'q'},
+               {"base",  1, 0, 'b'},
+               {"debug", 0, 0, 'd'},
+               {0,       0, 0,  0 }
+       };
+
+       strcpy(prv->qcow_path, argv[1]);
+       return;
+
+       /* getopt doesn't like the way I've arranged the strings for
+          some reason, so we just use the first arg as the qcow file
+          for now */
+
+       for (i=0; i<argc; i++)
+               fprintf(stderr, "Arg %i: %s\n", i, argv[i]);
+
+       while (1) {
+               fprintf(stderr, "Going... %s\n", argv[0]);
+               c = getopt_long(argc, argv, "db:q:", lopts, &optidx);
+               if (c == -1)
+                       break;
+
+               fprintf(stderr, "Got: %c\n", c);
+
+               switch (c) {
+
+               case 'b':
+                       strncpy(prv->base_path, optarg, MAX_PATH);
+                       break;
+               case 'q':
+                       strncpy(prv->qcow_path, optarg, MAX_PATH);
+                       break;
+               case 'd':
+                       prv->debug = 1;
+                       break;
+               };
+
+       }
+}
+
+#if 0
+/*
+ * Hook up @path to @dev
+ *
+ * NB: This doesn't work at the moment!
+ */
+static int qcow_loop_setup(const char *path, char *dev)
+{
+       int fd, lfd;
+       int i, ret;
+       char ldevpath[256];
+
+       fd = open(path, O_RDWR);
+       if (fd < 0)
+               return 0;
+
+       for (i = 0; i < 8; i++) {
+               snprintf(ldevpath, 256, "/dev/loop%i", i);
+               lfd = open(ldevpath, O_RDWR);
+               if (lfd < 0) {
+                       fprintf(stderr, "Failed to open %s\n", ldevpath);
+                       continue;
+               }
+               ret = ioctl(lfd, LOOP_SET_FD, fd);
+               close(lfd);
+               if (ret == 0) {
+                       strcpy(dev, ldevpath);
+                       close(fd);
+                       return 1;
+               } else {
+                       fprintf(stderr, "ioctl() failed:\n");
+                       perror(ldevpath);
+               }
+       }
+
+       close(fd);
+
+       printf("No free loops for file: %s\n", path);
+
+       return 0;
+}
+
+/*
+ * Detach @dev from its backing file
+ *
+ * NB: This isn't used at the moment!
+ */
+static int qcow_loop_destroy(const char *dev)
+{
+       int lfd;
+       int ret;
+
+       lfd = open(dev, O_RDWR);
+       if (lfd < 0)
+               return 0;
+
+       ret = ioctl(lfd, LOOP_CLR_FD, 0);
+
+       if (ret == 0)
+               return 1;
+       else
+               return 0;
+}
+#endif 
+
+/*
+ * Call the appropriate qcow functions to initialize all our metadata
+ * and accounting information.
+ */
+static int qcow_read_metadata(struct cow_device *dev, int force_init)
+{
+       struct qcow_private *prv = dev->plugin_private;
+       int ret;
+
+       if (force_init) {
+               /* At some point, we want to be able to format a
+                  qcow file outselves */
+               fprintf(stderr, "The QCOW plugin doesn't support init yet\n");
+               return PLUGIN_FAIL;
+       }
+
+       if (! prv->init) {
+               if (prv->debug)
+                       printf("Init qcow from header\n");
+               ret = qcow_init_from_header(&prv->qcow);
+               if (! ret)
+                       return PLUGIN_FAIL;
+
+               dev->block_size = 1 << prv->qcow.header.cluster_bits;
+               if (prv->debug)
+                       fprintf(stderr, "Block size: %u\n", dev->block_size);
+
+               dev->blocks = get_device_blocks(prv->base_dev) /
+                       (dev->block_size / 512);
+
+               if (prv->debug)
+                       fprintf(stderr, "Blocks: %lu\n", dev->blocks);
+       }
+
+       if (prv->debug)
+               printf("Reading tables\n");
+       
+       ret = qcow_read_tables(&prv->qcow);
+       if (! ret)
+               return PLUGIN_FAIL;
+
+       prv->init = 1;
+
+       return PLUGIN_OK;
+
+}
+
+static int qcow_init(struct cow_device *dev, int debug)
+{
+
+       struct qcow_private *prv;
+       struct stat s;
+
+       if (debug)
+               printf(NAME "init\n");
+
+       memset(errmsg, 0, ERR_LEN);
+
+       prv               = (struct qcow_private *)malloc(sizeof(*prv));
+       prv->qcow.fd      = -1;
+       prv->init         = 0;
+       prv->debug        = debug;
+       prv->base_path[0] = '\0';
+       prv->qcow_path[0] = '\0';
+       qcow_init_qcow(&prv->qcow);
+
+       qcow_process_args(dev->plugin_num_args, dev->plugin_args, prv);
+
+       if (prv->qcow_path[0] == '\0') {
+               snprintf(errmsg, ERR_LEN, "Path to qcow file is required!\n");
+               return PLUGIN_FAIL;
+       }
+
+       prv->qcow.next_avail_block = get_file_size(prv->qcow_path);
+       prv->qcow_size = get_file_size(prv->qcow_path);
+
+       /* Dirty hack to get around the fact that loop-backed files
+          can't grow */
+       qcow_inflate(prv);
+
+       if (prv->debug)
+               printf("Opening %s\n", prv->qcow_path);
+
+       prv->qcow.fd = dio_open(prv->qcow_path, O_RDWR);
+       if (prv->qcow.fd < 0) {
+               snprintf(errmsg, ERR_LEN,
+                        "Failed to open %s with O_DIRECT: %s\n",
+                        prv->qcow_path, strerror(prv->qcow.fd));
+               return PLUGIN_FAIL;
+       }
+
+       if (prv->debug)
+               printf("### Preset next_avail_block to %llu\n",
+                      prv->qcow.next_avail_block);
+
+       /* Init the qcow header information */
+       qcow_rw_header(prv->qcow.fd, &prv->qcow.header, 0);
+       qcow_read_backing_info(&prv->qcow);
+       qcow_print_header(&prv->qcow);
+
+       /* FIXME: REMOVE */
+       qcow_print_header(&prv->qcow);
+
+       /* Get the files/devices setup for device-mapper */
+       if (prv->base_path[0] == '\0')
+               strcpy(prv->base_path, prv->qcow.filename);
+
+       /* FIXME: Need real loop support!! */
+
+       if (is_file(prv->base_path)) {
+               sprintf(prv->base_dev, "/dev/loop0");
+               loop_destroy(prv->base_dev);
+               loop_setup(prv->base_dev, prv->base_path);
+       }
+
+       if (is_file(prv->qcow_path)) {
+               sprintf(prv->qcow_dev, "/dev/loop1");
+               loop_destroy(prv->qcow_dev);
+               loop_setup(prv->qcow_dev, prv->qcow_path);
+       }
+
+#if 0
+       if (is_file(prv->qcow_path))
+               qcow_loop_setup(prv->qcow_path, prv->qcow_dev);
+       else
+               strcpy(prv->qcow_dev, prv->qcow_path);
+
+       if (is_file(prv->base_path))
+               qcow_loop_setup(prv->base_path, prv->base_dev);
+       else
+               strcpy(prv->base_dev, prv->base_path);
+#endif
+
+       stat(prv->base_dev, &s);
+       prv->base_dev_t = s.st_rdev;
+
+       stat(prv->qcow_dev, &s);
+       prv->qcow_dev_t = s.st_rdev;
+
+       if (prv->debug)
+               printf("Base Device: %s  Cow Device: %s\n",
+                      prv->base_dev, prv->qcow_dev);
+
+       dev->plugin_private = prv;
+
+       return qcow_read_metadata(dev, 0);
+
+}
+
+static void qcow_cleanup(struct cow_device *dev)
+{
+       /* FIXME: Do something more useful here */
+       struct qcow_private *prv = dev->plugin_private;
+
+       close(prv->qcow.fd);
+
+       /* FIXME: Need to check if these are actually loops */
+       loop_destroy(prv->base_dev);
+       loop_destroy(prv->qcow_dev);
+
+       qcow_deflate(prv);
+
+       /* Free some stuff */
+}
+
+
+static int qcow_write_metadata(struct cow_device *dev)
+{
+       struct qcow_private *prv = dev->plugin_private;
+       int ret;
+
+       if (prv->debug)
+               printf("Writing metadata!\n");
+
+       ret = qcow_write_tables(&prv->qcow);
+
+       if (! ret)
+               return PLUGIN_FAIL;
+       else
+               return PLUGIN_OK;
+}
+
+static int qcow_map_block(struct cow_device *dev, 
+                         struct dmu_map_data *map)
+{
+
+       struct qcow_private *prv = dev->plugin_private;
+       uint64_t tmp;
+       uint64_t org, org_block;
+
+       /*
+        * FIXME: This is super ugly
+        */
+
+       /* Convert to start byte position of cluster */
+       org_block = dmu_map_get_block(map);
+       org = org_block << prv->qcow.header.cluster_bits;
+
+       if (prv->debug && 0)
+               fprintf(stderr, "Looking for existing map for %llu\n",
+                       org_block);
+
+       tmp = qcow_get_mapping(&prv->qcow, org);
+       if ((tmp == 0) && dmu_map_is_write(map)) {
+               /* NEW mapping for WRITE access:
+                  Remap to somewhere in the cow device */
+               if (prv->debug && 0)
+                       fprintf(stderr, "Not found, mapping...\n");
+
+               tmp = qcow_make_new_mapping(&prv->qcow, (uint64_t)org);
+
+               dmu_map_set_block(map, qcow_align_to_block(&prv->qcow, tmp));
+               dmu_map_set_offset(map, qcow_calc_offset(&prv->qcow, tmp));
+
+               dmu_map_set_copy_src_dev(map, prv->base_dev_t);
+               dmu_map_set_dest_dev(map, prv->qcow_dev_t);
+
+       } else if ((tmp == 0) && !dmu_map_is_write(map)) {
+               /* NEW mapping for READ access:
+                  Remap to the same place in the base device */
+               dmu_map_set_block(map, org_block);
+
+               dmu_map_set_dest_dev(map, prv->base_dev_t);
+
+       } else if ((tmp == 0) && dmu_map_is_write(map)) {
+               /* This should not be allowed */
+               return PLUGIN_FAIL;
+       } else {
+               /* OLD mapping (access doesn't matter):
+                  Remap to the correct location in the cow device */
+               dmu_map_set_block(map,
+                                 qcow_align_to_block(&prv->qcow, tmp));
+               dmu_map_set_offset(map, 
+                                  qcow_calc_offset(&prv->qcow, tmp));
+               if (prv->debug)
+                       fprintf(stderr,
+                               "Found existing map for %llu: %llx (%llu)\n",
+                               org, tmp, tmp);
+               
+               dmu_map_set_dest_dev(map, prv->qcow_dev_t);
+       }
+
+       if (tmp > prv->qcow.header.size) {
+               snprintf(errmsg, ERR_LEN,
+                        "Tried to map a block beyond end of device: "
+                        "%llu > %llu\n",
+                        tmp, prv->qcow.header.size);
+               return PLUGIN_FAIL;
+       }
+
+       return PLUGIN_OK;
+}
+
+static bool qcow_need_flush(struct cow_device *dev)
+{
+
+       struct qcow_private *prv = dev->plugin_private;
+
+       return qcow_is_anything_dirty(&prv->qcow);
+}
+
+int load_plugin(struct cowd_plugin *p)
+{
+
+       p->init_plugin    = qcow_init;
+       p->write_metadata = qcow_write_metadata;
+       p->map_prepare    = qcow_map_block;
+       p->cleanup_plugin = qcow_cleanup;
+       p->need_flush     = qcow_need_flush;
+       p->errmsg         = errmsg;
+       p->get_devs       = qcow_get_devs;
+
+       return 1;
+
+}

_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.