linux/drivers/md/persistent-data/dm-space-map-disk.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2011 Red Hat, Inc.
   3 *
   4 * This file is released under the GPL.
   5 */
   6
   7#include "dm-space-map-checker.h"
   8#include "dm-space-map-common.h"
   9#include "dm-space-map-disk.h"
  10#include "dm-space-map.h"
  11#include "dm-transaction-manager.h"
  12
  13#include <linux/list.h>
  14#include <linux/slab.h>
  15#include <linux/export.h>
  16#include <linux/device-mapper.h>
  17
  18#define DM_MSG_PREFIX "space map disk"
  19
  20/*----------------------------------------------------------------*/
  21
  22/*
  23 * Space map interface.
  24 */
  25struct sm_disk {
  26        struct dm_space_map sm;
  27
  28        struct ll_disk ll;
  29        struct ll_disk old_ll;
  30
  31        dm_block_t begin;
  32        dm_block_t nr_allocated_this_transaction;
  33};
  34
  35static void sm_disk_destroy(struct dm_space_map *sm)
  36{
  37        struct sm_disk *smd = container_of(sm, struct sm_disk, sm);
  38
  39        kfree(smd);
  40}
  41
  42static int sm_disk_extend(struct dm_space_map *sm, dm_block_t extra_blocks)
  43{
  44        struct sm_disk *smd = container_of(sm, struct sm_disk, sm);
  45
  46        return sm_ll_extend(&smd->ll, extra_blocks);
  47}
  48
  49static int sm_disk_get_nr_blocks(struct dm_space_map *sm, dm_block_t *count)
  50{
  51        struct sm_disk *smd = container_of(sm, struct sm_disk, sm);
  52        *count = smd->old_ll.nr_blocks;
  53
  54        return 0;
  55}
  56
  57static int sm_disk_get_nr_free(struct dm_space_map *sm, dm_block_t *count)
  58{
  59        struct sm_disk *smd = container_of(sm, struct sm_disk, sm);
  60        *count = (smd->old_ll.nr_blocks - smd->old_ll.nr_allocated) - smd->nr_allocated_this_transaction;
  61
  62        return 0;
  63}
  64
  65static int sm_disk_get_count(struct dm_space_map *sm, dm_block_t b,
  66                             uint32_t *result)
  67{
  68        struct sm_disk *smd = container_of(sm, struct sm_disk, sm);
  69        return sm_ll_lookup(&smd->ll, b, result);
  70}
  71
  72static int sm_disk_count_is_more_than_one(struct dm_space_map *sm, dm_block_t b,
  73                                          int *result)
  74{
  75        int r;
  76        uint32_t count;
  77
  78        r = sm_disk_get_count(sm, b, &count);
  79        if (r)
  80                return r;
  81
  82        return count > 1;
  83}
  84
  85static int sm_disk_set_count(struct dm_space_map *sm, dm_block_t b,
  86                             uint32_t count)
  87{
  88        int r;
  89        uint32_t old_count;
  90        enum allocation_event ev;
  91        struct sm_disk *smd = container_of(sm, struct sm_disk, sm);
  92
  93        r = sm_ll_insert(&smd->ll, b, count, &ev);
  94        if (!r) {
  95                switch (ev) {
  96                case SM_NONE:
  97                        break;
  98
  99                case SM_ALLOC:
 100                        /*
 101                         * This _must_ be free in the prior transaction
 102                         * otherwise we've lost atomicity.
 103                         */
 104                        smd->nr_allocated_this_transaction++;
 105                        break;
 106
 107                case SM_FREE:
 108                        /*
 109                         * It's only free if it's also free in the last
 110                         * transaction.
 111                         */
 112                        r = sm_ll_lookup(&smd->old_ll, b, &old_count);
 113                        if (r)
 114                                return r;
 115
 116                        if (!old_count)
 117                                smd->nr_allocated_this_transaction--;
 118                        break;
 119                }
 120        }
 121
 122        return r;
 123}
 124
 125static int sm_disk_inc_block(struct dm_space_map *sm, dm_block_t b)
 126{
 127        int r;
 128        enum allocation_event ev;
 129        struct sm_disk *smd = container_of(sm, struct sm_disk, sm);
 130
 131        r = sm_ll_inc(&smd->ll, b, &ev);
 132        if (!r && (ev == SM_ALLOC))
 133                /*
 134                 * This _must_ be free in the prior transaction
 135                 * otherwise we've lost atomicity.
 136                 */
 137                smd->nr_allocated_this_transaction++;
 138
 139        return r;
 140}
 141
 142static int sm_disk_dec_block(struct dm_space_map *sm, dm_block_t b)
 143{
 144        int r;
 145        uint32_t old_count;
 146        enum allocation_event ev;
 147        struct sm_disk *smd = container_of(sm, struct sm_disk, sm);
 148
 149        r = sm_ll_dec(&smd->ll, b, &ev);
 150        if (!r && (ev == SM_FREE)) {
 151                /*
 152                 * It's only free if it's also free in the last
 153                 * transaction.
 154                 */
 155                r = sm_ll_lookup(&smd->old_ll, b, &old_count);
 156                if (r)
 157                        return r;
 158
 159                if (!old_count)
 160                        smd->nr_allocated_this_transaction--;
 161        }
 162
 163        return r;
 164}
 165
 166static int sm_disk_new_block(struct dm_space_map *sm, dm_block_t *b)
 167{
 168        int r;
 169        enum allocation_event ev;
 170        struct sm_disk *smd = container_of(sm, struct sm_disk, sm);
 171
 172        /* FIXME: we should loop round a couple of times */
 173        r = sm_ll_find_free_block(&smd->old_ll, smd->begin, smd->old_ll.nr_blocks, b);
 174        if (r)
 175                return r;
 176
 177        smd->begin = *b + 1;
 178        r = sm_ll_inc(&smd->ll, *b, &ev);
 179        if (!r) {
 180                BUG_ON(ev != SM_ALLOC);
 181                smd->nr_allocated_this_transaction++;
 182        }
 183
 184        return r;
 185}
 186
 187static int sm_disk_commit(struct dm_space_map *sm)
 188{
 189        int r;
 190        dm_block_t nr_free;
 191        struct sm_disk *smd = container_of(sm, struct sm_disk, sm);
 192
 193        r = sm_disk_get_nr_free(sm, &nr_free);
 194        if (r)
 195                return r;
 196
 197        r = sm_ll_commit(&smd->ll);
 198        if (r)
 199                return r;
 200
 201        memcpy(&smd->old_ll, &smd->ll, sizeof(smd->old_ll));
 202        smd->begin = 0;
 203        smd->nr_allocated_this_transaction = 0;
 204
 205        r = sm_disk_get_nr_free(sm, &nr_free);
 206        if (r)
 207                return r;
 208
 209        return 0;
 210}
 211
 212static int sm_disk_root_size(struct dm_space_map *sm, size_t *result)
 213{
 214        *result = sizeof(struct disk_sm_root);
 215
 216        return 0;
 217}
 218
 219static int sm_disk_copy_root(struct dm_space_map *sm, void *where_le, size_t max)
 220{
 221        struct sm_disk *smd = container_of(sm, struct sm_disk, sm);
 222        struct disk_sm_root root_le;
 223
 224        root_le.nr_blocks = cpu_to_le64(smd->ll.nr_blocks);
 225        root_le.nr_allocated = cpu_to_le64(smd->ll.nr_allocated);
 226        root_le.bitmap_root = cpu_to_le64(smd->ll.bitmap_root);
 227        root_le.ref_count_root = cpu_to_le64(smd->ll.ref_count_root);
 228
 229        if (max < sizeof(root_le))
 230                return -ENOSPC;
 231
 232        memcpy(where_le, &root_le, sizeof(root_le));
 233
 234        return 0;
 235}
 236
 237/*----------------------------------------------------------------*/
 238
 239static struct dm_space_map ops = {
 240        .destroy = sm_disk_destroy,
 241        .extend = sm_disk_extend,
 242        .get_nr_blocks = sm_disk_get_nr_blocks,
 243        .get_nr_free = sm_disk_get_nr_free,
 244        .get_count = sm_disk_get_count,
 245        .count_is_more_than_one = sm_disk_count_is_more_than_one,
 246        .set_count = sm_disk_set_count,
 247        .inc_block = sm_disk_inc_block,
 248        .dec_block = sm_disk_dec_block,
 249        .new_block = sm_disk_new_block,
 250        .commit = sm_disk_commit,
 251        .root_size = sm_disk_root_size,
 252        .copy_root = sm_disk_copy_root
 253};
 254
 255static struct dm_space_map *dm_sm_disk_create_real(
 256        struct dm_transaction_manager *tm,
 257        dm_block_t nr_blocks)
 258{
 259        int r;
 260        struct sm_disk *smd;
 261
 262        smd = kmalloc(sizeof(*smd), GFP_KERNEL);
 263        if (!smd)
 264                return ERR_PTR(-ENOMEM);
 265
 266        smd->begin = 0;
 267        smd->nr_allocated_this_transaction = 0;
 268        memcpy(&smd->sm, &ops, sizeof(smd->sm));
 269
 270        r = sm_ll_new_disk(&smd->ll, tm);
 271        if (r)
 272                goto bad;
 273
 274        r = sm_ll_extend(&smd->ll, nr_blocks);
 275        if (r)
 276                goto bad;
 277
 278        r = sm_disk_commit(&smd->sm);
 279        if (r)
 280                goto bad;
 281
 282        return &smd->sm;
 283
 284bad:
 285        kfree(smd);
 286        return ERR_PTR(r);
 287}
 288
 289struct dm_space_map *dm_sm_disk_create(struct dm_transaction_manager *tm,
 290                                       dm_block_t nr_blocks)
 291{
 292        struct dm_space_map *sm = dm_sm_disk_create_real(tm, nr_blocks);
 293        return dm_sm_checker_create_fresh(sm);
 294}
 295EXPORT_SYMBOL_GPL(dm_sm_disk_create);
 296
 297static struct dm_space_map *dm_sm_disk_open_real(
 298        struct dm_transaction_manager *tm,
 299        void *root_le, size_t len)
 300{
 301        int r;
 302        struct sm_disk *smd;
 303
 304        smd = kmalloc(sizeof(*smd), GFP_KERNEL);
 305        if (!smd)
 306                return ERR_PTR(-ENOMEM);
 307
 308        smd->begin = 0;
 309        smd->nr_allocated_this_transaction = 0;
 310        memcpy(&smd->sm, &ops, sizeof(smd->sm));
 311
 312        r = sm_ll_open_disk(&smd->ll, tm, root_le, len);
 313        if (r)
 314                goto bad;
 315
 316        r = sm_disk_commit(&smd->sm);
 317        if (r)
 318                goto bad;
 319
 320        return &smd->sm;
 321
 322bad:
 323        kfree(smd);
 324        return ERR_PTR(r);
 325}
 326
 327struct dm_space_map *dm_sm_disk_open(struct dm_transaction_manager *tm,
 328                                     void *root_le, size_t len)
 329{
 330        return dm_sm_checker_create(
 331                dm_sm_disk_open_real(tm, root_le, len));
 332}
 333EXPORT_SYMBOL_GPL(dm_sm_disk_open);
 334
 335/*----------------------------------------------------------------*/
 336