linux/fs/pstore/ram_core.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2012 Google, Inc.
   3 *
   4 * This software is licensed under the terms of the GNU General Public
   5 * License version 2, as published by the Free Software Foundation, and
   6 * may be copied, distributed, and modified under those terms.
   7 *
   8 * This program is distributed in the hope that it will be useful,
   9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  11 * GNU General Public License for more details.
  12 *
  13 */
  14
  15#include <linux/device.h>
  16#include <linux/err.h>
  17#include <linux/errno.h>
  18#include <linux/kernel.h>
  19#include <linux/init.h>
  20#include <linux/io.h>
  21#include <linux/list.h>
  22#include <linux/memblock.h>
  23#include <linux/rslib.h>
  24#include <linux/slab.h>
  25#include <linux/vmalloc.h>
  26#include <linux/pstore_ram.h>
  27#include <asm/page.h>
  28
  29struct persistent_ram_buffer {
  30        uint32_t    sig;
  31        atomic_t    start;
  32        atomic_t    size;
  33        uint8_t     data[0];
  34};
  35
  36#define PERSISTENT_RAM_SIG (0x43474244) /* DBGC */
  37
  38static inline size_t buffer_size(struct persistent_ram_zone *prz)
  39{
  40        return atomic_read(&prz->buffer->size);
  41}
  42
  43static inline size_t buffer_start(struct persistent_ram_zone *prz)
  44{
  45        return atomic_read(&prz->buffer->start);
  46}
  47
  48/* increase and wrap the start pointer, returning the old value */
  49static inline size_t buffer_start_add(struct persistent_ram_zone *prz, size_t a)
  50{
  51        int old;
  52        int new;
  53
  54        do {
  55                old = atomic_read(&prz->buffer->start);
  56                new = old + a;
  57                while (unlikely(new > prz->buffer_size))
  58                        new -= prz->buffer_size;
  59        } while (atomic_cmpxchg(&prz->buffer->start, old, new) != old);
  60
  61        return old;
  62}
  63
  64/* increase the size counter until it hits the max size */
  65static inline void buffer_size_add(struct persistent_ram_zone *prz, size_t a)
  66{
  67        size_t old;
  68        size_t new;
  69
  70        if (atomic_read(&prz->buffer->size) == prz->buffer_size)
  71                return;
  72
  73        do {
  74                old = atomic_read(&prz->buffer->size);
  75                new = old + a;
  76                if (new > prz->buffer_size)
  77                        new = prz->buffer_size;
  78        } while (atomic_cmpxchg(&prz->buffer->size, old, new) != old);
  79}
  80
  81static void notrace persistent_ram_encode_rs8(struct persistent_ram_zone *prz,
  82        uint8_t *data, size_t len, uint8_t *ecc)
  83{
  84        int i;
  85        uint16_t par[prz->ecc_size];
  86
  87        /* Initialize the parity buffer */
  88        memset(par, 0, sizeof(par));
  89        encode_rs8(prz->rs_decoder, data, len, par, 0);
  90        for (i = 0; i < prz->ecc_size; i++)
  91                ecc[i] = par[i];
  92}
  93
  94static int persistent_ram_decode_rs8(struct persistent_ram_zone *prz,
  95        void *data, size_t len, uint8_t *ecc)
  96{
  97        int i;
  98        uint16_t par[prz->ecc_size];
  99
 100        for (i = 0; i < prz->ecc_size; i++)
 101                par[i] = ecc[i];
 102        return decode_rs8(prz->rs_decoder, data, par, len,
 103                                NULL, 0, NULL, 0, NULL);
 104}
 105
 106static void notrace persistent_ram_update_ecc(struct persistent_ram_zone *prz,
 107        unsigned int start, unsigned int count)
 108{
 109        struct persistent_ram_buffer *buffer = prz->buffer;
 110        uint8_t *buffer_end = buffer->data + prz->buffer_size;
 111        uint8_t *block;
 112        uint8_t *par;
 113        int ecc_block_size = prz->ecc_block_size;
 114        int ecc_size = prz->ecc_size;
 115        int size = prz->ecc_block_size;
 116
 117        if (!prz->ecc_size)
 118                return;
 119
 120        block = buffer->data + (start & ~(ecc_block_size - 1));
 121        par = prz->par_buffer + (start / ecc_block_size) * prz->ecc_size;
 122
 123        do {
 124                if (block + ecc_block_size > buffer_end)
 125                        size = buffer_end - block;
 126                persistent_ram_encode_rs8(prz, block, size, par);
 127                block += ecc_block_size;
 128                par += ecc_size;
 129        } while (block < buffer->data + start + count);
 130}
 131
 132static void persistent_ram_update_header_ecc(struct persistent_ram_zone *prz)
 133{
 134        struct persistent_ram_buffer *buffer = prz->buffer;
 135
 136        if (!prz->ecc_size)
 137                return;
 138
 139        persistent_ram_encode_rs8(prz, (uint8_t *)buffer, sizeof(*buffer),
 140                                  prz->par_header);
 141}
 142
 143static void persistent_ram_ecc_old(struct persistent_ram_zone *prz)
 144{
 145        struct persistent_ram_buffer *buffer = prz->buffer;
 146        uint8_t *block;
 147        uint8_t *par;
 148
 149        if (!prz->ecc_size)
 150                return;
 151
 152        block = buffer->data;
 153        par = prz->par_buffer;
 154        while (block < buffer->data + buffer_size(prz)) {
 155                int numerr;
 156                int size = prz->ecc_block_size;
 157                if (block + size > buffer->data + prz->buffer_size)
 158                        size = buffer->data + prz->buffer_size - block;
 159                numerr = persistent_ram_decode_rs8(prz, block, size, par);
 160                if (numerr > 0) {
 161                        pr_devel("persistent_ram: error in block %p, %d\n",
 162                               block, numerr);
 163                        prz->corrected_bytes += numerr;
 164                } else if (numerr < 0) {
 165                        pr_devel("persistent_ram: uncorrectable error in block %p\n",
 166                                block);
 167                        prz->bad_blocks++;
 168                }
 169                block += prz->ecc_block_size;
 170                par += prz->ecc_size;
 171        }
 172}
 173
 174static int persistent_ram_init_ecc(struct persistent_ram_zone *prz,
 175                                   int ecc_size)
 176{
 177        int numerr;
 178        struct persistent_ram_buffer *buffer = prz->buffer;
 179        int ecc_blocks;
 180        size_t ecc_total;
 181        int ecc_symsize = 8;
 182        int ecc_poly = 0x11d;
 183
 184        if (!ecc_size)
 185                return 0;
 186
 187        prz->ecc_block_size = 128;
 188        prz->ecc_size = ecc_size;
 189
 190        ecc_blocks = DIV_ROUND_UP(prz->buffer_size, prz->ecc_block_size);
 191        ecc_total = (ecc_blocks + 1) * prz->ecc_size;
 192        if (ecc_total >= prz->buffer_size) {
 193                pr_err("%s: invalid ecc_size %u (total %zu, buffer size %zu)\n",
 194                       __func__, prz->ecc_size, ecc_total, prz->buffer_size);
 195                return -EINVAL;
 196        }
 197
 198        prz->buffer_size -= ecc_total;
 199        prz->par_buffer = buffer->data + prz->buffer_size;
 200        prz->par_header = prz->par_buffer + ecc_blocks * prz->ecc_size;
 201
 202        /*
 203         * first consecutive root is 0
 204         * primitive element to generate roots = 1
 205         */
 206        prz->rs_decoder = init_rs(ecc_symsize, ecc_poly, 0, 1, prz->ecc_size);
 207        if (prz->rs_decoder == NULL) {
 208                pr_info("persistent_ram: init_rs failed\n");
 209                return -EINVAL;
 210        }
 211
 212        prz->corrected_bytes = 0;
 213        prz->bad_blocks = 0;
 214
 215        numerr = persistent_ram_decode_rs8(prz, buffer, sizeof(*buffer),
 216                                           prz->par_header);
 217        if (numerr > 0) {
 218                pr_info("persistent_ram: error in header, %d\n", numerr);
 219                prz->corrected_bytes += numerr;
 220        } else if (numerr < 0) {
 221                pr_info("persistent_ram: uncorrectable error in header\n");
 222                prz->bad_blocks++;
 223        }
 224
 225        return 0;
 226}
 227
 228ssize_t persistent_ram_ecc_string(struct persistent_ram_zone *prz,
 229        char *str, size_t len)
 230{
 231        ssize_t ret;
 232
 233        if (prz->corrected_bytes || prz->bad_blocks)
 234                ret = snprintf(str, len, ""
 235                        "\n%d Corrected bytes, %d unrecoverable blocks\n",
 236                        prz->corrected_bytes, prz->bad_blocks);
 237        else
 238                ret = snprintf(str, len, "\nNo errors detected\n");
 239
 240        return ret;
 241}
 242
 243static void notrace persistent_ram_update(struct persistent_ram_zone *prz,
 244        const void *s, unsigned int start, unsigned int count)
 245{
 246        struct persistent_ram_buffer *buffer = prz->buffer;
 247        memcpy(buffer->data + start, s, count);
 248        persistent_ram_update_ecc(prz, start, count);
 249}
 250
 251void persistent_ram_save_old(struct persistent_ram_zone *prz)
 252{
 253        struct persistent_ram_buffer *buffer = prz->buffer;
 254        size_t size = buffer_size(prz);
 255        size_t start = buffer_start(prz);
 256
 257        if (!size)
 258                return;
 259
 260        if (!prz->old_log) {
 261                persistent_ram_ecc_old(prz);
 262                prz->old_log = kmalloc(size, GFP_KERNEL);
 263        }
 264        if (!prz->old_log) {
 265                pr_err("persistent_ram: failed to allocate buffer\n");
 266                return;
 267        }
 268
 269        prz->old_log_size = size;
 270        memcpy(prz->old_log, &buffer->data[start], size - start);
 271        memcpy(prz->old_log + size - start, &buffer->data[0], start);
 272}
 273
 274int notrace persistent_ram_write(struct persistent_ram_zone *prz,
 275        const void *s, unsigned int count)
 276{
 277        int rem;
 278        int c = count;
 279        size_t start;
 280
 281        if (unlikely(c > prz->buffer_size)) {
 282                s += c - prz->buffer_size;
 283                c = prz->buffer_size;
 284        }
 285
 286        buffer_size_add(prz, c);
 287
 288        start = buffer_start_add(prz, c);
 289
 290        rem = prz->buffer_size - start;
 291        if (unlikely(rem < c)) {
 292                persistent_ram_update(prz, s, start, rem);
 293                s += rem;
 294                c -= rem;
 295                start = 0;
 296        }
 297        persistent_ram_update(prz, s, start, c);
 298
 299        persistent_ram_update_header_ecc(prz);
 300
 301        return count;
 302}
 303
 304size_t persistent_ram_old_size(struct persistent_ram_zone *prz)
 305{
 306        return prz->old_log_size;
 307}
 308
 309void *persistent_ram_old(struct persistent_ram_zone *prz)
 310{
 311        return prz->old_log;
 312}
 313
 314void persistent_ram_free_old(struct persistent_ram_zone *prz)
 315{
 316        kfree(prz->old_log);
 317        prz->old_log = NULL;
 318        prz->old_log_size = 0;
 319}
 320
 321void persistent_ram_zap(struct persistent_ram_zone *prz)
 322{
 323        atomic_set(&prz->buffer->start, 0);
 324        atomic_set(&prz->buffer->size, 0);
 325        persistent_ram_update_header_ecc(prz);
 326}
 327
 328static void *persistent_ram_vmap(phys_addr_t start, size_t size)
 329{
 330        struct page **pages;
 331        phys_addr_t page_start;
 332        unsigned int page_count;
 333        pgprot_t prot;
 334        unsigned int i;
 335        void *vaddr;
 336
 337        page_start = start - offset_in_page(start);
 338        page_count = DIV_ROUND_UP(size + offset_in_page(start), PAGE_SIZE);
 339
 340        prot = pgprot_noncached(PAGE_KERNEL);
 341
 342        pages = kmalloc(sizeof(struct page *) * page_count, GFP_KERNEL);
 343        if (!pages) {
 344                pr_err("%s: Failed to allocate array for %u pages\n", __func__,
 345                        page_count);
 346                return NULL;
 347        }
 348
 349        for (i = 0; i < page_count; i++) {
 350                phys_addr_t addr = page_start + i * PAGE_SIZE;
 351                pages[i] = pfn_to_page(addr >> PAGE_SHIFT);
 352        }
 353        vaddr = vmap(pages, page_count, VM_MAP, prot);
 354        kfree(pages);
 355
 356        return vaddr;
 357}
 358
 359static void *persistent_ram_iomap(phys_addr_t start, size_t size)
 360{
 361        if (!request_mem_region(start, size, "persistent_ram")) {
 362                pr_err("request mem region (0x%llx@0x%llx) failed\n",
 363                        (unsigned long long)size, (unsigned long long)start);
 364                return NULL;
 365        }
 366
 367        return ioremap(start, size);
 368}
 369
 370static int persistent_ram_buffer_map(phys_addr_t start, phys_addr_t size,
 371                struct persistent_ram_zone *prz)
 372{
 373        prz->paddr = start;
 374        prz->size = size;
 375
 376        if (pfn_valid(start >> PAGE_SHIFT))
 377                prz->vaddr = persistent_ram_vmap(start, size);
 378        else
 379                prz->vaddr = persistent_ram_iomap(start, size);
 380
 381        if (!prz->vaddr) {
 382                pr_err("%s: Failed to map 0x%llx pages at 0x%llx\n", __func__,
 383                        (unsigned long long)size, (unsigned long long)start);
 384                return -ENOMEM;
 385        }
 386
 387        prz->buffer = prz->vaddr + offset_in_page(start);
 388        prz->buffer_size = size - sizeof(struct persistent_ram_buffer);
 389
 390        return 0;
 391}
 392
 393static int __devinit persistent_ram_post_init(struct persistent_ram_zone *prz,
 394                                              u32 sig, int ecc_size)
 395{
 396        int ret;
 397
 398        ret = persistent_ram_init_ecc(prz, ecc_size);
 399        if (ret)
 400                return ret;
 401
 402        sig ^= PERSISTENT_RAM_SIG;
 403
 404        if (prz->buffer->sig == sig) {
 405                if (buffer_size(prz) > prz->buffer_size ||
 406                    buffer_start(prz) > buffer_size(prz))
 407                        pr_info("persistent_ram: found existing invalid buffer,"
 408                                " size %zu, start %zu\n",
 409                               buffer_size(prz), buffer_start(prz));
 410                else {
 411                        pr_debug("persistent_ram: found existing buffer,"
 412                                " size %zu, start %zu\n",
 413                               buffer_size(prz), buffer_start(prz));
 414                        persistent_ram_save_old(prz);
 415                        return 0;
 416                }
 417        } else {
 418                pr_debug("persistent_ram: no valid data in buffer"
 419                        " (sig = 0x%08x)\n", prz->buffer->sig);
 420        }
 421
 422        prz->buffer->sig = sig;
 423        persistent_ram_zap(prz);
 424
 425        return 0;
 426}
 427
 428void persistent_ram_free(struct persistent_ram_zone *prz)
 429{
 430        if (!prz)
 431                return;
 432
 433        if (prz->vaddr) {
 434                if (pfn_valid(prz->paddr >> PAGE_SHIFT)) {
 435                        vunmap(prz->vaddr);
 436                } else {
 437                        iounmap(prz->vaddr);
 438                        release_mem_region(prz->paddr, prz->size);
 439                }
 440                prz->vaddr = NULL;
 441        }
 442        persistent_ram_free_old(prz);
 443        kfree(prz);
 444}
 445
 446struct persistent_ram_zone * __devinit persistent_ram_new(phys_addr_t start,
 447                                                          size_t size, u32 sig,
 448                                                          int ecc_size)
 449{
 450        struct persistent_ram_zone *prz;
 451        int ret = -ENOMEM;
 452
 453        prz = kzalloc(sizeof(struct persistent_ram_zone), GFP_KERNEL);
 454        if (!prz) {
 455                pr_err("persistent_ram: failed to allocate persistent ram zone\n");
 456                goto err;
 457        }
 458
 459        ret = persistent_ram_buffer_map(start, size, prz);
 460        if (ret)
 461                goto err;
 462
 463        ret = persistent_ram_post_init(prz, sig, ecc_size);
 464        if (ret)
 465                goto err;
 466
 467        return prz;
 468err:
 469        persistent_ram_free(prz);
 470        return ERR_PTR(ret);
 471}
 472
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.