linux/drivers/md/dm-stripe.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2001-2003 Sistina Software (UK) Limited.
   3 *
   4 * This file is released under the GPL.
   5 */
   6
   7#include "dm.h"
   8
   9#include <linux/module.h>
  10#include <linux/init.h>
  11#include <linux/blkdev.h>
  12#include <linux/bio.h>
  13#include <linux/slab.h>
  14#include <linux/log2.h>
  15
  16#define DM_MSG_PREFIX "striped"
  17
  18struct stripe {
  19        struct dm_dev *dev;
  20        sector_t physical_start;
  21};
  22
  23struct stripe_c {
  24        uint32_t stripes;
  25
  26        /* The size of this target / num. stripes */
  27        sector_t stripe_width;
  28
  29        /* stripe chunk size */
  30        uint32_t chunk_shift;
  31        sector_t chunk_mask;
  32
  33        struct stripe stripe[0];
  34};
  35
  36static inline struct stripe_c *alloc_context(unsigned int stripes)
  37{
  38        size_t len;
  39
  40        if (array_too_big(sizeof(struct stripe_c), sizeof(struct stripe),
  41                          stripes))
  42                return NULL;
  43
  44        len = sizeof(struct stripe_c) + (sizeof(struct stripe) * stripes);
  45
  46        return kmalloc(len, GFP_KERNEL);
  47}
  48
  49/*
  50 * Parse a single <dev> <sector> pair
  51 */
  52static int get_stripe(struct dm_target *ti, struct stripe_c *sc,
  53                      unsigned int stripe, char **argv)
  54{
  55        unsigned long long start;
  56
  57        if (sscanf(argv[1], "%llu", &start) != 1)
  58                return -EINVAL;
  59
  60        if (dm_get_device(ti, argv[0], start, sc->stripe_width,
  61                          dm_table_get_mode(ti->table),
  62                          &sc->stripe[stripe].dev))
  63                return -ENXIO;
  64
  65        sc->stripe[stripe].physical_start = start;
  66        return 0;
  67}
  68
  69/*
  70 * Construct a striped mapping.
  71 * <number of stripes> <chunk size (2^^n)> [<dev_path> <offset>]+
  72 */
  73static int stripe_ctr(struct dm_target *ti, unsigned int argc, char **argv)
  74{
  75        struct stripe_c *sc;
  76        sector_t width;
  77        uint32_t stripes;
  78        uint32_t chunk_size;
  79        char *end;
  80        int r;
  81        unsigned int i;
  82
  83        if (argc < 2) {
  84                ti->error = "Not enough arguments";
  85                return -EINVAL;
  86        }
  87
  88        stripes = simple_strtoul(argv[0], &end, 10);
  89        if (*end) {
  90                ti->error = "Invalid stripe count";
  91                return -EINVAL;
  92        }
  93
  94        chunk_size = simple_strtoul(argv[1], &end, 10);
  95        if (*end) {
  96                ti->error = "Invalid chunk_size";
  97                return -EINVAL;
  98        }
  99
 100        /*
 101         * chunk_size is a power of two
 102         */
 103        if (!is_power_of_2(chunk_size) ||
 104            (chunk_size < (PAGE_SIZE >> SECTOR_SHIFT))) {
 105                ti->error = "Invalid chunk size";
 106                return -EINVAL;
 107        }
 108
 109        if (ti->len & (chunk_size - 1)) {
 110                ti->error = "Target length not divisible by "
 111                    "chunk size";
 112                return -EINVAL;
 113        }
 114
 115        width = ti->len;
 116        if (sector_div(width, stripes)) {
 117                ti->error = "Target length not divisible by "
 118                    "number of stripes";
 119                return -EINVAL;
 120        }
 121
 122        /*
 123         * Do we have enough arguments for that many stripes ?
 124         */
 125        if (argc != (2 + 2 * stripes)) {
 126                ti->error = "Not enough destinations "
 127                        "specified";
 128                return -EINVAL;
 129        }
 130
 131        sc = alloc_context(stripes);
 132        if (!sc) {
 133                ti->error = "Memory allocation for striped context "
 134                    "failed";
 135                return -ENOMEM;
 136        }
 137
 138        sc->stripes = stripes;
 139        sc->stripe_width = width;
 140        ti->split_io = chunk_size;
 141
 142        sc->chunk_mask = ((sector_t) chunk_size) - 1;
 143        for (sc->chunk_shift = 0; chunk_size; sc->chunk_shift++)
 144                chunk_size >>= 1;
 145        sc->chunk_shift--;
 146
 147        /*
 148         * Get the stripe destinations.
 149         */
 150        for (i = 0; i < stripes; i++) {
 151                argv += 2;
 152
 153                r = get_stripe(ti, sc, i, argv);
 154                if (r < 0) {
 155                        ti->error = "Couldn't parse stripe destination";
 156                        while (i--)
 157                                dm_put_device(ti, sc->stripe[i].dev);
 158                        kfree(sc);
 159                        return r;
 160                }
 161        }
 162
 163        ti->private = sc;
 164        return 0;
 165}
 166
 167static void stripe_dtr(struct dm_target *ti)
 168{
 169        unsigned int i;
 170        struct stripe_c *sc = (struct stripe_c *) ti->private;
 171
 172        for (i = 0; i < sc->stripes; i++)
 173                dm_put_device(ti, sc->stripe[i].dev);
 174
 175        kfree(sc);
 176}
 177
 178static int stripe_map(struct dm_target *ti, struct bio *bio,
 179                      union map_info *map_context)
 180{
 181        struct stripe_c *sc = (struct stripe_c *) ti->private;
 182
 183        sector_t offset = bio->bi_sector - ti->begin;
 184        sector_t chunk = offset >> sc->chunk_shift;
 185        uint32_t stripe = sector_div(chunk, sc->stripes);
 186
 187        bio->bi_bdev = sc->stripe[stripe].dev->bdev;
 188        bio->bi_sector = sc->stripe[stripe].physical_start +
 189            (chunk << sc->chunk_shift) + (offset & sc->chunk_mask);
 190        return DM_MAPIO_REMAPPED;
 191}
 192
 193static int stripe_status(struct dm_target *ti,
 194                         status_type_t type, char *result, unsigned int maxlen)
 195{
 196        struct stripe_c *sc = (struct stripe_c *) ti->private;
 197        unsigned int sz = 0;
 198        unsigned int i;
 199
 200        switch (type) {
 201        case STATUSTYPE_INFO:
 202                result[0] = '\0';
 203                break;
 204
 205        case STATUSTYPE_TABLE:
 206                DMEMIT("%d %llu", sc->stripes,
 207                        (unsigned long long)sc->chunk_mask + 1);
 208                for (i = 0; i < sc->stripes; i++)
 209                        DMEMIT(" %s %llu", sc->stripe[i].dev->name,
 210                            (unsigned long long)sc->stripe[i].physical_start);
 211                break;
 212        }
 213        return 0;
 214}
 215
 216static struct target_type stripe_target = {
 217        .name   = "striped",
 218        .version= {1, 0, 2},
 219        .module = THIS_MODULE,
 220        .ctr    = stripe_ctr,
 221        .dtr    = stripe_dtr,
 222        .map    = stripe_map,
 223        .status = stripe_status,
 224};
 225
 226int __init dm_stripe_init(void)
 227{
 228        int r;
 229
 230        r = dm_register_target(&stripe_target);
 231        if (r < 0)
 232                DMWARN("target registration failed");
 233
 234        return r;
 235}
 236
 237void dm_stripe_exit(void)
 238{
 239        if (dm_unregister_target(&stripe_target))
 240                DMWARN("target unregistration failed");
 241
 242        return;
 243}
 244