cregit-Linux how code gets into the kernel

Release 4.10 fs/pstore/ram_core.c

Directory: fs/pstore
/*
 * Copyright (C) 2012 Google, Inc.
 *
 * This software is licensed under the terms of the GNU General Public
 * License version 2, as published by the Free Software Foundation, and
 * may be copied, distributed, and modified under those terms.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 */


#define pr_fmt(fmt) "persistent_ram: " fmt

#include <linux/device.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/memblock.h>
#include <linux/pstore_ram.h>
#include <linux/rslib.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/vmalloc.h>
#include <asm/page.h>


struct persistent_ram_buffer {
	
uint32_t    sig;
	
atomic_t    start;
	
atomic_t    size;
	
uint8_t     data[0];
};


#define PERSISTENT_RAM_SIG (0x43474244) 
/* DBGC */


static inline size_t buffer_size(struct persistent_ram_zone *prz) { return atomic_read(&prz->buffer->size); }

Contributors

PersonTokensPropCommitsCommitProp
colin crosscolin cross23100.00%1100.00%
Total23100.00%1100.00%


static inline size_t buffer_start(struct persistent_ram_zone *prz) { return atomic_read(&prz->buffer->start); }

Contributors

PersonTokensPropCommitsCommitProp
colin crosscolin cross23100.00%1100.00%
Total23100.00%1100.00%

/* increase and wrap the start pointer, returning the old value */
static size_t buffer_start_add(struct persistent_ram_zone *prz, size_t a) { int old; int new; unsigned long flags = 0; if (!(prz->flags & PRZ_FLAG_NO_LOCK)) raw_spin_lock_irqsave(&prz->buffer_lock, flags); old = atomic_read(&prz->buffer->start); new = old + a; while (unlikely(new >= prz->buffer_size)) new -= prz->buffer_size; atomic_set(&prz->buffer->start, new); if (!(prz->flags & PRZ_FLAG_NO_LOCK)) raw_spin_unlock_irqrestore(&prz->buffer_lock, flags); return old; }

Contributors

PersonTokensPropCommitsCommitProp
rob herringrob herring8874.58%120.00%
joel fernandesjoel fernandes2823.73%240.00%
sebastian andrzej siewiorsebastian andrzej siewior10.85%120.00%
liu shuoxliu shuox10.85%120.00%
Total118100.00%5100.00%

/* increase the size counter until it hits the max size */
static void buffer_size_add(struct persistent_ram_zone *prz, size_t a) { size_t old; size_t new; unsigned long flags = 0; if (!(prz->flags & PRZ_FLAG_NO_LOCK)) raw_spin_lock_irqsave(&prz->buffer_lock, flags); old = atomic_read(&prz->buffer->size); if (old == prz->buffer_size) goto exit; new = old + a; if (new > prz->buffer_size) new = prz->buffer_size; atomic_set(&prz->buffer->size, new); exit: if (!(prz->flags & PRZ_FLAG_NO_LOCK)) raw_spin_unlock_irqrestore(&prz->buffer_lock, flags); }

Contributors

PersonTokensPropCommitsCommitProp
rob herringrob herring9676.80%125.00%
joel fernandesjoel fernandes2822.40%250.00%
sebastian andrzej siewiorsebastian andrzej siewior10.80%125.00%
Total125100.00%4100.00%


static void notrace persistent_ram_encode_rs8(struct persistent_ram_zone *prz, uint8_t *data, size_t len, uint8_t *ecc) { int i; uint16_t par[prz->ecc_info.ecc_size]; /* Initialize the parity buffer */ memset(par, 0, sizeof(par)); encode_rs8(prz->rs_decoder, data, len, par, 0); for (i = 0; i < prz->ecc_info.ecc_size; i++) ecc[i] = par[i]; }

Contributors

PersonTokensPropCommitsCommitProp
colin crosscolin cross8795.60%375.00%
arve hjonnevagarve hjonnevag44.40%125.00%
Total91100.00%4100.00%


static int persistent_ram_decode_rs8(struct persistent_ram_zone *prz, void *data, size_t len, uint8_t *ecc) { int i; uint16_t par[prz->ecc_info.ecc_size]; for (i = 0; i < prz->ecc_info.ecc_size; i++) par[i] = ecc[i]; return decode_rs8(prz->rs_decoder, data, par, len, NULL, 0, NULL, 0, NULL); }

Contributors

PersonTokensPropCommitsCommitProp
colin crosscolin cross8295.35%266.67%
arve hjonnevagarve hjonnevag44.65%133.33%
Total86100.00%3100.00%


static void notrace persistent_ram_update_ecc(struct persistent_ram_zone *prz, unsigned int start, unsigned int count) { struct persistent_ram_buffer *buffer = prz->buffer; uint8_t *buffer_end = buffer->data + prz->buffer_size; uint8_t *block; uint8_t *par; int ecc_block_size = prz->ecc_info.block_size; int ecc_size = prz->ecc_info.ecc_size; int size = ecc_block_size; if (!ecc_size) return; block = buffer->data + (start & ~(ecc_block_size - 1)); par = prz->par_buffer + (start / ecc_block_size) * ecc_size; do { if (block + ecc_block_size > buffer_end) size = buffer_end - block; persistent_ram_encode_rs8(prz, block, size, par); block += ecc_block_size; par += ecc_size; } while (block < buffer->data + start + count); }

Contributors

PersonTokensPropCommitsCommitProp
colin crosscolin cross15296.20%466.67%
arve hjonnevagarve hjonnevag53.16%116.67%
anton vorontsovanton vorontsov10.63%116.67%
Total158100.00%6100.00%


static void persistent_ram_update_header_ecc(struct persistent_ram_zone *prz) { struct persistent_ram_buffer *buffer = prz->buffer; if (!prz->ecc_info.ecc_size) return; persistent_ram_encode_rs8(prz, (uint8_t *)buffer, sizeof(*buffer), prz->par_header); }

Contributors

PersonTokensPropCommitsCommitProp
colin crosscolin cross4894.12%250.00%
arve hjonnevagarve hjonnevag23.92%125.00%
anton vorontsovanton vorontsov11.96%125.00%
Total51100.00%4100.00%


static void persistent_ram_ecc_old(struct persistent_ram_zone *prz) { struct persistent_ram_buffer *buffer = prz->buffer; uint8_t *block; uint8_t *par; if (!prz->ecc_info.ecc_size) return; block = buffer->data; par = prz->par_buffer; while (block < buffer->data + buffer_size(prz)) { int numerr; int size = prz->ecc_info.block_size; if (block + size > buffer->data + prz->buffer_size) size = buffer->data + prz->buffer_size - block; numerr = persistent_ram_decode_rs8(prz, block, size, par); if (numerr > 0) { pr_devel("error in block %p, %d\n", block, numerr); prz->corrected_bytes += numerr; } else if (numerr < 0) { pr_devel("uncorrectable error in block %p\n", block); prz->bad_blocks++; } block += prz->ecc_info.block_size; par += prz->ecc_info.ecc_size; } }

Contributors

PersonTokensPropCommitsCommitProp
colin crosscolin cross16392.61%350.00%
arve hjonnevagarve hjonnevag105.68%116.67%
fabian frederickfabian frederick21.14%116.67%
anton vorontsovanton vorontsov10.57%116.67%
Total176100.00%6100.00%


static int persistent_ram_init_ecc(struct persistent_ram_zone *prz, struct persistent_ram_ecc_info *ecc_info) { int numerr; struct persistent_ram_buffer *buffer = prz->buffer; int ecc_blocks; size_t ecc_total; if (!ecc_info || !ecc_info->ecc_size) return 0; prz->ecc_info.block_size = ecc_info->block_size ?: 128; prz->ecc_info.ecc_size = ecc_info->ecc_size ?: 16; prz->ecc_info.symsize = ecc_info->symsize ?: 8; prz->ecc_info.poly = ecc_info->poly ?: 0x11d; ecc_blocks = DIV_ROUND_UP(prz->buffer_size - prz->ecc_info.ecc_size, prz->ecc_info.block_size + prz->ecc_info.ecc_size); ecc_total = (ecc_blocks + 1) * prz->ecc_info.ecc_size; if (ecc_total >= prz->buffer_size) { pr_err("%s: invalid ecc_size %u (total %zu, buffer size %zu)\n", __func__, prz->ecc_info.ecc_size, ecc_total, prz->buffer_size); return -EINVAL; } prz->buffer_size -= ecc_total; prz->par_buffer = buffer->data + prz->buffer_size; prz->par_header = prz->par_buffer + ecc_blocks * prz->ecc_info.ecc_size; /* * first consecutive root is 0 * primitive element to generate roots = 1 */ prz->rs_decoder = init_rs(prz->ecc_info.symsize, prz->ecc_info.poly, 0, 1, prz->ecc_info.ecc_size); if (prz->rs_decoder == NULL) { pr_info("init_rs failed\n"); return -EINVAL; } prz->corrected_bytes = 0; prz->bad_blocks = 0; numerr = persistent_ram_decode_rs8(prz, buffer, sizeof(*buffer), prz->par_header); if (numerr > 0) { pr_info("error in header, %d\n", numerr); prz->corrected_bytes += numerr; } else if (numerr < 0) { pr_info("uncorrectable error in header\n"); prz->bad_blocks++; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
colin crosscolin cross22066.67%225.00%
arve hjonnevagarve hjonnevag8325.15%225.00%
anton vorontsovanton vorontsov247.27%337.50%
fabian frederickfabian frederick30.91%112.50%
Total330100.00%8100.00%


ssize_t persistent_ram_ecc_string(struct persistent_ram_zone *prz, char *str, size_t len) { ssize_t ret; if (!prz->ecc_info.ecc_size) return 0; if (prz->corrected_bytes || prz->bad_blocks) ret = snprintf(str, len, "" "\n%d Corrected bytes, %d unrecoverable blocks\n", prz->corrected_bytes, prz->bad_blocks); else ret = snprintf(str, len, "\nNo errors detected\n"); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
colin crosscolin cross6584.42%150.00%
arve hjonnevagarve hjonnevag1215.58%150.00%
Total77100.00%2100.00%


static void notrace persistent_ram_update(struct persistent_ram_zone *prz, const void *s, unsigned int start, unsigned int count) { struct persistent_ram_buffer *buffer = prz->buffer; memcpy_toio(buffer->data + start, s, count); persistent_ram_update_ecc(prz, start, count); }

Contributors

PersonTokensPropCommitsCommitProp
colin crosscolin cross5598.21%375.00%
furquan shaikhfurquan shaikh11.79%125.00%
Total56100.00%4100.00%


static int notrace persistent_ram_update_user(struct persistent_ram_zone *prz, const void __user *s, unsigned int start, unsigned int count) { struct persistent_ram_buffer *buffer = prz->buffer; int ret = unlikely(__copy_from_user(buffer->data + start, s, count)) ? -EFAULT : 0; persistent_ram_update_ecc(prz, start, count); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
mark salyzynmark salyzyn71100.00%1100.00%
Total71100.00%1100.00%


void persistent_ram_save_old(struct persistent_ram_zone *prz) { struct persistent_ram_buffer *buffer = prz->buffer; size_t size = buffer_size(prz); size_t start = buffer_start(prz); if (!size) return; if (!prz->old_log) { persistent_ram_ecc_old(prz); prz->old_log = kmalloc(size, GFP_KERNEL); } if (!prz->old_log) { pr_err("failed to allocate buffer\n"); return; } prz->old_log_size = size; memcpy_fromio(prz->old_log, &buffer->data[start], size - start); memcpy_fromio(prz->old_log + size - start, &buffer->data[0], start); }

Contributors

PersonTokensPropCommitsCommitProp
colin crosscolin cross10179.53%240.00%
anton vorontsovanton vorontsov2318.11%120.00%
andrew brestickerandrew bresticker21.57%120.00%
fabian frederickfabian frederick10.79%120.00%
Total127100.00%5100.00%


int notrace persistent_ram_write(struct persistent_ram_zone *prz, const void *s, unsigned int count) { int rem; int c = count; size_t start; if (unlikely(c > prz->buffer_size)) { s += c - prz->buffer_size; c = prz->buffer_size; } buffer_size_add(prz, c); start = buffer_start_add(prz, c); rem = prz->buffer_size - start; if (unlikely(rem < c)) { persistent_ram_update(prz, s, start, rem); s += rem; c -= rem; start = 0; } persistent_ram_update(prz, s, start, c); persistent_ram_update_header_ecc(prz); return count; }

Contributors

PersonTokensPropCommitsCommitProp
colin crosscolin cross13499.26%480.00%
anton vorontsovanton vorontsov10.74%120.00%
Total135100.00%5100.00%


int notrace persistent_ram_write_user(struct persistent_ram_zone *prz, const void __user *s, unsigned int count) { int rem, ret = 0, c = count; size_t start; if (unlikely(!access_ok(VERIFY_READ, s, count))) return -EFAULT; if (unlikely(c > prz->buffer_size)) { s += c - prz->buffer_size; c = prz->buffer_size; } buffer_size_add(prz, c); start = buffer_start_add(prz, c); rem = prz->buffer_size - start; if (unlikely(rem < c)) { ret = persistent_ram_update_user(prz, s, start, rem); s += rem; c -= rem; start = 0; } if (likely(!ret)) ret = persistent_ram_update_user(prz, s, start, c); persistent_ram_update_header_ecc(prz); return unlikely(ret) ? ret : count; }

Contributors

PersonTokensPropCommitsCommitProp
mark salyzynmark salyzyn177100.00%1100.00%
Total177100.00%1100.00%


size_t persistent_ram_old_size(struct persistent_ram_zone *prz) { return prz->old_log_size; }

Contributors

PersonTokensPropCommitsCommitProp
colin crosscolin cross15100.00%1100.00%
Total15100.00%1100.00%


void *persistent_ram_old(struct persistent_ram_zone *prz) { return prz->old_log; }

Contributors

PersonTokensPropCommitsCommitProp
colin crosscolin cross16100.00%1100.00%
Total16100.00%1100.00%


void persistent_ram_free_old(struct persistent_ram_zone *prz) { kfree(prz->old_log); prz->old_log = NULL; prz->old_log_size = 0; }

Contributors

PersonTokensPropCommitsCommitProp
colin crosscolin cross29100.00%1100.00%
Total29100.00%1100.00%


void persistent_ram_zap(struct persistent_ram_zone *prz) { atomic_set(&prz->buffer->start, 0); atomic_set(&prz->buffer->size, 0); persistent_ram_update_header_ecc(prz); }

Contributors

PersonTokensPropCommitsCommitProp
anton vorontsovanton vorontsov39100.00%1100.00%
Total39100.00%1100.00%


static void *persistent_ram_vmap(phys_addr_t start, size_t size, unsigned int memtype) { struct page **pages; phys_addr_t page_start; unsigned int page_count; pgprot_t prot; unsigned int i; void *vaddr; page_start = start - offset_in_page(start); page_count = DIV_ROUND_UP(size + offset_in_page(start), PAGE_SIZE); if (memtype) prot = pgprot_noncached(PAGE_KERNEL); else prot = pgprot_writecombine(PAGE_KERNEL); pages = kmalloc_array(page_count, sizeof(struct page *), GFP_KERNEL); if (!pages) { pr_err("%s: Failed to allocate array for %u pages\n", __func__, page_count); return NULL; } for (i = 0; i < page_count; i++) { phys_addr_t addr = page_start + i * PAGE_SIZE; pages[i] = pfn_to_page(addr >> PAGE_SHIFT); } vaddr = vmap(pages, page_count, VM_MAP, prot); kfree(pages); return vaddr; }

Contributors

PersonTokensPropCommitsCommitProp
colin crosscolin cross14281.14%233.33%
tony lindgrentony lindgren169.14%116.67%
anton vorontsovanton vorontsov137.43%116.67%
fabian frederickfabian frederick31.71%116.67%
rob herringrob herring10.57%116.67%
Total175100.00%6100.00%


static void *persistent_ram_iomap(phys_addr_t start, size_t size, unsigned int memtype) { void *va; if (!request_mem_region(start, size, "persistent_ram")) { pr_err("request mem region (0x%llx@0x%llx) failed\n", (unsigned long long)size, (unsigned long long)start); return NULL; } if (memtype) va = ioremap(start, size); else va = ioremap_wc(start, size); return va; }

Contributors

PersonTokensPropCommitsCommitProp
anton vorontsovanton vorontsov5566.27%133.33%
tony lindgrentony lindgren2732.53%133.33%
rob herringrob herring11.20%133.33%
Total83100.00%3100.00%


static int persistent_ram_buffer_map(phys_addr_t start, phys_addr_t size, struct persistent_ram_zone *prz, int memtype) { prz->paddr = start; prz->size = size; if (pfn_valid(start >> PAGE_SHIFT)) prz->vaddr = persistent_ram_vmap(start, size, memtype); else prz->vaddr = persistent_ram_iomap(start, size, memtype); if (!prz->vaddr) { pr_err("%s: Failed to map 0x%llx pages at 0x%llx\n", __func__, (unsigned long long)size, (unsigned long long)start); return -ENOMEM; } prz->buffer = prz->vaddr + offset_in_page(start); prz->buffer_size = size - sizeof(struct persistent_ram_buffer); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
anton vorontsovanton vorontsov7456.92%350.00%
colin crosscolin cross4937.69%233.33%
tony lindgrentony lindgren75.38%116.67%
Total130100.00%6100.00%


static int persistent_ram_post_init(struct persistent_ram_zone *prz, u32 sig, struct persistent_ram_ecc_info *ecc_info, unsigned long flags) { int ret; ret = persistent_ram_init_ecc(prz, ecc_info); if (ret) return ret; sig ^= PERSISTENT_RAM_SIG; if (prz->buffer->sig == sig) { if (buffer_size(prz) > prz->buffer_size || buffer_start(prz) > buffer_size(prz)) pr_info("found existing invalid buffer, size %zu, start %zu\n", buffer_size(prz), buffer_start(prz)); else { pr_debug("found existing buffer, size %zu, start %zu\n", buffer_size(prz), buffer_start(prz)); persistent_ram_save_old(prz); return 0; } } else { pr_debug("no valid data in buffer (sig = 0x%08x)\n", prz->buffer->sig); } prz->buffer->sig = sig; persistent_ram_zap(prz); prz->buffer_lock = __RAW_SPIN_LOCK_UNLOCKED(buffer_lock); prz->flags = flags; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
colin crosscolin cross10764.85%428.57%
anton vorontsovanton vorontsov3118.79%642.86%
joel fernandesjoel fernandes1911.52%214.29%
arve hjonnevagarve hjonnevag53.03%17.14%
fabian frederickfabian frederick31.82%17.14%
Total165100.00%14100.00%


void persistent_ram_free(struct persistent_ram_zone *prz) { if (!prz) return; if (prz->vaddr) { if (pfn_valid(prz->paddr >> PAGE_SHIFT)) { vunmap(prz->vaddr); } else { iounmap(prz->vaddr); release_mem_region(prz->paddr, prz->size); } prz->vaddr = NULL; } persistent_ram_free_old(prz); kfree(prz); }

Contributors

PersonTokensPropCommitsCommitProp
anton vorontsovanton vorontsov81100.00%2100.00%
Total81100.00%2100.00%


struct persistent_ram_zone *persistent_ram_new(phys_addr_t start, size_t size, u32 sig, struct persistent_ram_ecc_info *ecc_info, unsigned int memtype, u32 flags) { struct persistent_ram_zone *prz; int ret = -ENOMEM; prz = kzalloc(sizeof(struct persistent_ram_zone), GFP_KERNEL); if (!prz) { pr_err("failed to allocate persistent ram zone\n"); goto err; } ret = persistent_ram_buffer_map(start, size, prz, memtype); if (ret) goto err; ret = persistent_ram_post_init(prz, sig, ecc_info, flags); if (ret) goto err; return prz; err: persistent_ram_free(prz); return ERR_PTR(ret); }

Contributors

PersonTokensPropCommitsCommitProp
anton vorontsovanton vorontsov10686.18%342.86%
tony lindgrentony lindgren64.88%114.29%
arve hjonnevagarve hjonnevag54.07%114.29%
joel fernandesjoel fernandes54.07%114.29%
fabian frederickfabian frederick10.81%114.29%
Total123100.00%7100.00%


Overall Contributors

PersonTokensPropCommitsCommitProp
colin crosscolin cross156856.87%513.89%
anton vorontsovanton vorontsov45316.43%1644.44%
mark salyzynmark salyzyn2569.29%12.78%
rob herringrob herring1886.82%25.56%
arve hjonnevagarve hjonnevag1304.72%38.33%
joel fernandesjoel fernandes802.90%25.56%
tony lindgrentony lindgren562.03%12.78%
fabian frederickfabian frederick200.73%25.56%
andrew brestickerandrew bresticker20.07%12.78%
sebastian andrzej siewiorsebastian andrzej siewior20.07%12.78%
liu shuoxliu shuox10.04%12.78%
furquan shaikhfurquan shaikh10.04%12.78%
Total2757100.00%36100.00%
Directory: fs/pstore
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.