Expand HAMMER filesystem step 1/2
authorMichael Neumann <mneumann@ntecs.de>
Tue, 14 Jul 2009 13:55:02 +0000 (15:55 +0200)
committerMichael Neumann <mneumann@ntecs.de>
Tue, 14 Jul 2009 13:55:02 +0000 (15:55 +0200)
Format the volume and increase other volumes' vol_count field.
The space of the new volume is not yet given to the filesystem (that's step 2).

Example:

newfs_hammer -L TEST /dev/da0
mount_hammer /dev/da0 /hammer
hammer expand /hammer /dev/da1
umount /hammer
mount_hammer /dev/da0:/dev/da1 /hammer

# the following fails (wrong number of volumes)
mount_hammer /dev/da0 /hammer

sbin/hammer/cmd_expand.c
sys/vfs/hammer/hammer_expand.c
sys/vfs/hammer/hammer_ioctl.h

index c642566..4da637e 100644 (file)
@@ -2,7 +2,8 @@
  * Copyright (c) 2009 The DragonFly Project.  All rights reserved.
  *
  * This code is derived from software contributed to The DragonFly Project
- * by Michael Neumann <mneumann@ntecs.de>
+ * by Matthew Dillon <dillon@backplane.com> and
+ * Michael Neumann <mneumann@ntecs.de>
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -39,6 +40,7 @@
 #include "hammer.h"
 #include <string.h>
 
+static uint64_t check_volume(const char *vol_name);
 static void expand_usage(int exit_code);
 
 /*
@@ -61,6 +63,9 @@ hammer_cmd_expand(char **av, int ac)
 
        bzero(&expand, sizeof(expand));
        strncpy(expand.device_name, av[1], MAXPATHLEN);
+       expand.vol_size = check_volume(av[1]);
+       expand.boot_area_size = 0;      // XXX
+       expand.mem_area_size = 0;       // XXX
 
        if (ioctl(fd, HAMMERIOC_EXPAND, &expand) < 0) {
                fprintf(stderr, "hammer expand ioctl: %s\n", strerror(errno));
@@ -77,3 +82,47 @@ expand_usage(int exit_code)
        fprintf(stderr, "hammer expand <filesystem> <device>\n");
        exit(exit_code);
 }
+
+/*
+ * Check basic volume characteristics.  HAMMER filesystems use a minimum
+ * of a 16KB filesystem buffer size.
+ *
+ * Returns the size of the device.
+ *
+ * From newfs_hammer.c
+ */
+static
+uint64_t
+check_volume(const char *vol_name)
+{
+       struct partinfo pinfo;
+       int fd;
+
+       /*
+        * Get basic information about the volume
+        */
+       fd = open(vol_name, O_RDWR);
+       if (fd < 0)
+               errx(1, "Unable to open %s R+W", vol_name);
+
+       if (ioctl(fd, DIOCGPART, &pinfo) < 0) {
+               errx(1, "No block device: %s", vol_name);
+       }
+       /*
+        * When formatting a block device as a HAMMER volume the
+        * sector size must be compatible. HAMMER uses 16384 byte
+        * filesystem buffers.
+        */
+       if (pinfo.reserved_blocks) {
+               errx(1, "HAMMER cannot be placed in a partition "
+                       "which overlaps the disklabel or MBR");
+       }
+       if (pinfo.media_blksize > 16384 ||
+           16384 % pinfo.media_blksize) {
+               errx(1, "A media sector size of %d is not supported",
+                    pinfo.media_blksize);
+       }
+
+       close(fd);
+       return pinfo.media_size;
+}
index 10adf05..0d21f85 100644 (file)
@@ -2,7 +2,8 @@
  * Copyright (c) 2009 The DragonFly Project.  All rights reserved.
  *
  * This code is derived from software contributed to The DragonFly Project
- * by Michael Neumann <mneumann@ntecs.de>
+ * by Matthew Dillon <dillon@backplane.com> and
+ * Michael Neumann <mneumann@ntecs.de>
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  */
 
 #include "hammer.h"
+#include <sys/fcntl.h>
+#include <sys/nlookup.h>
+#include <sys/buf.h>
+
+static int
+hammer_format_volume_header(struct hammer_mount *hmp, const char *vol_name,
+       int vol_no, int vol_count,
+       int64_t vol_size, int64_t boot_area_size, int64_t mem_area_size);
+
 
 int
 hammer_ioc_expand(hammer_transaction_t trans, hammer_inode_t ip,
                struct hammer_ioc_expand *expand)
 {
-       kprintf("hammer expand, device: %s\n", expand->device_name);
-       return 0;
+       struct hammer_mount *hmp = trans->hmp;
+       struct mount *mp = hmp->mp;
+       int error;
+
+       if (mp->mnt_flag & MNT_RDONLY) {
+               kprintf("Cannot expand read-only HAMMER filesystem\n");
+               return (EINVAL);
+       }
+
+       if (hmp->nvolumes + 1 >= HAMMER_MAX_VOLUMES) {
+               kprintf("Max number of HAMMER volumes exceeded\n");
+               return (EINVAL);
+       }
+
+       error = hammer_format_volume_header(
+               hmp,
+               hmp->rootvol->ondisk->vol_name,
+               hmp->nvolumes,
+               hmp->nvolumes+1,
+               expand->vol_size,
+               expand->boot_area_size,
+               expand->mem_area_size);
+
+       if (error == 0) {
+               error = hammer_install_volume(hmp, expand->device_name, NULL);
+       }
+
+       if (error == 0) {
+               ++hmp->nvolumes;
+               hammer_sync_lock_sh(trans);
+               hammer_lock_ex(&hmp->blkmap_lock);
+
+               /*
+                * Set each volumes new value of the vol_count field.
+                */
+               for (int vol_no = 0; vol_no < hmp->nvolumes; ++vol_no) {
+                       hammer_volume_t volume;
+                       volume = hammer_get_volume(hmp, vol_no, &error);
+                       KKASSERT(error == 0);
+                       hammer_modify_volume_field(trans, volume, vol_count);
+                       volume->ondisk->vol_count = hmp->nvolumes;
+                       hammer_modify_volume_done(volume);
+                       hammer_rel_volume(volume, 0);
+               }
+
+               hammer_unlock(&hmp->blkmap_lock);
+               hammer_sync_unlock(trans);
+       }
+
+       if (error) {
+               kprintf("An error occured: %d\n", error);
+       }
+
+       return (error);
+}
+
+static int
+hammer_format_volume_header(struct hammer_mount *hmp, const char *vol_name,
+       int vol_no, int vol_count,
+       int64_t vol_size, int64_t boot_area_size, int64_t mem_area_size)
+{
+       struct vnode *devvp = NULL;
+       struct buf *bp = NULL;
+       struct nlookupdata nd;
+       struct hammer_volume_ondisk *ondisk;
+       int error;
+
+       /*
+        * Get the device vnode
+        */
+       error = nlookup_init(&nd, vol_name, UIO_SYSSPACE, NLC_FOLLOW);
+       if (error == 0)
+               error = nlookup(&nd);
+       if (error == 0)
+               error = cache_vref(&nd.nl_nch, nd.nl_cred, &devvp);
+       nlookup_done(&nd);
+
+       if (error == 0) {
+               if (vn_isdisk(devvp, &error)) {
+                       error = vfs_mountedon(devvp);
+               }
+       }
+       if (error == 0 &&
+           count_udev(devvp->v_umajor, devvp->v_uminor) > 0) {
+               error = EBUSY;
+       }
+       if (error == 0) {
+               vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
+               error = vinvalbuf(devvp, V_SAVE, 0, 0);
+               if (error == 0) {
+                       error = VOP_OPEN(devvp, FREAD|FWRITE, FSCRED, NULL);
+               }
+               vn_unlock(devvp);
+       }
+       if (error) {
+               if (devvp)
+                       vrele(devvp);
+               return (error);
+       }
+
+       /*
+        * Extract the volume number from the volume header and do various
+        * sanity checks.
+        */
+       KKASSERT(HAMMER_BUFSIZE >= sizeof(struct hammer_volume_ondisk));
+       error = bread(devvp, 0LL, HAMMER_BUFSIZE, &bp);
+       if (error || bp->b_bcount < sizeof(struct hammer_volume_ondisk))
+               goto late_failure;
+
+       ondisk = (struct hammer_volume_ondisk*) bp->b_data;
+
+       /*
+        * Note that we do NOT allow to use a device that contains
+        * a valid HAMMER signature. It has to be cleaned up with dd
+        * before.
+        */
+       if (ondisk->vol_signature == HAMMER_FSBUF_VOLUME) {
+               kprintf("hammer_expand: Formatting of valid HAMMER volume "
+                       "%s denied. Erase with dd!\n", vol_name);
+               error = EFTYPE;
+               goto late_failure;
+       }
+
+       bzero(ondisk, sizeof(struct hammer_volume_ondisk));
+       ksnprintf(ondisk->vol_name, sizeof(ondisk->vol_name), "%s", vol_name);
+       ondisk->vol_fstype = hmp->rootvol->ondisk->vol_fstype;
+       ondisk->vol_signature = HAMMER_FSBUF_VOLUME;
+       ondisk->vol_fsid = hmp->fsid;
+       ondisk->vol_rootvol = hmp->rootvol->vol_no;
+       ondisk->vol_no = vol_no;
+       ondisk->vol_count = vol_count;
+       ondisk->vol_version = hmp->version;
+
+       /*
+        * Reserve space for (future) header junk, setup our poor-man's
+        * bigblock allocator.
+        */
+       int64_t vol_alloc = HAMMER_BUFSIZE * 16;
+
+       ondisk->vol_bot_beg = vol_alloc;
+       vol_alloc += boot_area_size;
+       ondisk->vol_mem_beg = vol_alloc;
+       vol_alloc += mem_area_size;
+
+       /*
+        * The remaining area is the zone 2 buffer allocation area.  These
+        * buffers
+        */
+       ondisk->vol_buf_beg = vol_alloc;
+       ondisk->vol_buf_end = vol_size & ~(int64_t)HAMMER_BUFMASK;
+
+       if (ondisk->vol_buf_end < ondisk->vol_buf_beg) {
+               kprintf("volume %d %s is too small to hold the volume header",
+                    ondisk->vol_no, ondisk->vol_name);
+               error = EFTYPE;
+               goto late_failure;
+       }
+
+       ondisk->vol_nblocks = (ondisk->vol_buf_end - ondisk->vol_buf_beg) /
+                             HAMMER_BUFSIZE;
+       ondisk->vol_blocksize = HAMMER_BUFSIZE;
+
+       /*
+        * Write volume header to disk
+        */
+       error = bwrite(bp);
+       bp = NULL;
+
+late_failure:
+       if (bp)
+               brelse(bp);
+       VOP_CLOSE(devvp, FREAD|FWRITE);
+       if (devvp)
+               vrele(devvp);
+       return (error);
 }
index 9d5867d..770e61e 100644 (file)
@@ -338,6 +338,9 @@ struct hammer_ioc_version {
 struct hammer_ioc_expand {
        struct hammer_ioc_head head;
        char                    device_name[MAXPATHLEN];
+       int64_t                 vol_size;
+       int64_t                 boot_area_size;
+       int64_t                 mem_area_size;
 };
 
 union hammer_ioc_mrecord_any {