diff options
author | Neil Brown <neilb@suse.de> | 2005-06-07 23:16:35 +0000 |
---|---|---|
committer | Neil Brown <neilb@suse.de> | 2005-06-07 23:16:35 +0000 |
commit | c82f047cfceb479c9c6b56b44c196018af050e45 (patch) | |
tree | 2bce84dc8b9cf6068a863372f0c06530def3a1a4 /bitmap.c | |
parent | a3fd117c7a2b2449704ee86eb9ec180906142f7a (diff) | |
download | mdadm-c82f047cfceb479c9c6b56b44c196018af050e45.tar.gz mdadm-c82f047cfceb479c9c6b56b44c196018af050e45.tar.xz mdadm-c82f047cfceb479c9c6b56b44c196018af050e45.zip |
Initial bitmap support
Signed-off-by: Neil Brown <neilb@cse.unsw.edu.au>
Diffstat (limited to 'bitmap.c')
-rw-r--r-- | bitmap.c | 315 |
1 files changed, 315 insertions, 0 deletions
diff --git a/bitmap.c b/bitmap.c new file mode 100644 index 0000000..57969a6 --- /dev/null +++ b/bitmap.c @@ -0,0 +1,315 @@ +/* + * mdadm - manage Linux "md" devices aka RAID arrays. + * + * Copyright (C) 2004 Paul Clements, SteelEye Technology, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include "mdadm.h" +#include <asm/byteorder.h> + +#define min(a,b) (((a) < (b)) ? (a) : (b)) + +inline void sb_le_to_cpu(bitmap_super_t *sb) +{ + sb->magic = __le32_to_cpu(sb->magic); + sb->version = __le32_to_cpu(sb->version); + /* uuid gets no translation */ + sb->events = __le64_to_cpu(sb->events); + sb->events_cleared = __le64_to_cpu(sb->events_cleared); + sb->state = __le32_to_cpu(sb->state); + sb->chunksize = __le32_to_cpu(sb->chunksize); + sb->daemon_sleep = __le32_to_cpu(sb->daemon_sleep); + sb->sync_size = __le64_to_cpu(sb->sync_size); +} + +inline void sb_cpu_to_le(bitmap_super_t *sb) +{ + sb_le_to_cpu(sb); /* these are really the same thing */ +} + +mapping_t bitmap_states[] = { + { "OK", 0 }, + { "Out of date", 2 }, + { NULL, -1 } +}; + +const char *bitmap_state(int state_num) +{ + char *state = map_num(bitmap_states, state_num); + return state ? state : "Unknown"; +} + +const char *human_chunksize(unsigned long bytes) +{ + static char buf[16]; + char *suffixes[] = { "B", "KB", "MB", "GB", "TB", NULL }; + int i = 0; + + while (bytes >> 10) { + bytes >>= 10; + i++; + } + + sprintf(buf, "%lu %s", bytes, suffixes[i]); + + return buf; +} + +typedef struct bitmap_info_s { + bitmap_super_t sb; + unsigned long long total_bits; + unsigned long long dirty_bits; +} bitmap_info_t; + +/* count the dirty bits in the first num_bits of byte */ +inline int count_dirty_bits_byte(char byte, int num_bits) +{ + int num = 0; + + switch (num_bits) { /* fall through... */ + case 8: if (byte & 128) num++; + case 7: if (byte & 64) num++; + case 6: if (byte & 32) num++; + case 5: if (byte & 16) num++; + case 4: if (byte & 8) num++; + case 3: if (byte & 4) num++; + case 2: if (byte & 2) num++; + case 1: if (byte & 1) num++; + default: break; + } + + return num; +} + +int count_dirty_bits(char *buf, int num_bits) +{ + int i, num = 0; + + for (i=0; i < num_bits / 8; i++) + num += count_dirty_bits_byte(buf[i], 8); + + if (num_bits % 8) /* not an even byte boundary */ + num += count_dirty_bits_byte(buf[i], num_bits % 8); + + return num; +} + +/* calculate the size of the bitmap given the array size and bitmap chunksize */ +unsigned long long bitmap_bits(unsigned long long array_size, + unsigned long chunksize) +{ + return (array_size * 512 + chunksize - 1) / chunksize; +} + +bitmap_info_t *bitmap_fd_read(int fd, int brief) +{ + unsigned long long total_bits = 0, read_bits = 0, dirty_bits = 0; + bitmap_info_t *info; + char buf[512]; + int n; + + info = malloc(sizeof(*info)); + if (info == NULL) { + fprintf(stderr, Name ": failed to allocate %d bytes\n", + sizeof(*info)); + return NULL; + } + + if (read(fd, &info->sb, sizeof(info->sb)) != sizeof(info->sb)) { + fprintf(stderr, Name ": failed to read superblock of bitmap " + "file: %s\n", strerror(errno)); + free(info); + return NULL; + } + + sb_le_to_cpu(&info->sb); /* convert superblock to CPU byte ordering */ + + if (brief || info->sb.sync_size == 0) + goto out; + + /* read the rest of the file counting total bits and dirty bits -- + * we stop when either: + * 1) we hit EOF, in which case we assume the rest of the bits (if any) + * are dirty + * 2) we've read the full bitmap, in which case we ignore any trailing + * data in the file + */ + total_bits = bitmap_bits(info->sb.sync_size, info->sb.chunksize); + + while ((n = read(fd, buf, sizeof(*buf))) > 0) { + unsigned long long remaining = total_bits - read_bits; + + if (remaining > sizeof(*buf) * 8) /* we want the full buffer */ + remaining = sizeof(*buf) * 8; + if (remaining > n * 8) /* the file is truncated */ + remaining = n * 8; + dirty_bits += count_dirty_bits(buf, remaining); + + read_bits += remaining; + if (read_bits >= total_bits) /* we've got what we want */ + break; + } + + if (read_bits < total_bits) { /* file truncated... */ + fprintf(stderr, Name ": WARNING: bitmap file is not large " + "enough for array size %llu!\n\n", info->sb.sync_size); + total_bits = read_bits; + } +out: + info->total_bits = total_bits; + info->dirty_bits = dirty_bits; + return info; +} + +bitmap_info_t *bitmap_file_read(char *filename, int brief) +{ + int fd; + bitmap_info_t *info; + + fd = open(filename, O_RDONLY); + if (fd < 0) { + fprintf(stderr, Name ": failed to open bitmap file %s: %s\n", + filename, strerror(errno)); + return NULL; + } + + info = bitmap_fd_read(fd, brief); + close(fd); + return info; +} + +int ExamineBitmap(char *filename, int brief) +{ + /* + * Read the bitmap file and display its contents + */ + + bitmap_super_t *sb; + bitmap_info_t *info; + int rv = 1; + + info = bitmap_file_read(filename, brief); + if (!info) + return rv; + + sb = &info->sb; + printf(" Filename : %s\n", filename); + printf(" Magic : %08x\n", sb->magic); + if (sb->magic != BITMAP_MAGIC) { + fprintf(stderr, Name ": invalid bitmap magic 0x%x, the bitmap file appears to be corrupted\n", sb->magic); + } + printf(" Version : %d\n", sb->version); + if (sb->version != BITMAP_MAJOR) { + fprintf(stderr, Name ": unknown bitmap version %d, either the bitmap file is corrupted or you need to upgrade your tools\n", sb->version); + goto free_info; + } + + rv = 0; + printf(" UUID : %08x.%08x.%08x.%08x\n", + *(__u32 *)(sb->uuid+0), + *(__u32 *)(sb->uuid+4), + *(__u32 *)(sb->uuid+8), + *(__u32 *)(sb->uuid+12)); + printf(" Events : %llu\n", sb->events); + printf(" Events Cleared : %llu\n", sb->events_cleared); + printf(" State : %s\n", bitmap_state(sb->state)); + printf(" Chunksize : %s\n", human_chunksize(sb->chunksize)); + printf(" Daemon : %ds flush period\n", sb->daemon_sleep); + printf(" Sync Size : %llu%s\n", sb->sync_size, + human_size(sb->sync_size * 1024)); + if (brief) + goto free_info; + printf(" Bitmap : %llu bits (chunks), %llu dirty (%2.1f%%)\n", + info->total_bits, info->dirty_bits, + 100.0 * info->dirty_bits / (info->total_bits + 1)); +free_info: + free(info); + return rv; +} + +int CreateBitmap(char *filename, int force, char uuid[16], + unsigned long chunksize, unsigned long daemon_sleep, + unsigned long long array_size) +{ + /* + * Create a bitmap file with a superblock and (optionally) a full bitmap + */ + + FILE *fp; + int rv = 1; + char block[512]; + bitmap_super_t sb; + long long bytes, filesize; + + if (!force && access(filename, F_OK) == 0) { + fprintf(stderr, Name ": bitmap file %s already exists, use --force to overwrite\n", filename); + return rv; + } + + fp = fopen(filename, "w"); + if (fp == NULL) { + fprintf(stderr, Name ": failed to open bitmap file %s: %s\n", + filename, strerror(errno)); + return rv; + } + + memset(&sb, 0, sizeof(sb)); + sb.magic = BITMAP_MAGIC; + sb.version = BITMAP_MAJOR; + if (uuid != NULL) + memcpy(sb.uuid, uuid, 16); + sb.chunksize = chunksize; + sb.daemon_sleep = daemon_sleep; + sb.sync_size = array_size; + + sb_cpu_to_le(&sb); /* convert to on-disk byte ordering */ + + if (fwrite(&sb, sizeof(sb), 1, fp) != 1) { + fprintf(stderr, Name ": failed to write superblock to bitmap file %s: %s\n", filename, strerror(errno)); + goto out; + } + + /* calculate the size of the bitmap and write it to disk */ + bytes = (bitmap_bits(array_size, chunksize) + 7) / 8; + if (!bytes) { + rv = 0; + goto out; + } + + filesize = bytes + sizeof(sb); + + memset(block, 0xff, sizeof(block)); + + while (bytes > 0) { + if (fwrite(block, sizeof(block), 1, fp) != 1) { + fprintf(stderr, Name ": failed to write bitmap file %s: %s\n", filename, strerror(errno)); + goto out; + } + bytes -= sizeof(block); + } + + rv = 0; + /* make the file be the right size (well, to the nearest byte) */ + ftruncate(fileno(fp), filesize); +out: + fclose(fp); + if (rv) + unlink(filename); /* possibly corrupted, better get rid of it */ + return rv; +} |