From 469e594bd236651614ca136725c0d5ea0bd4d6a5 Mon Sep 17 00:00:00 2001 From: Krzysztof Mazur Date: Thu, 16 Dec 2010 19:31:11 +0100 Subject: [PATCH 04/84] lsbd: initial support for partitions --- drivers/block/lsbd.c | 196 ++++++++++++++++++++++++++++++++++++++++++++++++--- include/linux/lsbd.h | 20 +++++- 2 files changed, 204 insertions(+), 12 deletions(-) diff --git a/drivers/block/lsbd.c b/drivers/block/lsbd.c index 0ff42b6..e5197ae 100644 --- a/drivers/block/lsbd.c +++ b/drivers/block/lsbd.c @@ -41,6 +41,11 @@ #define LSBD_MAX 8 #define PART_BITS 5 +struct __block_test { + int a[(sizeof(struct lsbd_block) == 1024) ? 1 : -1]; +}; + + enum { LSBD_QUEUE_MOVE, LSBD_QUEUE_HIGH, @@ -48,6 +53,11 @@ enum { LSBD_QUEUE_COUNT, /* must be last */ }; +struct lsbd_partition { + unsigned int start; + unsigned int size; +}; + struct lsbd { unsigned int id; kdev_t dev; @@ -72,7 +82,9 @@ struct lsbd { wait_queue_head_t wqueue_wait; unsigned int wqueue_len; struct semaphore req_sem; - + + struct lsbd_partition part[16]; + int stop; struct task_struct *tsk; struct completion *event; @@ -85,7 +97,7 @@ struct lsbd_request { int move; }; -#if 0 +#if 1 #define lsbd_debug(c, fmt, ...) \ printk(KERN_DEBUG "lsbd%d: "fmt, (c)->id, ## __VA_ARGS__) #else @@ -120,6 +132,22 @@ unsigned int block_diff(struct lsbd *p, unsigned int a, unsigned int b) return p->blocks + a - b; } +static unsigned int lsbd_map_sector(struct lsbd *p, unsigned int partition, + unsigned int sector) +{ + if (!partition) { + if (sector >= p->lsectors) + return LSBD_SECT_INVALID; + return sector; + } + + BUG_ON(partition > 16); + if (sector >= p->part[partition].size) + return LSBD_SECT_INVALID; + + return sector + p->part[partition].start; +} + static int lsbd_open(struct inode *inode, struct file *file) { int minor; @@ -205,10 +233,6 @@ static int lsbd_block_commit(struct lsbd *p, struct lsbd_block *b) return 0; } -struct __block_test { - int a[(sizeof(struct lsbd_block) == 1024) ? 1 : -1]; -}; - /* * lsbd_find_current_block - find current block */ @@ -409,6 +433,57 @@ int lsbd_read_lcache(struct lsbd *p) return 0; } +static int lsbd_update_part(struct lsbd *p, unsigned int part) +{ + lsbd_debug(p, "partition %d: start %d MiB, size %d MiB\n", part, + p->part[part].start / (1048576 / p->sector_size), + p->part[part].size / (1048576 / p->sector_size)); + lsbd_sizes[(p->id >> PART_BITS) + part] = p->part[part].size + * (p->sector_size >> 10); + return 0; +} + +static int lsbd_load_partitions(struct lsbd *p, struct lsbd_block *b) +{ + unsigned int start; + unsigned int size; + unsigned int i; + + for (i = 0; i < 16; i++) { + p->part[i].start = 0; + p->part[i].size = 0; + } + + for (i = 1; i < 16; i++) { + start = be32_to_cpu(b->part[i].start_flags) & LSBD_PART_MASK; + size = be32_to_cpu(b->part[i].size_res) & LSBD_PART_MASK; + + if ((u64) start + size > p->lsectors) { + lsbd_error(p, "invalid partition %d: start %d MiB, " + "size %d MiB\n", + i, start / (1048576 / p->sector_size), + size / (1048576 / p->sector_size)); + start = 0; + size = 0; + } + p->part[i].start = start; + p->part[i].size = size; + lsbd_update_part(p, i); + } + return 0; +} + +static int lsbd_write_partitions(struct lsbd *p, struct lsbd_block *b) +{ + unsigned int i; + + for (i = 0; i < 16; i++) { + b->part[i].start_flags = cpu_to_be32(p->part[i].start); + b->part[i].size_res = cpu_to_be32(p->part[i].size); + } + return 0; +} + static int lsbd_load_params(struct lsbd *p) { struct buffer_head *bh; @@ -425,12 +500,14 @@ static int lsbd_load_params(struct lsbd *p) i = 100; for (; i > 0; i--, block = block_prev(p, block)) { - bh = lsbd_bread(p, i, sizeof(*b)); + lsbd_debug(p, "params: block %d\n", block); + bh = lsbd_bread(p, block, p->sector_size); if (bh == NULL) continue; b = (void *) bh->b_data; if (!lsbd_block_verify_ok(p, b)) { + lsbd_debug(p, "params: block %d failed\n", block); brelse(bh); continue; } @@ -442,12 +519,11 @@ static int lsbd_load_params(struct lsbd *p) if (!p->lsectors) p->lsectors = p->blocks; - brelse(bh); - if ((sector_size & (sector_size - 1)) || (sector_size < 4096)) { lsbd_error(p, "sector_size=%d must be a power of two " "and larger than 4 KiB\n", sector_size); + brelse(bh); return -EINVAL; } @@ -466,6 +542,7 @@ static int lsbd_load_params(struct lsbd *p) lsbd_error(p, "refusing to start partial image %d " "avail, %d total\n", p->blocks, blocks); + brelse(bh); return -EINVAL; } @@ -474,6 +551,9 @@ static int lsbd_load_params(struct lsbd *p) p->blocks * p->sectors_per_block * (p->sector_size >> 10)); lsbd_sizes[p->id] = p->lsectors * (p->sector_size >> 10); + + lsbd_load_partitions(p, b); + brelse(bh); return 0; } return 1; @@ -560,6 +640,69 @@ static int lsbd_set_dev(struct lsbd *p, int major, int minor) return lsbd_mount(p); } + +static int lsbd_partition(struct lsbd *p, struct lsbd_part_info *part) +{ + lsbd_debug(p, "part: %d %d %d %d\n", part->num, part->flags, + part->start, part->size); + if (part->num > 16) + return -EINVAL; + + if (part->flags) + return -EINVAL; + + if (!part->num) { + unsigned int sectors; + + if (part->start) + return -EINVAL; + + sectors = p->blocks * (p->sectors_per_block - 1); + if (sectors < 1024) + sectors += 128; + + /* don't allow more than 7/8 utilization */ + if (part->size / 7 > sectors / 8) + return -EINVAL; + + if (part->size > p->lsectors) { + lsbd_lcache_t *l; + lsbd_lcache_t *ol; + unsigned int i; + + l = vmalloc(part->size * sizeof(l[0])); + if (l == NULL) + return -ENOMEM; + for (i = p->lsectors; i < part->size; i++) + l[i] = LSBD_SECT_ZERO; + ol = p->lcache; + p->lcache = l; + + /* + * FIXME: implement lcache locking + */ + schedule_timeout(10); + vfree(ol); + } + + p->lsectors = part->size; + return 0; + } + + if ((u64) part->start + part->size > p->lsectors) + return -EINVAL; + + if (part->start & ~LSBD_PART_MASK) + return -EINVAL; + + if (part->size & ~LSBD_PART_MASK) + return -EINVAL; + + p->part[part->num].start = part->start; + p->part[part->num].size = part->size; + lsbd_update_part(p, part->num); + return 0; +} static int lsbd_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) @@ -591,6 +734,17 @@ static int lsbd_ioctl(struct inode *inode, struct file *file, err = lsbd_set_dev(p, major, minor); break; } + case LSBD_PART: + { + struct lsbd_part_info part; + + if (copy_from_user(&part, (void *) arg, sizeof(part))) { + err = -EFAULT; + } else { + err = lsbd_partition(p, &part); + } + break; + } case BLKGETSIZE: err = put_user((unsigned long) lsbd_sizes[minor] << 1, (unsigned long *) arg); @@ -598,9 +752,15 @@ static int lsbd_ioctl(struct inode *inode, struct file *file, case BLKGETSIZE64: err = put_user((u64) lsbd_sizes[minor] << 10, (u64 *) arg); break; + case BLKROSET: + case BLKROGET: + case BLKRASET: + case BLKRAGET: + case BLKFLSBUF: + case BLKSSZGET: + case BLKPG: case BLKBSZGET: case BLKBSZSET: - case BLKSSZGET: err = blk_ioctl(inode->i_rdev, cmd, arg); break; default: @@ -665,10 +825,13 @@ static int lsbd_queue_bh(struct lsbd *p, struct buffer_head *bh, return 0; } +#define lsbd_partition(x) ((x) & ((1 << PART_BITS) - 1)) + static int lsbd_make_request(request_queue_t *q, int rw, struct buffer_head *bh) { struct lsbd *p = &lsbd_dev[MINOR(bh->b_rdev) >> PART_BITS]; + unsigned int partition = lsbd_partition(MINOR(bh->b_rdev)); unsigned int lsector = LSBD_SECT_INVALID; if (rw == READA || rw == READ) { @@ -679,10 +842,13 @@ static int lsbd_make_request(request_queue_t *q, int rw, bh->b_rsector); lsbd_debug(p, "blocknr %ld, size %hd\n", bh->b_blocknr, bh->b_size); - if (lsector >= p->lsectors) { + + lsector = lsbd_map_sector(p, partition, lsector); + if (lsector == LSBD_SECT_INVALID) { buffer_IO_error(bh); return 0; } + sector = p->lcache[lsector]; lsbd_debug(p, "mapped to sector %d\n", sector); if (sector == LSBD_SECT_INVALID) { @@ -709,6 +875,13 @@ static int lsbd_make_request(request_queue_t *q, int rw, if (rw == WRITE) { lsector = bh->b_rsector >> 3; + + lsector = lsbd_map_sector(p, partition, lsector); + if (lsector == LSBD_SECT_INVALID) { + buffer_IO_error(bh); + return 0; + } + if (lsbd_queue_bh(p, bh, lsector, LSBD_QUEUE_NORMAL)) buffer_IO_error(bh); return 0; @@ -957,6 +1130,7 @@ int lsbd_write_block(struct lsbd *p) * sizeof(struct lsbd_sect))); lsbd_write_lcache(p, b); + lsbd_write_partitions(p, b); lsbd_block_commit(p, b); for (i = 0; i < p->sectors_per_block; i++) { mark_buffer_uptodate(bh[i], 1); diff --git a/include/linux/lsbd.h b/include/linux/lsbd.h index 0f2f4ec..5c101c8 100644 --- a/include/linux/lsbd.h +++ b/include/linux/lsbd.h @@ -23,8 +23,23 @@ #include #define LSBD_SET_DEV _IO('l', 0x0) +#define LSBD_PART _IO('l', 0x1) + +struct lsbd_part_info { + __u32 num; + __u32 start; + __u32 size; + __u32 flags; +} __attribute__((packed)); #ifdef __KERNEL__ +#define LSBD_PART_MASK 0xffffff00 +#define LSBD_PART_FLAGS_MASK 0xff +struct lsbd_part { + u32 start_flags; + u32 size_res; +} __attribute__((packed)); + #define LSBD_BLOCK_MAGIC 0x4c534244AA1122FFULL struct lsbd_block { u64 magic; /* 0x00 block magic */ @@ -57,9 +72,12 @@ struct lsbd_block { u32 lcache_base; /* 0x94 first logical sector in this cache */ u32 lcache_chunk; /* 0x98 number of entries per lcache */ u32 lcache_checksum; /* 0x9c lcache content checksum */ + + u32 pad2[(0x100 - 0xa0) / 4]; /* 0xc0 - reserved */ + struct lsbd_part part[16]; /* 0x100-0x17f partition table */ - u32 reserved[256 - 0xa0/4 - 1]; /* 0x100 */ + u32 pad3[256 - 0x180/4 - 1]; /* 0xc0 */ u32 checksum; } __attribute__((packed)); -- 1.8.4.652.g0d6e0ce