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_info.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_info.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_info.ecc_size];
  99
 100        for (i = 0; i < prz->ecc_info.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_info.block_size;
 114        int ecc_size = prz->ecc_info.ecc_size;
 115        int size = ecc_block_size;
 116
 117        if (!ecc_size)
 118                return;
 119
 120        block = buffer->data + (start & ~(ecc_block_size - 1));
 121        par = prz->par_buffer + (start / ecc_block_size) * 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_info.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_info.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_info.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_info.block_size;
 170                par += prz->ecc_info.ecc_size;
 171        }
 172}
 173
 174static int persistent_ram_init_ecc(struct persistent_ram_zone *prz,
 175                                   struct persistent_ram_ecc_info *ecc_info)
 176{
 177        int numerr;
 178        struct persistent_ram_buffer *buffer = prz->buffer;
 179        int ecc_blocks;
 180        size_t ecc_total;
 181
 182        if (!ecc_info || !ecc_info->ecc_size)
 183                return 0;
 184
 185        prz->ecc_info.block_size = ecc_info->block_size ?: 128;
 186        prz->ecc_info.ecc_size = ecc_info->ecc_size ?: 16;
 187        prz->ecc_info.symsize = ecc_info->symsize ?: 8;
 188        prz->ecc_info.poly = ecc_info->poly ?: 0x11d;
 189
 190        ecc_blocks = DIV_ROUND_UP(prz->buffer_size - prz->ecc_info.ecc_size,
 191                                  prz->ecc_info.block_size +
 192                                  prz->ecc_info.ecc_size);
 193        ecc_total = (ecc_blocks + 1) * prz->ecc_info.ecc_size;
 194        if (ecc_total >= prz->buffer_size) {
 195                pr_err("%s: invalid ecc_size %u (total %zu, buffer size %zu)\n",
 196                       __func__, prz->ecc_info.ecc_size,
 197                       ecc_total, prz->buffer_size);
 198                return -EINVAL;
 199        }
 200
 201        prz->buffer_size -= ecc_total;
 202        prz->par_buffer = buffer->data + prz->buffer_size;
 203        prz->par_header = prz->par_buffer +
 204                          ecc_blocks * prz->ecc_info.ecc_size;
 205
 206        /*
 207         * first consecutive root is 0
 208         * primitive element to generate roots = 1
 209         */
 210        prz->rs_decoder = init_rs(prz->ecc_info.symsize, prz->ecc_info.poly,
 211                                  0, 1, prz->ecc_info.ecc_size);
 212        if (prz->rs_decoder == NULL) {
 213                pr_info("persistent_ram: init_rs failed\n");
 214                return -EINVAL;
 215        }
 216
 217        prz->corrected_bytes = 0;
 218        prz->bad_blocks = 0;
 219
 220        numerr = persistent_ram_decode_rs8(prz, buffer, sizeof(*buffer),
 221                                           prz->par_header);
 222        if (numerr > 0) {
 223                pr_info("persistent_ram: error in header, %d\n", numerr);
 224                prz->corrected_bytes += numerr;
 225        } else if (numerr < 0) {
 226                pr_info("persistent_ram: uncorrectable error in header\n");
 227                prz->bad_blocks++;
 228        }
 229
 230        return 0;
 231}
 232
 233ssize_t persistent_ram_ecc_string(struct persistent_ram_zone *prz,
 234        char *str, size_t len)
 235{
 236        ssize_t ret;
 237
 238        if (!prz->ecc_info.ecc_size)
 239                return 0;
 240
 241        if (prz->corrected_bytes || prz->bad_blocks)
 242                ret = snprintf(str, len, ""
 243                        "\n%d Corrected bytes, %d unrecoverable blocks\n",
 244                        prz->corrected_bytes, prz->bad_blocks);
 245        else
 246                ret = snprintf(str, len, "\nNo errors detected\n");
 247
 248        return ret;
 249}
 250
 251static void notrace persistent_ram_update(struct persistent_ram_zone *prz,
 252        const void *s, unsigned int start, unsigned int count)
 253{
 254        struct persistent_ram_buffer *buffer = prz->buffer;
 255        memcpy(buffer->data + start, s, count);
 256        persistent_ram_update_ecc(prz, start, count);
 257}
 258
 259void persistent_ram_save_old(struct persistent_ram_zone *prz)
 260{
 261        struct persistent_ram_buffer *buffer = prz->buffer;
 262        size_t size = buffer_size(prz);
 263        size_t start = buffer_start(prz);
 264
 265        if (!size)
 266                return;
 267
 268        if (!prz->old_log) {
 269                persistent_ram_ecc_old(prz);
 270                prz->old_log = kmalloc(size, GFP_KERNEL);
 271        }
 272        if (!prz->old_log) {
 273                pr_err("persistent_ram: failed to allocate buffer\n");
 274                return;
 275        }
 276
 277        prz->old_log_size = size;
 278        memcpy(prz->old_log, &buffer->data[start], size - start);
 279        memcpy(prz->old_log + size - start, &buffer->data[0], start);
 280}
 281
 282int notrace persistent_ram_write(struct persistent_ram_zone *prz,
 283        const void *s, unsigned int count)
 284{
 285        int rem;
 286        int c = count;
 287        size_t start;
 288
 289        if (unlikely(c > prz->buffer_size)) {
 290                s += c - prz->buffer_size;
 291                c = prz->buffer_size;
 292        }
 293
 294        buffer_size_add(prz, c);
 295
 296        start = buffer_start_add(prz, c);
 297
 298        rem = prz->buffer_size - start;
 299        if (unlikely(rem < c)) {
 300                persistent_ram_update(prz, s, start, rem);
 301                s += rem;
 302                c -= rem;
 303                start = 0;
 304        }
 305        persistent_ram_update(prz, s, start, c);
 306
 307        persistent_ram_update_header_ecc(prz);
 308
 309        return count;
 310}
 311
 312size_t persistent_ram_old_size(struct persistent_ram_zone *prz)
 313{
 314        return prz->old_log_size;
 315}
 316
 317void *persistent_ram_old(struct persistent_ram_zone *prz)
 318{
 319        return prz->old_log;
 320}
 321
 322void persistent_ram_free_old(struct persistent_ram_zone *prz)
 323{
 324        kfree(prz->old_log);
 325        prz->old_log = NULL;
 326        prz->old_log_size = 0;
 327}
 328
 329void persistent_ram_zap(struct persistent_ram_zone *prz)
 330{
 331        atomic_set(&prz->buffer->start, 0);
 332        atomic_set(&prz->buffer->size, 0);
 333        persistent_ram_update_header_ecc(prz);
 334}
 335
 336static void *persistent_ram_vmap(phys_addr_t start, size_t size)
 337{
 338        struct page **pages;
 339        phys_addr_t page_start;
 340        unsigned int page_count;
 341        pgprot_t prot;
 342        unsigned int i;
 343        void *vaddr;
 344
 345        page_start = start - offset_in_page(start);
 346        page_count = DIV_ROUND_UP(size + offset_in_page(start), PAGE_SIZE);
 347
 348        prot = pgprot_noncached(PAGE_KERNEL);
 349
 350        pages = kmalloc(sizeof(struct page *) * page_count, GFP_KERNEL);
 351        if (!pages) {
 352                pr_err("%s: Failed to allocate array for %u pages\n", __func__,
 353                        page_count);
 354                return NULL;
 355        }
 356
 357        for (i = 0; i < page_count; i++) {
 358                phys_addr_t addr = page_start + i * PAGE_SIZE;
 359                pages[i] = pfn_to_page(addr >> PAGE_SHIFT);
 360        }
 361        vaddr = vmap(pages, page_count, VM_MAP, prot);
 362        kfree(pages);
 363
 364        return vaddr;
 365}
 366
 367static void *persistent_ram_iomap(phys_addr_t start, size_t size)
 368{
 369        if (!request_mem_region(start, size, "persistent_ram")) {
 370                pr_err("request mem region (0x%llx@0x%llx) failed\n",
 371                        (unsigned long long)size, (unsigned long long)start);
 372                return NULL;
 373        }
 374
 375        return ioremap(start, size);
 376}
 377
 378static int persistent_ram_buffer_map(phys_addr_t start, phys_addr_t size,
 379                struct persistent_ram_zone *prz)
 380{
 381        prz->paddr = start;
 382        prz->size = size;
 383
 384        if (pfn_valid(start >> PAGE_SHIFT))
 385                prz->vaddr = persistent_ram_vmap(start, size);
 386        else
 387                prz->vaddr = persistent_ram_iomap(start, size);
 388
 389        if (!prz->vaddr) {
 390                pr_err("%s: Failed to map 0x%llx pages at 0x%llx\n", __func__,
 391                        (unsigned long long)size, (unsigned long long)start);
 392                return -ENOMEM;
 393        }
 394
 395        prz->buffer = prz->vaddr + offset_in_page(start);
 396        prz->buffer_size = size - sizeof(struct persistent_ram_buffer);
 397
 398        return 0;
 399}
 400
 401static int persistent_ram_post_init(struct persistent_ram_zone *prz, u32 sig,
 402                                    struct persistent_ram_ecc_info *ecc_info)
 403{
 404        int ret;
 405
 406        ret = persistent_ram_init_ecc(prz, ecc_info);
 407        if (ret)
 408                return ret;
 409
 410        sig ^= PERSISTENT_RAM_SIG;
 411
 412        if (prz->buffer->sig == sig) {
 413                if (buffer_size(prz) > prz->buffer_size ||
 414                    buffer_start(prz) > buffer_size(prz))
 415                        pr_info("persistent_ram: found existing invalid buffer,"
 416                                " size %zu, start %zu\n",
 417                               buffer_size(prz), buffer_start(prz));
 418                else {
 419                        pr_debug("persistent_ram: found existing buffer,"
 420                                " size %zu, start %zu\n",
 421                               buffer_size(prz), buffer_start(prz));
 422                        persistent_ram_save_old(prz);
 423                        return 0;
 424                }
 425        } else {
 426                pr_debug("persistent_ram: no valid data in buffer"
 427                        " (sig = 0x%08x)\n", prz->buffer->sig);
 428        }
 429
 430        prz->buffer->sig = sig;
 431        persistent_ram_zap(prz);
 432
 433        return 0;
 434}
 435
 436void persistent_ram_free(struct persistent_ram_zone *prz)
 437{
 438        if (!prz)
 439                return;
 440
 441        if (prz->vaddr) {
 442                if (pfn_valid(prz->paddr >> PAGE_SHIFT)) {
 443                        vunmap(prz->vaddr);
 444                } else {
 445                        iounmap(prz->vaddr);
 446                        release_mem_region(prz->paddr, prz->size);
 447                }
 448                prz->vaddr = NULL;
 449        }
 450        persistent_ram_free_old(prz);
 451        kfree(prz);
 452}
 453
 454struct persistent_ram_zone *persistent_ram_new(phys_addr_t start, size_t size,
 455                        u32 sig, struct persistent_ram_ecc_info *ecc_info)
 456{
 457        struct persistent_ram_zone *prz;
 458        int ret = -ENOMEM;
 459
 460        prz = kzalloc(sizeof(struct persistent_ram_zone), GFP_KERNEL);
 461        if (!prz) {
 462                pr_err("persistent_ram: failed to allocate persistent ram zone\n");
 463                goto err;
 464        }
 465
 466        ret = persistent_ram_buffer_map(start, size, prz);
 467        if (ret)
 468                goto err;
 469
 470        ret = persistent_ram_post_init(prz, sig, ecc_info);
 471        if (ret)
 472                goto err;
 473
 474        return prz;
 475err:
 476        persistent_ram_free(prz);
 477        return ERR_PTR(ret);
 478}
 479