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#define pr_fmt(fmt) "persistent_ram: " fmt
  16
  17#include <linux/device.h>
  18#include <linux/err.h>
  19#include <linux/errno.h>
  20#include <linux/kernel.h>
  21#include <linux/init.h>
  22#include <linux/io.h>
  23#include <linux/list.h>
  24#include <linux/memblock.h>
  25#include <linux/rslib.h>
  26#include <linux/slab.h>
  27#include <linux/vmalloc.h>
  28#include <linux/pstore_ram.h>
  29#include <asm/page.h>
  30
  31struct persistent_ram_buffer {
  32        uint32_t    sig;
  33        atomic_t    start;
  34        atomic_t    size;
  35        uint8_t     data[0];
  36};
  37
  38#define PERSISTENT_RAM_SIG (0x43474244) /* DBGC */
  39
  40static inline size_t buffer_size(struct persistent_ram_zone *prz)
  41{
  42        return atomic_read(&prz->buffer->size);
  43}
  44
  45static inline size_t buffer_start(struct persistent_ram_zone *prz)
  46{
  47        return atomic_read(&prz->buffer->start);
  48}
  49
  50/* increase and wrap the start pointer, returning the old value */
  51static size_t buffer_start_add_atomic(struct persistent_ram_zone *prz, size_t a)
  52{
  53        int old;
  54        int new;
  55
  56        do {
  57                old = atomic_read(&prz->buffer->start);
  58                new = old + a;
  59                while (unlikely(new >= prz->buffer_size))
  60                        new -= prz->buffer_size;
  61        } while (atomic_cmpxchg(&prz->buffer->start, old, new) != old);
  62
  63        return old;
  64}
  65
  66/* increase the size counter until it hits the max size */
  67static void buffer_size_add_atomic(struct persistent_ram_zone *prz, size_t a)
  68{
  69        size_t old;
  70        size_t new;
  71
  72        if (atomic_read(&prz->buffer->size) == prz->buffer_size)
  73                return;
  74
  75        do {
  76                old = atomic_read(&prz->buffer->size);
  77                new = old + a;
  78                if (new > prz->buffer_size)
  79                        new = prz->buffer_size;
  80        } while (atomic_cmpxchg(&prz->buffer->size, old, new) != old);
  81}
  82
  83static DEFINE_RAW_SPINLOCK(buffer_lock);
  84
  85/* increase and wrap the start pointer, returning the old value */
  86static size_t buffer_start_add_locked(struct persistent_ram_zone *prz, size_t a)
  87{
  88        int old;
  89        int new;
  90        unsigned long flags;
  91
  92        raw_spin_lock_irqsave(&buffer_lock, flags);
  93
  94        old = atomic_read(&prz->buffer->start);
  95        new = old + a;
  96        while (unlikely(new >= prz->buffer_size))
  97                new -= prz->buffer_size;
  98        atomic_set(&prz->buffer->start, new);
  99
 100        raw_spin_unlock_irqrestore(&buffer_lock, flags);
 101
 102        return old;
 103}
 104
 105/* increase the size counter until it hits the max size */
 106static void buffer_size_add_locked(struct persistent_ram_zone *prz, size_t a)
 107{
 108        size_t old;
 109        size_t new;
 110        unsigned long flags;
 111
 112        raw_spin_lock_irqsave(&buffer_lock, flags);
 113
 114        old = atomic_read(&prz->buffer->size);
 115        if (old == prz->buffer_size)
 116                goto exit;
 117
 118        new = old + a;
 119        if (new > prz->buffer_size)
 120                new = prz->buffer_size;
 121        atomic_set(&prz->buffer->size, new);
 122
 123exit:
 124        raw_spin_unlock_irqrestore(&buffer_lock, flags);
 125}
 126
 127static size_t (*buffer_start_add)(struct persistent_ram_zone *, size_t) = buffer_start_add_atomic;
 128static void (*buffer_size_add)(struct persistent_ram_zone *, size_t) = buffer_size_add_atomic;
 129
 130static void notrace persistent_ram_encode_rs8(struct persistent_ram_zone *prz,
 131        uint8_t *data, size_t len, uint8_t *ecc)
 132{
 133        int i;
 134        uint16_t par[prz->ecc_info.ecc_size];
 135
 136        /* Initialize the parity buffer */
 137        memset(par, 0, sizeof(par));
 138        encode_rs8(prz->rs_decoder, data, len, par, 0);
 139        for (i = 0; i < prz->ecc_info.ecc_size; i++)
 140                ecc[i] = par[i];
 141}
 142
 143static int persistent_ram_decode_rs8(struct persistent_ram_zone *prz,
 144        void *data, size_t len, uint8_t *ecc)
 145{
 146        int i;
 147        uint16_t par[prz->ecc_info.ecc_size];
 148
 149        for (i = 0; i < prz->ecc_info.ecc_size; i++)
 150                par[i] = ecc[i];
 151        return decode_rs8(prz->rs_decoder, data, par, len,
 152                                NULL, 0, NULL, 0, NULL);
 153}
 154
 155static void notrace persistent_ram_update_ecc(struct persistent_ram_zone *prz,
 156        unsigned int start, unsigned int count)
 157{
 158        struct persistent_ram_buffer *buffer = prz->buffer;
 159        uint8_t *buffer_end = buffer->data + prz->buffer_size;
 160        uint8_t *block;
 161        uint8_t *par;
 162        int ecc_block_size = prz->ecc_info.block_size;
 163        int ecc_size = prz->ecc_info.ecc_size;
 164        int size = ecc_block_size;
 165
 166        if (!ecc_size)
 167                return;
 168
 169        block = buffer->data + (start & ~(ecc_block_size - 1));
 170        par = prz->par_buffer + (start / ecc_block_size) * ecc_size;
 171
 172        do {
 173                if (block + ecc_block_size > buffer_end)
 174                        size = buffer_end - block;
 175                persistent_ram_encode_rs8(prz, block, size, par);
 176                block += ecc_block_size;
 177                par += ecc_size;
 178        } while (block < buffer->data + start + count);
 179}
 180
 181static void persistent_ram_update_header_ecc(struct persistent_ram_zone *prz)
 182{
 183        struct persistent_ram_buffer *buffer = prz->buffer;
 184
 185        if (!prz->ecc_info.ecc_size)
 186                return;
 187
 188        persistent_ram_encode_rs8(prz, (uint8_t *)buffer, sizeof(*buffer),
 189                                  prz->par_header);
 190}
 191
 192static void persistent_ram_ecc_old(struct persistent_ram_zone *prz)
 193{
 194        struct persistent_ram_buffer *buffer = prz->buffer;
 195        uint8_t *block;
 196        uint8_t *par;
 197
 198        if (!prz->ecc_info.ecc_size)
 199                return;
 200
 201        block = buffer->data;
 202        par = prz->par_buffer;
 203        while (block < buffer->data + buffer_size(prz)) {
 204                int numerr;
 205                int size = prz->ecc_info.block_size;
 206                if (block + size > buffer->data + prz->buffer_size)
 207                        size = buffer->data + prz->buffer_size - block;
 208                numerr = persistent_ram_decode_rs8(prz, block, size, par);
 209                if (numerr > 0) {
 210                        pr_devel("error in block %p, %d\n", block, numerr);
 211                        prz->corrected_bytes += numerr;
 212                } else if (numerr < 0) {
 213                        pr_devel("uncorrectable error in block %p\n", block);
 214                        prz->bad_blocks++;
 215                }
 216                block += prz->ecc_info.block_size;
 217                par += prz->ecc_info.ecc_size;
 218        }
 219}
 220
 221static int persistent_ram_init_ecc(struct persistent_ram_zone *prz,
 222                                   struct persistent_ram_ecc_info *ecc_info)
 223{
 224        int numerr;
 225        struct persistent_ram_buffer *buffer = prz->buffer;
 226        int ecc_blocks;
 227        size_t ecc_total;
 228
 229        if (!ecc_info || !ecc_info->ecc_size)
 230                return 0;
 231
 232        prz->ecc_info.block_size = ecc_info->block_size ?: 128;
 233        prz->ecc_info.ecc_size = ecc_info->ecc_size ?: 16;
 234        prz->ecc_info.symsize = ecc_info->symsize ?: 8;
 235        prz->ecc_info.poly = ecc_info->poly ?: 0x11d;
 236
 237        ecc_blocks = DIV_ROUND_UP(prz->buffer_size - prz->ecc_info.ecc_size,
 238                                  prz->ecc_info.block_size +
 239                                  prz->ecc_info.ecc_size);
 240        ecc_total = (ecc_blocks + 1) * prz->ecc_info.ecc_size;
 241        if (ecc_total >= prz->buffer_size) {
 242                pr_err("%s: invalid ecc_size %u (total %zu, buffer size %zu)\n",
 243                       __func__, prz->ecc_info.ecc_size,
 244                       ecc_total, prz->buffer_size);
 245                return -EINVAL;
 246        }
 247
 248        prz->buffer_size -= ecc_total;
 249        prz->par_buffer = buffer->data + prz->buffer_size;
 250        prz->par_header = prz->par_buffer +
 251                          ecc_blocks * prz->ecc_info.ecc_size;
 252
 253        /*
 254         * first consecutive root is 0
 255         * primitive element to generate roots = 1
 256         */
 257        prz->rs_decoder = init_rs(prz->ecc_info.symsize, prz->ecc_info.poly,
 258                                  0, 1, prz->ecc_info.ecc_size);
 259        if (prz->rs_decoder == NULL) {
 260                pr_info("init_rs failed\n");
 261                return -EINVAL;
 262        }
 263
 264        prz->corrected_bytes = 0;
 265        prz->bad_blocks = 0;
 266
 267        numerr = persistent_ram_decode_rs8(prz, buffer, sizeof(*buffer),
 268                                           prz->par_header);
 269        if (numerr > 0) {
 270                pr_info("error in header, %d\n", numerr);
 271                prz->corrected_bytes += numerr;
 272        } else if (numerr < 0) {
 273                pr_info("uncorrectable error in header\n");
 274                prz->bad_blocks++;
 275        }
 276
 277        return 0;
 278}
 279
 280ssize_t persistent_ram_ecc_string(struct persistent_ram_zone *prz,
 281        char *str, size_t len)
 282{
 283        ssize_t ret;
 284
 285        if (!prz->ecc_info.ecc_size)
 286                return 0;
 287
 288        if (prz->corrected_bytes || prz->bad_blocks)
 289                ret = snprintf(str, len, ""
 290                        "\n%d Corrected bytes, %d unrecoverable blocks\n",
 291                        prz->corrected_bytes, prz->bad_blocks);
 292        else
 293                ret = snprintf(str, len, "\nNo errors detected\n");
 294
 295        return ret;
 296}
 297
 298static void notrace persistent_ram_update(struct persistent_ram_zone *prz,
 299        const void *s, unsigned int start, unsigned int count)
 300{
 301        struct persistent_ram_buffer *buffer = prz->buffer;
 302        memcpy(buffer->data + start, s, count);
 303        persistent_ram_update_ecc(prz, start, count);
 304}
 305
 306void persistent_ram_save_old(struct persistent_ram_zone *prz)
 307{
 308        struct persistent_ram_buffer *buffer = prz->buffer;
 309        size_t size = buffer_size(prz);
 310        size_t start = buffer_start(prz);
 311
 312        if (!size)
 313                return;
 314
 315        if (!prz->old_log) {
 316                persistent_ram_ecc_old(prz);
 317                prz->old_log = kmalloc(size, GFP_KERNEL);
 318        }
 319        if (!prz->old_log) {
 320                pr_err("failed to allocate buffer\n");
 321                return;
 322        }
 323
 324        prz->old_log_size = size;
 325        memcpy(prz->old_log, &buffer->data[start], size - start);
 326        memcpy(prz->old_log + size - start, &buffer->data[0], start);
 327}
 328
 329int notrace persistent_ram_write(struct persistent_ram_zone *prz,
 330        const void *s, unsigned int count)
 331{
 332        int rem;
 333        int c = count;
 334        size_t start;
 335
 336        if (unlikely(c > prz->buffer_size)) {
 337                s += c - prz->buffer_size;
 338                c = prz->buffer_size;
 339        }
 340
 341        buffer_size_add(prz, c);
 342
 343        start = buffer_start_add(prz, c);
 344
 345        rem = prz->buffer_size - start;
 346        if (unlikely(rem < c)) {
 347                persistent_ram_update(prz, s, start, rem);
 348                s += rem;
 349                c -= rem;
 350                start = 0;
 351        }
 352        persistent_ram_update(prz, s, start, c);
 353
 354        persistent_ram_update_header_ecc(prz);
 355
 356        return count;
 357}
 358
 359size_t persistent_ram_old_size(struct persistent_ram_zone *prz)
 360{
 361        return prz->old_log_size;
 362}
 363
 364void *persistent_ram_old(struct persistent_ram_zone *prz)
 365{
 366        return prz->old_log;
 367}
 368
 369void persistent_ram_free_old(struct persistent_ram_zone *prz)
 370{
 371        kfree(prz->old_log);
 372        prz->old_log = NULL;
 373        prz->old_log_size = 0;
 374}
 375
 376void persistent_ram_zap(struct persistent_ram_zone *prz)
 377{
 378        atomic_set(&prz->buffer->start, 0);
 379        atomic_set(&prz->buffer->size, 0);
 380        persistent_ram_update_header_ecc(prz);
 381}
 382
 383static void *persistent_ram_vmap(phys_addr_t start, size_t size,
 384                unsigned int memtype)
 385{
 386        struct page **pages;
 387        phys_addr_t page_start;
 388        unsigned int page_count;
 389        pgprot_t prot;
 390        unsigned int i;
 391        void *vaddr;
 392
 393        page_start = start - offset_in_page(start);
 394        page_count = DIV_ROUND_UP(size + offset_in_page(start), PAGE_SIZE);
 395
 396        if (memtype)
 397                prot = pgprot_noncached(PAGE_KERNEL);
 398        else
 399                prot = pgprot_writecombine(PAGE_KERNEL);
 400
 401        pages = kmalloc_array(page_count, sizeof(struct page *), GFP_KERNEL);
 402        if (!pages) {
 403                pr_err("%s: Failed to allocate array for %u pages\n",
 404                       __func__, page_count);
 405                return NULL;
 406        }
 407
 408        for (i = 0; i < page_count; i++) {
 409                phys_addr_t addr = page_start + i * PAGE_SIZE;
 410                pages[i] = pfn_to_page(addr >> PAGE_SHIFT);
 411        }
 412        vaddr = vmap(pages, page_count, VM_MAP, prot);
 413        kfree(pages);
 414
 415        return vaddr;
 416}
 417
 418static void *persistent_ram_iomap(phys_addr_t start, size_t size,
 419                unsigned int memtype)
 420{
 421        void *va;
 422
 423        if (!request_mem_region(start, size, "persistent_ram")) {
 424                pr_err("request mem region (0x%llx@0x%llx) failed\n",
 425                        (unsigned long long)size, (unsigned long long)start);
 426                return NULL;
 427        }
 428
 429        buffer_start_add = buffer_start_add_locked;
 430        buffer_size_add = buffer_size_add_locked;
 431
 432        if (memtype)
 433                va = ioremap(start, size);
 434        else
 435                va = ioremap_wc(start, size);
 436
 437        return va;
 438}
 439
 440static int persistent_ram_buffer_map(phys_addr_t start, phys_addr_t size,
 441                struct persistent_ram_zone *prz, int memtype)
 442{
 443        prz->paddr = start;
 444        prz->size = size;
 445
 446        if (pfn_valid(start >> PAGE_SHIFT))
 447                prz->vaddr = persistent_ram_vmap(start, size, memtype);
 448        else
 449                prz->vaddr = persistent_ram_iomap(start, size, memtype);
 450
 451        if (!prz->vaddr) {
 452                pr_err("%s: Failed to map 0x%llx pages at 0x%llx\n", __func__,
 453                        (unsigned long long)size, (unsigned long long)start);
 454                return -ENOMEM;
 455        }
 456
 457        prz->buffer = prz->vaddr + offset_in_page(start);
 458        prz->buffer_size = size - sizeof(struct persistent_ram_buffer);
 459
 460        return 0;
 461}
 462
 463static int persistent_ram_post_init(struct persistent_ram_zone *prz, u32 sig,
 464                                    struct persistent_ram_ecc_info *ecc_info)
 465{
 466        int ret;
 467
 468        ret = persistent_ram_init_ecc(prz, ecc_info);
 469        if (ret)
 470                return ret;
 471
 472        sig ^= PERSISTENT_RAM_SIG;
 473
 474        if (prz->buffer->sig == sig) {
 475                if (buffer_size(prz) > prz->buffer_size ||
 476                    buffer_start(prz) > buffer_size(prz))
 477                        pr_info("found existing invalid buffer, size %zu, start %zu\n",
 478                                buffer_size(prz), buffer_start(prz));
 479                else {
 480                        pr_debug("found existing buffer, size %zu, start %zu\n",
 481                                 buffer_size(prz), buffer_start(prz));
 482                        persistent_ram_save_old(prz);
 483                        return 0;
 484                }
 485        } else {
 486                pr_debug("no valid data in buffer (sig = 0x%08x)\n",
 487                         prz->buffer->sig);
 488        }
 489
 490        prz->buffer->sig = sig;
 491        persistent_ram_zap(prz);
 492
 493        return 0;
 494}
 495
 496void persistent_ram_free(struct persistent_ram_zone *prz)
 497{
 498        if (!prz)
 499                return;
 500
 501        if (prz->vaddr) {
 502                if (pfn_valid(prz->paddr >> PAGE_SHIFT)) {
 503                        vunmap(prz->vaddr);
 504                } else {
 505                        iounmap(prz->vaddr);
 506                        release_mem_region(prz->paddr, prz->size);
 507                }
 508                prz->vaddr = NULL;
 509        }
 510        persistent_ram_free_old(prz);
 511        kfree(prz);
 512}
 513
 514struct persistent_ram_zone *persistent_ram_new(phys_addr_t start, size_t size,
 515                        u32 sig, struct persistent_ram_ecc_info *ecc_info,
 516                        unsigned int memtype)
 517{
 518        struct persistent_ram_zone *prz;
 519        int ret = -ENOMEM;
 520
 521        prz = kzalloc(sizeof(struct persistent_ram_zone), GFP_KERNEL);
 522        if (!prz) {
 523                pr_err("failed to allocate persistent ram zone\n");
 524                goto err;
 525        }
 526
 527        ret = persistent_ram_buffer_map(start, size, prz, memtype);
 528        if (ret)
 529                goto err;
 530
 531        ret = persistent_ram_post_init(prz, sig, ecc_info);
 532        if (ret)
 533                goto err;
 534
 535        return prz;
 536err:
 537        persistent_ram_free(prz);
 538        return ERR_PTR(ret);
 539}
 540