Merge afdb0f2ec5 ("Merge tag 'fscrypt-for-linus' of git://git.kernel.org/pub/scm/fs/fscrypt/fscrypt") into android-mainline

Baby-steps along the way to 5.8-rc1.

Needed hand-holding for crypto fs changes, thanks to Eric for the
resolutions.

Cc: Eric Biggers <ebiggers@google.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
Change-Id: I7198edbca759839aceeec2598e7a81305756c4d7
This commit is contained in:
Greg Kroah-Hartman 2020-06-05 10:50:45 +02:00
commit f76a7cf187
42 changed files with 4075 additions and 494 deletions

View File

@ -0,0 +1,243 @@
.. SPDX-License-Identifier: GPL-2.0
pstore block oops/panic logger
==============================
Introduction
------------
pstore block (pstore/blk) is an oops/panic logger that writes its logs to a
block device and non-block device before the system crashes. You can get
these log files by mounting pstore filesystem like::
mount -t pstore pstore /sys/fs/pstore
pstore block concepts
---------------------
pstore/blk provides efficient configuration method for pstore/blk, which
divides all configurations into two parts, configurations for user and
configurations for driver.
Configurations for user determine how pstore/blk works, such as pmsg_size,
kmsg_size and so on. All of them support both Kconfig and module parameters,
but module parameters have priority over Kconfig.
Configurations for driver are all about block device and non-block device,
such as total_size of block device and read/write operations.
Configurations for user
-----------------------
All of these configurations support both Kconfig and module parameters, but
module parameters have priority over Kconfig.
Here is an example for module parameters::
pstore_blk.blkdev=179:7 pstore_blk.kmsg_size=64
The detail of each configurations may be of interest to you.
blkdev
~~~~~~
The block device to use. Most of the time, it is a partition of block device.
It's required for pstore/blk. It is also used for MTD device.
It accepts the following variants for block device:
1. <hex_major><hex_minor> device number in hexadecimal represents itself; no
leading 0x, for example b302.
#. /dev/<disk_name> represents the device number of disk
#. /dev/<disk_name><decimal> represents the device number of partition - device
number of disk plus the partition number
#. /dev/<disk_name>p<decimal> - same as the above; this form is used when disk
name of partitioned disk ends with a digit.
#. PARTUUID=00112233-4455-6677-8899-AABBCCDDEEFF represents the unique id of
a partition if the partition table provides it. The UUID may be either an
EFI/GPT UUID, or refer to an MSDOS partition using the format SSSSSSSS-PP,
where SSSSSSSS is a zero-filled hex representation of the 32-bit
"NT disk signature", and PP is a zero-filled hex representation of the
1-based partition number.
#. PARTUUID=<UUID>/PARTNROFF=<int> to select a partition in relation to a
partition with a known unique id.
#. <major>:<minor> major and minor number of the device separated by a colon.
It accepts the following variants for MTD device:
1. <device name> MTD device name. "pstore" is recommended.
#. <device number> MTD device number.
kmsg_size
~~~~~~~~~
The chunk size in KB for oops/panic front-end. It **MUST** be a multiple of 4.
It's optional if you do not care oops/panic log.
There are multiple chunks for oops/panic front-end depending on the remaining
space except other pstore front-ends.
pstore/blk will log to oops/panic chunks one by one, and always overwrite the
oldest chunk if there is no more free chunk.
pmsg_size
~~~~~~~~~
The chunk size in KB for pmsg front-end. It **MUST** be a multiple of 4.
It's optional if you do not care pmsg log.
Unlike oops/panic front-end, there is only one chunk for pmsg front-end.
Pmsg is a user space accessible pstore object. Writes to */dev/pmsg0* are
appended to the chunk. On reboot the contents are available in
*/sys/fs/pstore/pmsg-pstore-blk-0*.
console_size
~~~~~~~~~~~~
The chunk size in KB for console front-end. It **MUST** be a multiple of 4.
It's optional if you do not care console log.
Similar to pmsg front-end, there is only one chunk for console front-end.
All log of console will be appended to the chunk. On reboot the contents are
available in */sys/fs/pstore/console-pstore-blk-0*.
ftrace_size
~~~~~~~~~~~
The chunk size in KB for ftrace front-end. It **MUST** be a multiple of 4.
It's optional if you do not care console log.
Similar to oops front-end, there are multiple chunks for ftrace front-end
depending on the count of cpu processors. Each chunk size is equal to
ftrace_size / processors_count.
All log of ftrace will be appended to the chunk. On reboot the contents are
combined and available in */sys/fs/pstore/ftrace-pstore-blk-0*.
Persistent function tracing might be useful for debugging software or hardware
related hangs. Here is an example of usage::
# mount -t pstore pstore /sys/fs/pstore
# mount -t debugfs debugfs /sys/kernel/debug/
# echo 1 > /sys/kernel/debug/pstore/record_ftrace
# reboot -f
[...]
# mount -t pstore pstore /sys/fs/pstore
# tail /sys/fs/pstore/ftrace-pstore-blk-0
CPU:0 ts:5914676 c0063828 c0063b94 call_cpuidle <- cpu_startup_entry+0x1b8/0x1e0
CPU:0 ts:5914678 c039ecdc c006385c cpuidle_enter_state <- call_cpuidle+0x44/0x48
CPU:0 ts:5914680 c039e9a0 c039ecf0 cpuidle_enter_freeze <- cpuidle_enter_state+0x304/0x314
CPU:0 ts:5914681 c0063870 c039ea30 sched_idle_set_state <- cpuidle_enter_state+0x44/0x314
CPU:1 ts:5916720 c0160f59 c015ee04 kernfs_unmap_bin_file <- __kernfs_remove+0x140/0x204
CPU:1 ts:5916721 c05ca625 c015ee0c __mutex_lock_slowpath <- __kernfs_remove+0x148/0x204
CPU:1 ts:5916723 c05c813d c05ca630 yield_to <- __mutex_lock_slowpath+0x314/0x358
CPU:1 ts:5916724 c05ca2d1 c05ca638 __ww_mutex_lock <- __mutex_lock_slowpath+0x31c/0x358
max_reason
~~~~~~~~~~
Limiting which kinds of kmsg dumps are stored can be controlled via
the ``max_reason`` value, as defined in include/linux/kmsg_dump.h's
``enum kmsg_dump_reason``. For example, to store both Oopses and Panics,
``max_reason`` should be set to 2 (KMSG_DUMP_OOPS), to store only Panics
``max_reason`` should be set to 1 (KMSG_DUMP_PANIC). Setting this to 0
(KMSG_DUMP_UNDEF), means the reason filtering will be controlled by the
``printk.always_kmsg_dump`` boot param: if unset, it'll be KMSG_DUMP_OOPS,
otherwise KMSG_DUMP_MAX.
Configurations for driver
-------------------------
Only a block device driver cares about these configurations. A block device
driver uses ``register_pstore_blk`` to register to pstore/blk.
.. kernel-doc:: fs/pstore/blk.c
:identifiers: register_pstore_blk
A non-block device driver uses ``register_pstore_device`` with
``struct pstore_device_info`` to register to pstore/blk.
.. kernel-doc:: fs/pstore/blk.c
:identifiers: register_pstore_device
.. kernel-doc:: include/linux/pstore_blk.h
:identifiers: pstore_device_info
Compression and header
----------------------
Block device is large enough for uncompressed oops data. Actually we do not
recommend data compression because pstore/blk will insert some information into
the first line of oops/panic data. For example::
Panic: Total 16 times
It means that it's OOPS|Panic for the 16th time since the first booting.
Sometimes the number of occurrences of oops|panic since the first booting is
important to judge whether the system is stable.
The following line is inserted by pstore filesystem. For example::
Oops#2 Part1
It means that it's OOPS for the 2nd time on the last boot.
Reading the data
----------------
The dump data can be read from the pstore filesystem. The format for these
files is ``dmesg-pstore-blk-[N]`` for oops/panic front-end,
``pmsg-pstore-blk-0`` for pmsg front-end and so on. The timestamp of the
dump file records the trigger time. To delete a stored record from block
device, simply unlink the respective pstore file.
Attentions in panic read/write APIs
-----------------------------------
If on panic, the kernel is not going to run for much longer, the tasks will not
be scheduled and most kernel resources will be out of service. It
looks like a single-threaded program running on a single-core computer.
The following points require special attention for panic read/write APIs:
1. Can **NOT** allocate any memory.
If you need memory, just allocate while the block driver is initializing
rather than waiting until the panic.
#. Must be polled, **NOT** interrupt driven.
No task schedule any more. The block driver should delay to ensure the write
succeeds, but NOT sleep.
#. Can **NOT** take any lock.
There is no other task, nor any shared resource; you are safe to break all
locks.
#. Just use CPU to transfer.
Do not use DMA to transfer unless you are sure that DMA will not keep lock.
#. Control registers directly.
Please control registers directly rather than use Linux kernel resources.
Do I/O map while initializing rather than wait until a panic occurs.
#. Reset your block device and controller if necessary.
If you are not sure of the state of your block device and controller when
a panic occurs, you are safe to stop and reset them.
pstore/blk supports psblk_blkdev_info(), which is defined in
*linux/pstore_blk.h*, to get information of using block device, such as the
device number, sector count and start sector of the whole disk.
pstore block internals
----------------------
For developer reference, here are all the important structures and APIs:
.. kernel-doc:: fs/pstore/zone.c
:internal:
.. kernel-doc:: include/linux/pstore_zone.h
:internal:
.. kernel-doc:: fs/pstore/blk.c
:export:
.. kernel-doc:: include/linux/pstore_blk.h
:internal:

View File

@ -32,11 +32,17 @@ memory to be mapped strongly ordered, and atomic operations on strongly ordered
memory are implementation defined, and won't work on many ARMs such as omaps.
The memory area is divided into ``record_size`` chunks (also rounded down to
power of two) and each oops/panic writes a ``record_size`` chunk of
power of two) and each kmesg dump writes a ``record_size`` chunk of
information.
Dumping both oopses and panics can be done by setting 1 in the ``dump_oops``
variable while setting 0 in that variable dumps only the panics.
Limiting which kinds of kmsg dumps are stored can be controlled via
the ``max_reason`` value, as defined in include/linux/kmsg_dump.h's
``enum kmsg_dump_reason``. For example, to store both Oopses and Panics,
``max_reason`` should be set to 2 (KMSG_DUMP_OOPS), to store only Panics
``max_reason`` should be set to 1 (KMSG_DUMP_PANIC). Setting this to 0
(KMSG_DUMP_UNDEF), means the reason filtering will be controlled by the
``printk.always_kmsg_dump`` boot param: if unset, it'll be KMSG_DUMP_OOPS,
otherwise KMSG_DUMP_MAX.
The module uses a counter to record multiple dumps but the counter gets reset
on restart (i.e. new dumps after the restart will overwrite old ones).
@ -90,7 +96,7 @@ Setting the ramoops parameters can be done in several different manners:
.mem_address = <...>,
.mem_type = <...>,
.record_size = <...>,
.dump_oops = <...>,
.max_reason = <...>,
.ecc = <...>,
};

View File

@ -30,7 +30,7 @@ Optional properties:
- ecc-size: enables ECC support and specifies ECC buffer size in bytes
(defaults to 0: no ECC)
- record-size: maximum size in bytes of each dump done on oops/panic
- record-size: maximum size in bytes of each kmsg dump.
(defaults to 0: disabled)
- console-size: size in bytes of log buffer reserved for kernel messages
@ -45,7 +45,16 @@ Optional properties:
- unbuffered: if present, use unbuffered mappings to map the reserved region
(defaults to buffered mappings)
- no-dump-oops: if present, only dump panics (defaults to panics and oops)
- max-reason: if present, sets maximum type of kmsg dump reasons to store
(defaults to 2: log Oopses and Panics). This can be set to INT_MAX to
store all kmsg dumps. See include/linux/kmsg_dump.h KMSG_DUMP_* for other
kmsg dump reason values. Setting this to 0 (KMSG_DUMP_UNDEF), means the
reason filtering will be controlled by the printk.always_kmsg_dump boot
param: if unset, it will be KMSG_DUMP_OOPS, otherwise KMSG_DUMP_MAX.
- no-dump-oops: deprecated, use max_reason instead. If present, and
max_reason is not specified, it is equivalent to max_reason = 1
(KMSG_DUMP_PANIC).
- flags: if present, pass ramoops behavioral flags (defaults to 0,
see include/linux/pstore_ram.h RAMOOPS_FLAG_* for flag values).

View File

@ -225,8 +225,12 @@ fsync_mode=%s Control the policy of fsync. Currently supports "posix",
pass, but the performance will regress. "nobarrier" is
based on "posix", but doesn't issue flush command for
non-atomic files likewise "nobarrier" mount option.
test_dummy_encryption Enable dummy encryption, which provides a fake fscrypt
test_dummy_encryption
test_dummy_encryption=%s
Enable dummy encryption, which provides a fake fscrypt
context. The fake fscrypt context is used by xfstests.
The argument may be either "v1" or "v2", in order to
select the corresponding fscrypt policy version.
checkpoint=%s[:%u[%]] Set to "disable" to turn off checkpointing. Set to "enable"
to reenable checkpointing. Is enabled by default. While
disabled, any unmounting or unexpected shutdowns will cause

View File

@ -13721,6 +13721,7 @@ M: Tony Luck <tony.luck@intel.com>
S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git for-next/pstore
F: Documentation/admin-guide/ramoops.rst
F: Documentation/admin-guide/pstore-blk.rst
F: Documentation/devicetree/bindings/reserved-memory/ramoops.txt
F: drivers/acpi/apei/erst.c
F: drivers/firmware/efi/efi-pstore.c

View File

@ -655,9 +655,7 @@ static void oops_to_nvram(struct kmsg_dumper *dumper,
int rc = -1;
switch (reason) {
case KMSG_DUMP_RESTART:
case KMSG_DUMP_HALT:
case KMSG_DUMP_POWEROFF:
case KMSG_DUMP_SHUTDOWN:
/* These are almost always orderly shutdowns. */
return;
case KMSG_DUMP_OOPS:

View File

@ -170,6 +170,16 @@ config MTD_OOPS
buffer in a flash partition where it can be read back at some
later point.
config MTD_PSTORE
tristate "Log panic/oops to an MTD buffer based on pstore"
depends on PSTORE_BLK
help
This enables panic and oops messages to be logged to a circular
buffer in a flash partition where it can be read back as files after
mounting pstore filesystem.
If unsure, say N.
config MTD_SWAP
tristate "Swap on MTD device support"
depends on MTD && SWAP

View File

@ -20,6 +20,7 @@ obj-$(CONFIG_RFD_FTL) += rfd_ftl.o
obj-$(CONFIG_SSFDC) += ssfdc.o
obj-$(CONFIG_SM_FTL) += sm_ftl.o
obj-$(CONFIG_MTD_OOPS) += mtdoops.o
obj-$(CONFIG_MTD_PSTORE) += mtdpstore.o
obj-$(CONFIG_MTD_SWAP) += mtdswap.o
nftl-objs := nftlcore.o nftlmount.o

578
drivers/mtd/mtdpstore.c Normal file
View File

@ -0,0 +1,578 @@
// SPDX-License-Identifier: GPL-2.0
#define dev_fmt(fmt) "mtdoops-pstore: " fmt
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pstore_blk.h>
#include <linux/mtd/mtd.h>
#include <linux/bitops.h>
static struct mtdpstore_context {
int index;
struct pstore_blk_config info;
struct pstore_device_info dev;
struct mtd_info *mtd;
unsigned long *rmmap; /* removed bit map */
unsigned long *usedmap; /* used bit map */
/*
* used for panic write
* As there are no block_isbad for panic case, we should keep this
* status before panic to ensure panic_write not failed.
*/
unsigned long *badmap; /* bad block bit map */
} oops_cxt;
static int mtdpstore_block_isbad(struct mtdpstore_context *cxt, loff_t off)
{
int ret;
struct mtd_info *mtd = cxt->mtd;
u64 blknum;
off = ALIGN_DOWN(off, mtd->erasesize);
blknum = div_u64(off, mtd->erasesize);
if (test_bit(blknum, cxt->badmap))
return true;
ret = mtd_block_isbad(mtd, off);
if (ret < 0) {
dev_err(&mtd->dev, "mtd_block_isbad failed, aborting\n");
return ret;
} else if (ret > 0) {
set_bit(blknum, cxt->badmap);
return true;
}
return false;
}
static inline int mtdpstore_panic_block_isbad(struct mtdpstore_context *cxt,
loff_t off)
{
struct mtd_info *mtd = cxt->mtd;
u64 blknum;
off = ALIGN_DOWN(off, mtd->erasesize);
blknum = div_u64(off, mtd->erasesize);
return test_bit(blknum, cxt->badmap);
}
static inline void mtdpstore_mark_used(struct mtdpstore_context *cxt,
loff_t off)
{
struct mtd_info *mtd = cxt->mtd;
u64 zonenum = div_u64(off, cxt->info.kmsg_size);
dev_dbg(&mtd->dev, "mark zone %llu used\n", zonenum);
set_bit(zonenum, cxt->usedmap);
}
static inline void mtdpstore_mark_unused(struct mtdpstore_context *cxt,
loff_t off)
{
struct mtd_info *mtd = cxt->mtd;
u64 zonenum = div_u64(off, cxt->info.kmsg_size);
dev_dbg(&mtd->dev, "mark zone %llu unused\n", zonenum);
clear_bit(zonenum, cxt->usedmap);
}
static inline void mtdpstore_block_mark_unused(struct mtdpstore_context *cxt,
loff_t off)
{
struct mtd_info *mtd = cxt->mtd;
u32 zonecnt = mtd->erasesize / cxt->info.kmsg_size;
u64 zonenum;
off = ALIGN_DOWN(off, mtd->erasesize);
zonenum = div_u64(off, cxt->info.kmsg_size);
while (zonecnt > 0) {
dev_dbg(&mtd->dev, "mark zone %llu unused\n", zonenum);
clear_bit(zonenum, cxt->usedmap);
zonenum++;
zonecnt--;
}
}
static inline int mtdpstore_is_used(struct mtdpstore_context *cxt, loff_t off)
{
u64 zonenum = div_u64(off, cxt->info.kmsg_size);
u64 blknum = div_u64(off, cxt->mtd->erasesize);
if (test_bit(blknum, cxt->badmap))
return true;
return test_bit(zonenum, cxt->usedmap);
}
static int mtdpstore_block_is_used(struct mtdpstore_context *cxt,
loff_t off)
{
struct mtd_info *mtd = cxt->mtd;
u32 zonecnt = mtd->erasesize / cxt->info.kmsg_size;
u64 zonenum;
off = ALIGN_DOWN(off, mtd->erasesize);
zonenum = div_u64(off, cxt->info.kmsg_size);
while (zonecnt > 0) {
if (test_bit(zonenum, cxt->usedmap))
return true;
zonenum++;
zonecnt--;
}
return false;
}
static int mtdpstore_is_empty(struct mtdpstore_context *cxt, char *buf,
size_t size)
{
struct mtd_info *mtd = cxt->mtd;
size_t sz;
int i;
sz = min_t(uint32_t, size, mtd->writesize / 4);
for (i = 0; i < sz; i++) {
if (buf[i] != (char)0xFF)
return false;
}
return true;
}
static void mtdpstore_mark_removed(struct mtdpstore_context *cxt, loff_t off)
{
struct mtd_info *mtd = cxt->mtd;
u64 zonenum = div_u64(off, cxt->info.kmsg_size);
dev_dbg(&mtd->dev, "mark zone %llu removed\n", zonenum);
set_bit(zonenum, cxt->rmmap);
}
static void mtdpstore_block_clear_removed(struct mtdpstore_context *cxt,
loff_t off)
{
struct mtd_info *mtd = cxt->mtd;
u32 zonecnt = mtd->erasesize / cxt->info.kmsg_size;
u64 zonenum;
off = ALIGN_DOWN(off, mtd->erasesize);
zonenum = div_u64(off, cxt->info.kmsg_size);
while (zonecnt > 0) {
clear_bit(zonenum, cxt->rmmap);
zonenum++;
zonecnt--;
}
}
static int mtdpstore_block_is_removed(struct mtdpstore_context *cxt,
loff_t off)
{
struct mtd_info *mtd = cxt->mtd;
u32 zonecnt = mtd->erasesize / cxt->info.kmsg_size;
u64 zonenum;
off = ALIGN_DOWN(off, mtd->erasesize);
zonenum = div_u64(off, cxt->info.kmsg_size);
while (zonecnt > 0) {
if (test_bit(zonenum, cxt->rmmap))
return true;
zonenum++;
zonecnt--;
}
return false;
}
static int mtdpstore_erase_do(struct mtdpstore_context *cxt, loff_t off)
{
struct mtd_info *mtd = cxt->mtd;
struct erase_info erase;
int ret;
off = ALIGN_DOWN(off, cxt->mtd->erasesize);
dev_dbg(&mtd->dev, "try to erase off 0x%llx\n", off);
erase.len = cxt->mtd->erasesize;
erase.addr = off;
ret = mtd_erase(cxt->mtd, &erase);
if (!ret)
mtdpstore_block_clear_removed(cxt, off);
else
dev_err(&mtd->dev, "erase of region [0x%llx, 0x%llx] on \"%s\" failed\n",
(unsigned long long)erase.addr,
(unsigned long long)erase.len, cxt->info.device);
return ret;
}
/*
* called while removing file
*
* Avoiding over erasing, do erase block only when the whole block is unused.
* If the block contains valid log, do erase lazily on flush_removed() when
* unregister.
*/
static ssize_t mtdpstore_erase(size_t size, loff_t off)
{
struct mtdpstore_context *cxt = &oops_cxt;
if (mtdpstore_block_isbad(cxt, off))
return -EIO;
mtdpstore_mark_unused(cxt, off);
/* If the block still has valid data, mtdpstore do erase lazily */
if (likely(mtdpstore_block_is_used(cxt, off))) {
mtdpstore_mark_removed(cxt, off);
return 0;
}
/* all zones are unused, erase it */
return mtdpstore_erase_do(cxt, off);
}
/*
* What is security for mtdpstore?
* As there is no erase for panic case, we should ensure at least one zone
* is writable. Otherwise, panic write will fail.
* If zone is used, write operation will return -ENOMSG, which means that
* pstore/blk will try one by one until gets an empty zone. So, it is not
* needed to ensure the next zone is empty, but at least one.
*/
static int mtdpstore_security(struct mtdpstore_context *cxt, loff_t off)
{
int ret = 0, i;
struct mtd_info *mtd = cxt->mtd;
u32 zonenum = (u32)div_u64(off, cxt->info.kmsg_size);
u32 zonecnt = (u32)div_u64(cxt->mtd->size, cxt->info.kmsg_size);
u32 blkcnt = (u32)div_u64(cxt->mtd->size, cxt->mtd->erasesize);
u32 erasesize = cxt->mtd->erasesize;
for (i = 0; i < zonecnt; i++) {
u32 num = (zonenum + i) % zonecnt;
/* found empty zone */
if (!test_bit(num, cxt->usedmap))
return 0;
}
/* If there is no any empty zone, we have no way but to do erase */
while (blkcnt--) {
div64_u64_rem(off + erasesize, cxt->mtd->size, (u64 *)&off);
if (mtdpstore_block_isbad(cxt, off))
continue;
ret = mtdpstore_erase_do(cxt, off);
if (!ret) {
mtdpstore_block_mark_unused(cxt, off);
break;
}
}
if (ret)
dev_err(&mtd->dev, "all blocks bad!\n");
dev_dbg(&mtd->dev, "end security\n");
return ret;
}
static ssize_t mtdpstore_write(const char *buf, size_t size, loff_t off)
{
struct mtdpstore_context *cxt = &oops_cxt;
struct mtd_info *mtd = cxt->mtd;
size_t retlen;
int ret;
if (mtdpstore_block_isbad(cxt, off))
return -ENOMSG;
/* zone is used, please try next one */
if (mtdpstore_is_used(cxt, off))
return -ENOMSG;
dev_dbg(&mtd->dev, "try to write off 0x%llx size %zu\n", off, size);
ret = mtd_write(cxt->mtd, off, size, &retlen, (u_char *)buf);
if (ret < 0 || retlen != size) {
dev_err(&mtd->dev, "write failure at %lld (%zu of %zu written), err %d\n",
off, retlen, size, ret);
return -EIO;
}
mtdpstore_mark_used(cxt, off);
mtdpstore_security(cxt, off);
return retlen;
}
static inline bool mtdpstore_is_io_error(int ret)
{
return ret < 0 && !mtd_is_bitflip(ret) && !mtd_is_eccerr(ret);
}
/*
* All zones will be read as pstore/blk will read zone one by one when do
* recover.
*/
static ssize_t mtdpstore_read(char *buf, size_t size, loff_t off)
{
struct mtdpstore_context *cxt = &oops_cxt;
struct mtd_info *mtd = cxt->mtd;
size_t retlen, done;
int ret;
if (mtdpstore_block_isbad(cxt, off))
return -ENOMSG;
dev_dbg(&mtd->dev, "try to read off 0x%llx size %zu\n", off, size);
for (done = 0, retlen = 0; done < size; done += retlen) {
retlen = 0;
ret = mtd_read(cxt->mtd, off + done, size - done, &retlen,
(u_char *)buf + done);
if (mtdpstore_is_io_error(ret)) {
dev_err(&mtd->dev, "read failure at %lld (%zu of %zu read), err %d\n",
off + done, retlen, size - done, ret);
/* the zone may be broken, try next one */
return -ENOMSG;
}
/*
* ECC error. The impact on log data is so small. Maybe we can
* still read it and try to understand. So mtdpstore just hands
* over what it gets and user can judge whether the data is
* valid or not.
*/
if (mtd_is_eccerr(ret)) {
dev_err(&mtd->dev, "ecc error at %lld (%zu of %zu read), err %d\n",
off + done, retlen, size - done, ret);
/* driver may not set retlen when ecc error */
retlen = retlen == 0 ? size - done : retlen;
}
}
if (mtdpstore_is_empty(cxt, buf, size))
mtdpstore_mark_unused(cxt, off);
else
mtdpstore_mark_used(cxt, off);
mtdpstore_security(cxt, off);
return retlen;
}
static ssize_t mtdpstore_panic_write(const char *buf, size_t size, loff_t off)
{
struct mtdpstore_context *cxt = &oops_cxt;
struct mtd_info *mtd = cxt->mtd;
size_t retlen;
int ret;
if (mtdpstore_panic_block_isbad(cxt, off))
return -ENOMSG;
/* zone is used, please try next one */
if (mtdpstore_is_used(cxt, off))
return -ENOMSG;
ret = mtd_panic_write(cxt->mtd, off, size, &retlen, (u_char *)buf);
if (ret < 0 || size != retlen) {
dev_err(&mtd->dev, "panic write failure at %lld (%zu of %zu read), err %d\n",
off, retlen, size, ret);
return -EIO;
}
mtdpstore_mark_used(cxt, off);
return retlen;
}
static void mtdpstore_notify_add(struct mtd_info *mtd)
{
int ret;
struct mtdpstore_context *cxt = &oops_cxt;
struct pstore_blk_config *info = &cxt->info;
unsigned long longcnt;
if (!strcmp(mtd->name, info->device))
cxt->index = mtd->index;
if (mtd->index != cxt->index || cxt->index < 0)
return;
dev_dbg(&mtd->dev, "found matching MTD device %s\n", mtd->name);
if (mtd->size < info->kmsg_size * 2) {
dev_err(&mtd->dev, "MTD partition %d not big enough\n",
mtd->index);
return;
}
/*
* kmsg_size must be aligned to 4096 Bytes, which is limited by
* psblk. The default value of kmsg_size is 64KB. If kmsg_size
* is larger than erasesize, some errors will occur since mtdpsotre
* is designed on it.
*/
if (mtd->erasesize < info->kmsg_size) {
dev_err(&mtd->dev, "eraseblock size of MTD partition %d too small\n",
mtd->index);
return;
}
if (unlikely(info->kmsg_size % mtd->writesize)) {
dev_err(&mtd->dev, "record size %lu KB must align to write size %d KB\n",
info->kmsg_size / 1024,
mtd->writesize / 1024);
return;
}
longcnt = BITS_TO_LONGS(div_u64(mtd->size, info->kmsg_size));
cxt->rmmap = kcalloc(longcnt, sizeof(long), GFP_KERNEL);
cxt->usedmap = kcalloc(longcnt, sizeof(long), GFP_KERNEL);
longcnt = BITS_TO_LONGS(div_u64(mtd->size, mtd->erasesize));
cxt->badmap = kcalloc(longcnt, sizeof(long), GFP_KERNEL);
cxt->dev.total_size = mtd->size;
/* just support dmesg right now */
cxt->dev.flags = PSTORE_FLAGS_DMESG;
cxt->dev.read = mtdpstore_read;
cxt->dev.write = mtdpstore_write;
cxt->dev.erase = mtdpstore_erase;
cxt->dev.panic_write = mtdpstore_panic_write;
ret = register_pstore_device(&cxt->dev);
if (ret) {
dev_err(&mtd->dev, "mtd%d register to psblk failed\n",
mtd->index);
return;
}
cxt->mtd = mtd;
dev_info(&mtd->dev, "Attached to MTD device %d\n", mtd->index);
}
static int mtdpstore_flush_removed_do(struct mtdpstore_context *cxt,
loff_t off, size_t size)
{
struct mtd_info *mtd = cxt->mtd;
u_char *buf;
int ret;
size_t retlen;
struct erase_info erase;
buf = kmalloc(mtd->erasesize, GFP_KERNEL);
if (!buf)
return -ENOMEM;
/* 1st. read to cache */
ret = mtd_read(mtd, off, mtd->erasesize, &retlen, buf);
if (mtdpstore_is_io_error(ret))
goto free;
/* 2nd. erase block */
erase.len = mtd->erasesize;
erase.addr = off;
ret = mtd_erase(mtd, &erase);
if (ret)
goto free;
/* 3rd. write back */
while (size) {
unsigned int zonesize = cxt->info.kmsg_size;
/* there is valid data on block, write back */
if (mtdpstore_is_used(cxt, off)) {
ret = mtd_write(mtd, off, zonesize, &retlen, buf);
if (ret)
dev_err(&mtd->dev, "write failure at %lld (%zu of %u written), err %d\n",
off, retlen, zonesize, ret);
}
off += zonesize;
size -= min_t(unsigned int, zonesize, size);
}
free:
kfree(buf);
return ret;
}
/*
* What does mtdpstore_flush_removed() do?
* When user remove any log file on pstore filesystem, mtdpstore should do
* something to ensure log file removed. If the whole block is no longer used,
* it's nice to erase the block. However if the block still contains valid log,
* what mtdpstore can do is to erase and write the valid log back.
*/
static int mtdpstore_flush_removed(struct mtdpstore_context *cxt)
{
struct mtd_info *mtd = cxt->mtd;
int ret;
loff_t off;
u32 blkcnt = (u32)div_u64(mtd->size, mtd->erasesize);
for (off = 0; blkcnt > 0; blkcnt--, off += mtd->erasesize) {
ret = mtdpstore_block_isbad(cxt, off);
if (ret)
continue;
ret = mtdpstore_block_is_removed(cxt, off);
if (!ret)
continue;
ret = mtdpstore_flush_removed_do(cxt, off, mtd->erasesize);
if (ret)
return ret;
}
return 0;
}
static void mtdpstore_notify_remove(struct mtd_info *mtd)
{
struct mtdpstore_context *cxt = &oops_cxt;
if (mtd->index != cxt->index || cxt->index < 0)
return;
mtdpstore_flush_removed(cxt);
unregister_pstore_device(&cxt->dev);
kfree(cxt->badmap);
kfree(cxt->usedmap);
kfree(cxt->rmmap);
cxt->mtd = NULL;
cxt->index = -1;
}
static struct mtd_notifier mtdpstore_notifier = {
.add = mtdpstore_notify_add,
.remove = mtdpstore_notify_remove,
};
static int __init mtdpstore_init(void)
{
int ret;
struct mtdpstore_context *cxt = &oops_cxt;
struct pstore_blk_config *info = &cxt->info;
ret = pstore_blk_get_config(info);
if (unlikely(ret))
return ret;
if (strlen(info->device) == 0) {
pr_err("mtd device must be supplied (device name is empty)\n");
return -EINVAL;
}
if (!info->kmsg_size) {
pr_err("no backend enabled (kmsg_size is 0)\n");
return -EINVAL;
}
/* Setup the MTD device to use */
ret = kstrtoint((char *)info->device, 0, &cxt->index);
if (ret)
cxt->index = -1;
register_mtd_user(&mtdpstore_notifier);
return 0;
}
module_init(mtdpstore_init);
static void __exit mtdpstore_exit(void)
{
unregister_mtd_user(&mtdpstore_notifier);
}
module_exit(mtdpstore_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("WeiXiong Liao <liaoweixiong@allwinnertech.com>");
MODULE_DESCRIPTION("MTD backend for pstore/blk");

View File

@ -57,7 +57,7 @@ static struct ramoops_platform_data chromeos_ramoops_data = {
.record_size = 0x40000,
.console_size = 0x20000,
.ftrace_size = 0x20000,
.dump_oops = 1,
.max_reason = KMSG_DUMP_OOPS,
};
static struct platform_device chromeos_ramoops = {

View File

@ -54,6 +54,7 @@ struct page *fscrypt_alloc_bounce_page(gfp_t gfp_flags)
/**
* fscrypt_free_bounce_page() - free a ciphertext bounce page
* @bounce_page: the bounce page to free, or NULL
*
* Free a bounce page that was allocated by fscrypt_encrypt_pagecache_blocks(),
* or by fscrypt_alloc_bounce_page() directly.
@ -144,7 +145,8 @@ int fscrypt_crypt_block(const struct inode *inode, fscrypt_direction_t rw,
}
/**
* fscrypt_encrypt_pagecache_blocks() - Encrypt filesystem blocks from a pagecache page
* fscrypt_encrypt_pagecache_blocks() - Encrypt filesystem blocks from a
* pagecache page
* @page: The locked pagecache page containing the block(s) to encrypt
* @len: Total size of the block(s) to encrypt. Must be a nonzero
* multiple of the filesystem's block size.
@ -234,7 +236,8 @@ int fscrypt_encrypt_block_inplace(const struct inode *inode, struct page *page,
EXPORT_SYMBOL(fscrypt_encrypt_block_inplace);
/**
* fscrypt_decrypt_pagecache_blocks() - Decrypt filesystem blocks in a pagecache page
* fscrypt_decrypt_pagecache_blocks() - Decrypt filesystem blocks in a
* pagecache page
* @page: The locked pagecache page containing the block(s) to decrypt
* @len: Total size of the block(s) to decrypt. Must be a nonzero
* multiple of the filesystem's block size.
@ -358,6 +361,8 @@ void fscrypt_msg(const struct inode *inode, const char *level,
/**
* fscrypt_init() - Set up for fs encryption.
*
* Return: 0 on success; -errno on failure
*/
static int __init fscrypt_init(void)
{

View File

@ -18,7 +18,7 @@
#include <crypto/skcipher.h>
#include "fscrypt_private.h"
/**
/*
* struct fscrypt_nokey_name - identifier for directory entry when key is absent
*
* When userspace lists an encrypted directory without access to the key, the
@ -100,9 +100,12 @@ static inline bool fscrypt_is_dot_dotdot(const struct qstr *str)
/**
* fscrypt_fname_encrypt() - encrypt a filename
*
* The output buffer must be at least as large as the input buffer.
* Any extra space is filled with NUL padding before encryption.
* @inode: inode of the parent directory (for regular filenames)
* or of the symlink (for symlink targets)
* @iname: the filename to encrypt
* @out: (output) the encrypted filename
* @olen: size of the encrypted filename. It must be at least @iname->len.
* Any extra space is filled with NUL padding before encryption.
*
* Return: 0 on success, -errno on failure
*/
@ -152,8 +155,11 @@ int fscrypt_fname_encrypt(const struct inode *inode, const struct qstr *iname,
/**
* fname_decrypt() - decrypt a filename
*
* The caller must have allocated sufficient memory for the @oname string.
* @inode: inode of the parent directory (for regular filenames)
* or of the symlink (for symlink targets)
* @iname: the encrypted filename to decrypt
* @oname: (output) the decrypted filename. The caller must have allocated
* enough space for this, e.g. using fscrypt_fname_alloc_buffer().
*
* Return: 0 on success, -errno on failure
*/
@ -201,7 +207,10 @@ static const char lookup_table[65] =
#define BASE64_CHARS(nbytes) DIV_ROUND_UP((nbytes) * 4, 3)
/**
* base64_encode() -
* base64_encode() - base64-encode some bytes
* @src: the bytes to encode
* @len: number of bytes to encode
* @dst: (output) the base64-encoded string. Not NUL-terminated.
*
* Encodes the input string using characters from the set [A-Za-z0-9+,].
* The encoded string is roughly 4/3 times the size of the input string.
@ -267,7 +276,12 @@ bool fscrypt_fname_encrypted_size(const struct inode *inode, u32 orig_len,
}
/**
* fscrypt_fname_alloc_buffer - allocate a buffer for presented filenames
* fscrypt_fname_alloc_buffer() - allocate a buffer for presented filenames
* @inode: inode of the parent directory (for regular filenames)
* or of the symlink (for symlink targets)
* @max_encrypted_len: maximum length of encrypted filenames the buffer will be
* used to present
* @crypto_str: (output) buffer to allocate
*
* Allocate a buffer that is large enough to hold any decrypted or encoded
* filename (null-terminated), for the given maximum encrypted filename length.
@ -292,9 +306,10 @@ int fscrypt_fname_alloc_buffer(const struct inode *inode,
EXPORT_SYMBOL(fscrypt_fname_alloc_buffer);
/**
* fscrypt_fname_free_buffer - free the buffer for presented filenames
* fscrypt_fname_free_buffer() - free a buffer for presented filenames
* @crypto_str: the buffer to free
*
* Free the buffer allocated by fscrypt_fname_alloc_buffer().
* Free a buffer that was allocated by fscrypt_fname_alloc_buffer().
*/
void fscrypt_fname_free_buffer(struct fscrypt_str *crypto_str)
{
@ -306,10 +321,19 @@ void fscrypt_fname_free_buffer(struct fscrypt_str *crypto_str)
EXPORT_SYMBOL(fscrypt_fname_free_buffer);
/**
* fscrypt_fname_disk_to_usr() - converts a filename from disk space to user
* space
*
* The caller must have allocated sufficient memory for the @oname string.
* fscrypt_fname_disk_to_usr() - convert an encrypted filename to
* user-presentable form
* @inode: inode of the parent directory (for regular filenames)
* or of the symlink (for symlink targets)
* @hash: first part of the name's dirhash, if applicable. This only needs to
* be provided if the filename is located in an indexed directory whose
* encryption key may be unavailable. Not needed for symlink targets.
* @minor_hash: second part of the name's dirhash, if applicable
* @iname: encrypted filename to convert. May also be "." or "..", which
* aren't actually encrypted.
* @oname: output buffer for the user-presentable filename. The caller must
* have allocated enough space for this, e.g. using
* fscrypt_fname_alloc_buffer().
*
* If the key is available, we'll decrypt the disk name. Otherwise, we'll
* encode it for presentation in fscrypt_nokey_name format.

View File

@ -45,7 +45,7 @@ struct fscrypt_context_v2 {
u8 nonce[FS_KEY_DERIVATION_NONCE_SIZE];
};
/**
/*
* fscrypt_context - the encryption context of an inode
*
* This is the on-disk equivalent of an fscrypt_policy, stored alongside each
@ -159,7 +159,7 @@ fscrypt_policy_flags(const union fscrypt_policy *policy)
BUG();
}
/**
/*
* For encrypted symlinks, the ciphertext length is stored at the beginning
* of the string in little-endian format.
*/
@ -258,15 +258,14 @@ typedef enum {
/* crypto.c */
extern struct kmem_cache *fscrypt_info_cachep;
extern int fscrypt_initialize(unsigned int cop_flags);
extern int fscrypt_crypt_block(const struct inode *inode,
fscrypt_direction_t rw, u64 lblk_num,
struct page *src_page, struct page *dest_page,
unsigned int len, unsigned int offs,
gfp_t gfp_flags);
extern struct page *fscrypt_alloc_bounce_page(gfp_t gfp_flags);
int fscrypt_initialize(unsigned int cop_flags);
int fscrypt_crypt_block(const struct inode *inode, fscrypt_direction_t rw,
u64 lblk_num, struct page *src_page,
struct page *dest_page, unsigned int len,
unsigned int offs, gfp_t gfp_flags);
struct page *fscrypt_alloc_bounce_page(gfp_t gfp_flags);
extern void __printf(3, 4) __cold
void __printf(3, 4) __cold
fscrypt_msg(const struct inode *inode, const char *level, const char *fmt, ...);
#define fscrypt_warn(inode, fmt, ...) \
@ -292,12 +291,10 @@ void fscrypt_generate_iv(union fscrypt_iv *iv, u64 lblk_num,
const struct fscrypt_info *ci);
/* fname.c */
extern int fscrypt_fname_encrypt(const struct inode *inode,
const struct qstr *iname,
u8 *out, unsigned int olen);
extern bool fscrypt_fname_encrypted_size(const struct inode *inode,
u32 orig_len, u32 max_len,
u32 *encrypted_len_ret);
int fscrypt_fname_encrypt(const struct inode *inode, const struct qstr *iname,
u8 *out, unsigned int olen);
bool fscrypt_fname_encrypted_size(const struct inode *inode, u32 orig_len,
u32 max_len, u32 *encrypted_len_ret);
/* hkdf.c */
@ -305,8 +302,8 @@ struct fscrypt_hkdf {
struct crypto_shash *hmac_tfm;
};
extern int fscrypt_init_hkdf(struct fscrypt_hkdf *hkdf, const u8 *master_key,
unsigned int master_key_size);
int fscrypt_init_hkdf(struct fscrypt_hkdf *hkdf, const u8 *master_key,
unsigned int master_key_size);
/*
* The list of contexts in which fscrypt uses HKDF. These values are used as
@ -323,11 +320,11 @@ extern int fscrypt_init_hkdf(struct fscrypt_hkdf *hkdf, const u8 *master_key,
#define HKDF_CONTEXT_IV_INO_LBLK_32_KEY 6
#define HKDF_CONTEXT_INODE_HASH_KEY 7
extern int fscrypt_hkdf_expand(const struct fscrypt_hkdf *hkdf, u8 context,
const u8 *info, unsigned int infolen,
u8 *okm, unsigned int okmlen);
int fscrypt_hkdf_expand(const struct fscrypt_hkdf *hkdf, u8 context,
const u8 *info, unsigned int infolen,
u8 *okm, unsigned int okmlen);
extern void fscrypt_destroy_hkdf(struct fscrypt_hkdf *hkdf);
void fscrypt_destroy_hkdf(struct fscrypt_hkdf *hkdf);
/* inline_crypt.c */
#ifdef CONFIG_FS_ENCRYPTION_INLINE_CRYPT
@ -568,14 +565,17 @@ static inline int master_key_spec_len(const struct fscrypt_key_specifier *spec)
return 0;
}
extern struct key *
struct key *
fscrypt_find_master_key(struct super_block *sb,
const struct fscrypt_key_specifier *mk_spec);
extern int fscrypt_verify_key_added(struct super_block *sb,
const u8 identifier[FSCRYPT_KEY_IDENTIFIER_SIZE]);
int fscrypt_add_test_dummy_key(struct super_block *sb,
struct fscrypt_key_specifier *key_spec);
extern int __init fscrypt_init_keyring(void);
int fscrypt_verify_key_added(struct super_block *sb,
const u8 identifier[FSCRYPT_KEY_IDENTIFIER_SIZE]);
int __init fscrypt_init_keyring(void);
/* keysetup.c */
@ -590,36 +590,34 @@ struct fscrypt_mode {
extern struct fscrypt_mode fscrypt_modes[];
extern int fscrypt_prepare_key(struct fscrypt_prepared_key *prep_key,
const u8 *raw_key, unsigned int raw_key_size,
bool is_hw_wrapped,
const struct fscrypt_info *ci);
int fscrypt_prepare_key(struct fscrypt_prepared_key *prep_key,
const u8 *raw_key, unsigned int raw_key_size,
bool is_hw_wrapped, const struct fscrypt_info *ci);
extern void fscrypt_destroy_prepared_key(struct fscrypt_prepared_key *prep_key);
void fscrypt_destroy_prepared_key(struct fscrypt_prepared_key *prep_key);
extern int fscrypt_set_per_file_enc_key(struct fscrypt_info *ci,
const u8 *raw_key);
int fscrypt_set_per_file_enc_key(struct fscrypt_info *ci, const u8 *raw_key);
extern int fscrypt_derive_dirhash_key(struct fscrypt_info *ci,
const struct fscrypt_master_key *mk);
int fscrypt_derive_dirhash_key(struct fscrypt_info *ci,
const struct fscrypt_master_key *mk);
/* keysetup_v1.c */
extern void fscrypt_put_direct_key(struct fscrypt_direct_key *dk);
void fscrypt_put_direct_key(struct fscrypt_direct_key *dk);
extern int fscrypt_setup_v1_file_key(struct fscrypt_info *ci,
const u8 *raw_master_key);
int fscrypt_setup_v1_file_key(struct fscrypt_info *ci,
const u8 *raw_master_key);
int fscrypt_setup_v1_file_key_via_subscribed_keyrings(struct fscrypt_info *ci);
extern int fscrypt_setup_v1_file_key_via_subscribed_keyrings(
struct fscrypt_info *ci);
/* policy.c */
extern bool fscrypt_policies_equal(const union fscrypt_policy *policy1,
const union fscrypt_policy *policy2);
extern bool fscrypt_supported_policy(const union fscrypt_policy *policy_u,
const struct inode *inode);
extern int fscrypt_policy_from_context(union fscrypt_policy *policy_u,
const union fscrypt_context *ctx_u,
int ctx_size);
bool fscrypt_policies_equal(const union fscrypt_policy *policy1,
const union fscrypt_policy *policy2);
bool fscrypt_supported_policy(const union fscrypt_policy *policy_u,
const struct inode *inode);
int fscrypt_policy_from_context(union fscrypt_policy *policy_u,
const union fscrypt_context *ctx_u,
int ctx_size);
#endif /* _FSCRYPT_PRIVATE_H */

View File

@ -10,7 +10,7 @@
#include "fscrypt_private.h"
/**
* fscrypt_file_open - prepare to open a possibly-encrypted regular file
* fscrypt_file_open() - prepare to open a possibly-encrypted regular file
* @inode: the inode being opened
* @filp: the struct file being set up
*
@ -261,7 +261,7 @@ int __fscrypt_encrypt_symlink(struct inode *inode, const char *target,
EXPORT_SYMBOL_GPL(__fscrypt_encrypt_symlink);
/**
* fscrypt_get_symlink - get the target of an encrypted symlink
* fscrypt_get_symlink() - get the target of an encrypted symlink
* @inode: the symlink inode
* @caddr: the on-disk contents of the symlink
* @max_size: size of @caddr buffer

View File

@ -20,6 +20,7 @@
#include <crypto/skcipher.h>
#include <linux/key-type.h>
#include <linux/random.h>
#include <linux/seq_file.h>
#include "fscrypt_private.h"
@ -425,9 +426,9 @@ static int add_existing_master_key(struct fscrypt_master_key *mk,
return 0;
}
static int add_master_key(struct super_block *sb,
struct fscrypt_master_key_secret *secret,
const struct fscrypt_key_specifier *mk_spec)
static int do_add_master_key(struct super_block *sb,
struct fscrypt_master_key_secret *secret,
const struct fscrypt_key_specifier *mk_spec)
{
static DEFINE_MUTEX(fscrypt_add_key_mutex);
struct key *key;
@ -466,6 +467,49 @@ static int add_master_key(struct super_block *sb,
return err;
}
/* Size of software "secret" derived from hardware-wrapped key */
#define RAW_SECRET_SIZE 32
static int add_master_key(struct super_block *sb,
struct fscrypt_master_key_secret *secret,
struct fscrypt_key_specifier *key_spec)
{
int err;
if (key_spec->type == FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER) {
u8 _kdf_key[RAW_SECRET_SIZE];
u8 *kdf_key = secret->raw;
unsigned int kdf_key_size = secret->size;
if (secret->is_hw_wrapped) {
kdf_key = _kdf_key;
kdf_key_size = RAW_SECRET_SIZE;
err = fscrypt_derive_raw_secret(sb, secret->raw,
secret->size,
kdf_key, kdf_key_size);
if (err)
return err;
}
err = fscrypt_init_hkdf(&secret->hkdf, kdf_key, kdf_key_size);
/*
* Now that the HKDF context is initialized, the raw HKDF key is
* no longer needed.
*/
memzero_explicit(kdf_key, kdf_key_size);
if (err)
return err;
/* Calculate the key identifier */
err = fscrypt_hkdf_expand(&secret->hkdf,
HKDF_CONTEXT_KEY_IDENTIFIER, NULL, 0,
key_spec->u.identifier,
FSCRYPT_KEY_IDENTIFIER_SIZE);
if (err)
return err;
}
return do_add_master_key(sb, secret, key_spec);
}
static int fscrypt_provisioning_key_preparse(struct key_preparsed_payload *prep)
{
const struct fscrypt_provisioning_key_payload *payload = prep->data;
@ -571,9 +615,6 @@ static int get_keyring_key(u32 key_id, u32 type,
return err;
}
/* Size of software "secret" derived from hardware-wrapped key */
#define RAW_SECRET_SIZE 32
/*
* Add a master encryption key to the filesystem, causing all files which were
* encrypted with it to appear "unlocked" (decrypted) when accessed.
@ -604,9 +645,6 @@ int fscrypt_ioctl_add_key(struct file *filp, void __user *_uarg)
struct fscrypt_add_key_arg __user *uarg = _uarg;
struct fscrypt_add_key_arg arg;
struct fscrypt_master_key_secret secret;
u8 _kdf_key[RAW_SECRET_SIZE];
u8 *kdf_key;
unsigned int kdf_key_size;
int err;
if (copy_from_user(&arg, uarg, sizeof(arg)))
@ -618,7 +656,25 @@ int fscrypt_ioctl_add_key(struct file *filp, void __user *_uarg)
if (memchr_inv(arg.__reserved, 0, sizeof(arg.__reserved)))
return -EINVAL;
/*
* Only root can add keys that are identified by an arbitrary descriptor
* rather than by a cryptographic hash --- since otherwise a malicious
* user could add the wrong key.
*/
if (arg.key_spec.type == FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR &&
!capable(CAP_SYS_ADMIN))
return -EACCES;
memset(&secret, 0, sizeof(secret));
if (arg.__flags) {
if (arg.__flags & ~__FSCRYPT_ADD_KEY_FLAG_HW_WRAPPED)
return -EINVAL;
if (arg.key_spec.type != FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER)
return -EINVAL;
secret.is_hw_wrapped = true;
}
if (arg.key_id) {
if (arg.raw_size != 0)
return -EINVAL;
@ -626,14 +682,13 @@ int fscrypt_ioctl_add_key(struct file *filp, void __user *_uarg)
if (err)
goto out_wipe_secret;
err = -EINVAL;
if (!(arg.__flags & __FSCRYPT_ADD_KEY_FLAG_HW_WRAPPED) &&
secret.size > FSCRYPT_MAX_KEY_SIZE)
if (secret.size > FSCRYPT_MAX_KEY_SIZE && !secret.is_hw_wrapped)
goto out_wipe_secret;
} else {
if (arg.raw_size < FSCRYPT_MIN_KEY_SIZE ||
arg.raw_size >
((arg.__flags & __FSCRYPT_ADD_KEY_FLAG_HW_WRAPPED) ?
FSCRYPT_MAX_HW_WRAPPED_KEY_SIZE : FSCRYPT_MAX_KEY_SIZE))
arg.raw_size > (secret.is_hw_wrapped ?
FSCRYPT_MAX_HW_WRAPPED_KEY_SIZE :
FSCRYPT_MAX_KEY_SIZE))
return -EINVAL;
secret.size = arg.raw_size;
err = -EFAULT;
@ -641,73 +696,46 @@ int fscrypt_ioctl_add_key(struct file *filp, void __user *_uarg)
goto out_wipe_secret;
}
switch (arg.key_spec.type) {
case FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR:
/*
* Only root can add keys that are identified by an arbitrary
* descriptor rather than by a cryptographic hash --- since
* otherwise a malicious user could add the wrong key.
*/
err = -EACCES;
if (!capable(CAP_SYS_ADMIN))
goto out_wipe_secret;
err = -EINVAL;
if (arg.__flags)
goto out_wipe_secret;
break;
case FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER:
err = -EINVAL;
if (arg.__flags & ~__FSCRYPT_ADD_KEY_FLAG_HW_WRAPPED)
goto out_wipe_secret;
if (arg.__flags & __FSCRYPT_ADD_KEY_FLAG_HW_WRAPPED) {
kdf_key = _kdf_key;
kdf_key_size = RAW_SECRET_SIZE;
err = fscrypt_derive_raw_secret(sb, secret.raw,
secret.size,
kdf_key, kdf_key_size);
if (err)
goto out_wipe_secret;
secret.is_hw_wrapped = true;
} else {
kdf_key = secret.raw;
kdf_key_size = secret.size;
}
err = fscrypt_init_hkdf(&secret.hkdf, kdf_key, kdf_key_size);
/*
* Now that the HKDF context is initialized, the raw HKDF
* key is no longer needed.
*/
memzero_explicit(kdf_key, kdf_key_size);
if (err)
goto out_wipe_secret;
/* Calculate the key identifier and return it to userspace. */
err = fscrypt_hkdf_expand(&secret.hkdf,
HKDF_CONTEXT_KEY_IDENTIFIER,
NULL, 0, arg.key_spec.u.identifier,
FSCRYPT_KEY_IDENTIFIER_SIZE);
if (err)
goto out_wipe_secret;
err = -EFAULT;
if (copy_to_user(uarg->key_spec.u.identifier,
arg.key_spec.u.identifier,
FSCRYPT_KEY_IDENTIFIER_SIZE))
goto out_wipe_secret;
break;
default:
WARN_ON(1);
err = -EINVAL;
goto out_wipe_secret;
}
err = add_master_key(sb, &secret, &arg.key_spec);
if (err)
goto out_wipe_secret;
/* Return the key identifier to userspace, if applicable */
err = -EFAULT;
if (arg.key_spec.type == FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER &&
copy_to_user(uarg->key_spec.u.identifier, arg.key_spec.u.identifier,
FSCRYPT_KEY_IDENTIFIER_SIZE))
goto out_wipe_secret;
err = 0;
out_wipe_secret:
wipe_master_key_secret(&secret);
return err;
}
EXPORT_SYMBOL_GPL(fscrypt_ioctl_add_key);
/*
* Add the key for '-o test_dummy_encryption' to the filesystem keyring.
*
* Use a per-boot random key to prevent people from misusing this option.
*/
int fscrypt_add_test_dummy_key(struct super_block *sb,
struct fscrypt_key_specifier *key_spec)
{
static u8 test_key[FSCRYPT_MAX_KEY_SIZE];
struct fscrypt_master_key_secret secret;
int err;
get_random_once(test_key, FSCRYPT_MAX_KEY_SIZE);
memset(&secret, 0, sizeof(secret));
secret.size = FSCRYPT_MAX_KEY_SIZE;
memcpy(secret.raw, test_key, FSCRYPT_MAX_KEY_SIZE);
err = add_master_key(sb, &secret, key_spec);
wipe_master_key_secret(&secret);
return err;
}
/*
* Verify that the current user has added a master key with the given identifier
* (returns -ENOKEY if not). This is needed to prevent a user from encrypting

View File

@ -161,7 +161,6 @@ static int setup_per_mode_enc_key(struct fscrypt_info *ci,
struct fscrypt_prepared_key *keys,
u8 hkdf_context, bool include_fs_uuid)
{
static DEFINE_MUTEX(mode_key_setup_mutex);
const struct inode *inode = ci->ci_inode;
const struct super_block *sb = inode->i_sb;
struct fscrypt_mode *mode = ci->ci_mode;
@ -230,6 +229,7 @@ static int setup_per_mode_enc_key(struct fscrypt_info *ci,
}
done_unlock:
ci->ci_key = *prep_key;
err = 0;
out_unlock:
mutex_unlock(&fscrypt_mode_key_setup_mutex);
@ -513,21 +513,18 @@ int fscrypt_get_encryption_info(struct inode *inode)
res = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx));
if (res < 0) {
if (!fscrypt_dummy_context_enabled(inode) ||
IS_ENCRYPTED(inode)) {
const union fscrypt_context *dummy_ctx =
fscrypt_get_dummy_context(inode->i_sb);
if (IS_ENCRYPTED(inode) || !dummy_ctx) {
fscrypt_warn(inode,
"Error %d getting encryption context",
res);
return res;
}
/* Fake up a context for an unencrypted directory */
memset(&ctx, 0, sizeof(ctx));
ctx.version = FSCRYPT_CONTEXT_V1;
ctx.v1.contents_encryption_mode = FSCRYPT_MODE_AES_256_XTS;
ctx.v1.filenames_encryption_mode = FSCRYPT_MODE_AES_256_CTS;
memset(ctx.v1.master_key_descriptor, 0x42,
FSCRYPT_KEY_DESCRIPTOR_SIZE);
res = sizeof(ctx.v1);
res = fscrypt_context_size(dummy_ctx);
memcpy(&ctx, dummy_ctx, res);
}
crypt_info = kmem_cache_zalloc(fscrypt_info_cachep, GFP_NOFS);
@ -593,7 +590,8 @@ int fscrypt_get_encryption_info(struct inode *inode)
EXPORT_SYMBOL(fscrypt_get_encryption_info);
/**
* fscrypt_put_encryption_info - free most of an inode's fscrypt data
* fscrypt_put_encryption_info() - free most of an inode's fscrypt data
* @inode: an inode being evicted
*
* Free the inode's fscrypt_info. Filesystems must call this when the inode is
* being evicted. An RCU grace period need not have elapsed yet.
@ -606,7 +604,8 @@ void fscrypt_put_encryption_info(struct inode *inode)
EXPORT_SYMBOL(fscrypt_put_encryption_info);
/**
* fscrypt_free_inode - free an inode's fscrypt data requiring RCU delay
* fscrypt_free_inode() - free an inode's fscrypt data requiring RCU delay
* @inode: an inode being freed
*
* Free the inode's cached decrypted symlink target, if any. Filesystems must
* call this after an RCU grace period, just before they free the inode.
@ -621,7 +620,8 @@ void fscrypt_free_inode(struct inode *inode)
EXPORT_SYMBOL(fscrypt_free_inode);
/**
* fscrypt_drop_inode - check whether the inode's master key has been removed
* fscrypt_drop_inode() - check whether the inode's master key has been removed
* @inode: an inode being considered for eviction
*
* Filesystems supporting fscrypt must call this from their ->drop_inode()
* method so that encrypted inodes are evicted as soon as they're no longer in

View File

@ -11,12 +11,15 @@
*/
#include <linux/random.h>
#include <linux/seq_file.h>
#include <linux/string.h>
#include <linux/mount.h>
#include "fscrypt_private.h"
/**
* fscrypt_policies_equal - check whether two encryption policies are the same
* fscrypt_policies_equal() - check whether two encryption policies are the same
* @policy1: the first policy
* @policy2: the second policy
*
* Return: %true if equal, else %false
*/
@ -190,7 +193,9 @@ static bool fscrypt_supported_v2_policy(const struct fscrypt_policy_v2 *policy,
}
/**
* fscrypt_supported_policy - check whether an encryption policy is supported
* fscrypt_supported_policy() - check whether an encryption policy is supported
* @policy_u: the encryption policy
* @inode: the inode on which the policy will be used
*
* Given an encryption policy, check whether all its encryption modes and other
* settings are supported by this kernel on the given inode. (But we don't
@ -212,7 +217,10 @@ bool fscrypt_supported_policy(const union fscrypt_policy *policy_u,
}
/**
* fscrypt_new_context_from_policy - create a new fscrypt_context from a policy
* fscrypt_new_context_from_policy() - create a new fscrypt_context from
* an fscrypt_policy
* @ctx_u: output context
* @policy_u: input policy
*
* Create an fscrypt_context for an inode that is being assigned the given
* encryption policy. A new nonce is randomly generated.
@ -262,7 +270,11 @@ static int fscrypt_new_context_from_policy(union fscrypt_context *ctx_u,
}
/**
* fscrypt_policy_from_context - convert an fscrypt_context to an fscrypt_policy
* fscrypt_policy_from_context() - convert an fscrypt_context to
* an fscrypt_policy
* @policy_u: output policy
* @ctx_u: input context
* @ctx_size: size of input context in bytes
*
* Given an fscrypt_context, build the corresponding fscrypt_policy.
*
@ -628,3 +640,127 @@ int fscrypt_inherit_context(struct inode *parent, struct inode *child,
return preload ? fscrypt_get_encryption_info(child): 0;
}
EXPORT_SYMBOL(fscrypt_inherit_context);
/**
* fscrypt_set_test_dummy_encryption() - handle '-o test_dummy_encryption'
* @sb: the filesystem on which test_dummy_encryption is being specified
* @arg: the argument to the test_dummy_encryption option.
* If no argument was specified, then @arg->from == NULL.
* @dummy_ctx: the filesystem's current dummy context (input/output, see below)
*
* Handle the test_dummy_encryption mount option by creating a dummy encryption
* context, saving it in @dummy_ctx, and adding the corresponding dummy
* encryption key to the filesystem. If the @dummy_ctx is already set, then
* instead validate that it matches @arg. Don't support changing it via
* remount, as that is difficult to do safely.
*
* The reason we use an fscrypt_context rather than an fscrypt_policy is because
* we mustn't generate a new nonce each time we access a dummy-encrypted
* directory, as that would change the way filenames are encrypted.
*
* Return: 0 on success (dummy context set, or the same context is already set);
* -EEXIST if a different dummy context is already set;
* or another -errno value.
*/
int fscrypt_set_test_dummy_encryption(struct super_block *sb,
const substring_t *arg,
struct fscrypt_dummy_context *dummy_ctx)
{
const char *argstr = "v2";
const char *argstr_to_free = NULL;
struct fscrypt_key_specifier key_spec = { 0 };
int version;
union fscrypt_context *ctx = NULL;
int err;
if (arg->from) {
argstr = argstr_to_free = match_strdup(arg);
if (!argstr)
return -ENOMEM;
}
if (!strcmp(argstr, "v1")) {
version = FSCRYPT_CONTEXT_V1;
key_spec.type = FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR;
memset(key_spec.u.descriptor, 0x42,
FSCRYPT_KEY_DESCRIPTOR_SIZE);
} else if (!strcmp(argstr, "v2")) {
version = FSCRYPT_CONTEXT_V2;
key_spec.type = FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER;
/* key_spec.u.identifier gets filled in when adding the key */
} else {
err = -EINVAL;
goto out;
}
if (dummy_ctx->ctx) {
/*
* Note: if we ever make test_dummy_encryption support
* specifying other encryption settings, such as the encryption
* modes, we'll need to compare those settings here.
*/
if (dummy_ctx->ctx->version == version)
err = 0;
else
err = -EEXIST;
goto out;
}
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
if (!ctx) {
err = -ENOMEM;
goto out;
}
err = fscrypt_add_test_dummy_key(sb, &key_spec);
if (err)
goto out;
ctx->version = version;
switch (ctx->version) {
case FSCRYPT_CONTEXT_V1:
ctx->v1.contents_encryption_mode = FSCRYPT_MODE_AES_256_XTS;
ctx->v1.filenames_encryption_mode = FSCRYPT_MODE_AES_256_CTS;
memcpy(ctx->v1.master_key_descriptor, key_spec.u.descriptor,
FSCRYPT_KEY_DESCRIPTOR_SIZE);
break;
case FSCRYPT_CONTEXT_V2:
ctx->v2.contents_encryption_mode = FSCRYPT_MODE_AES_256_XTS;
ctx->v2.filenames_encryption_mode = FSCRYPT_MODE_AES_256_CTS;
memcpy(ctx->v2.master_key_identifier, key_spec.u.identifier,
FSCRYPT_KEY_IDENTIFIER_SIZE);
break;
default:
WARN_ON(1);
err = -EINVAL;
goto out;
}
dummy_ctx->ctx = ctx;
ctx = NULL;
err = 0;
out:
kfree(ctx);
kfree(argstr_to_free);
return err;
}
EXPORT_SYMBOL_GPL(fscrypt_set_test_dummy_encryption);
/**
* fscrypt_show_test_dummy_encryption() - show '-o test_dummy_encryption'
* @seq: the seq_file to print the option to
* @sep: the separator character to use
* @sb: the filesystem whose options are being shown
*
* Show the test_dummy_encryption mount option, if it was specified.
* This is mainly used for /proc/mounts.
*/
void fscrypt_show_test_dummy_encryption(struct seq_file *seq, char sep,
struct super_block *sb)
{
const union fscrypt_context *ctx = fscrypt_get_dummy_context(sb);
if (!ctx)
return;
seq_printf(seq, "%ctest_dummy_encryption=v%d", sep, ctx->version);
}
EXPORT_SYMBOL_GPL(fscrypt_show_test_dummy_encryption);

View File

@ -1358,11 +1358,9 @@ struct ext4_super_block {
*/
#define EXT4_MF_MNTDIR_SAMPLED 0x0001
#define EXT4_MF_FS_ABORTED 0x0002 /* Fatal error detected */
#define EXT4_MF_TEST_DUMMY_ENCRYPTION 0x0004
#ifdef CONFIG_FS_ENCRYPTION
#define DUMMY_ENCRYPTION_ENABLED(sbi) (unlikely((sbi)->s_mount_flags & \
EXT4_MF_TEST_DUMMY_ENCRYPTION))
#define DUMMY_ENCRYPTION_ENABLED(sbi) ((sbi)->s_dummy_enc_ctx.ctx != NULL)
#else
#define DUMMY_ENCRYPTION_ENABLED(sbi) (0)
#endif
@ -1540,6 +1538,9 @@ struct ext4_sb_info {
struct ratelimit_state s_warning_ratelimit_state;
struct ratelimit_state s_msg_ratelimit_state;
/* Encryption context for '-o test_dummy_encryption' */
struct fscrypt_dummy_context s_dummy_enc_ctx;
/*
* Barrier between writepages ops and changing any inode's JOURNAL_DATA
* or EXTENTS flag.

View File

@ -1106,6 +1106,7 @@ static void ext4_put_super(struct super_block *sb)
crypto_free_shash(sbi->s_chksum_driver);
kfree(sbi->s_blockgroup_lock);
fs_put_dax(sbi->s_daxdev);
fscrypt_free_dummy_context(&sbi->s_dummy_enc_ctx);
#ifdef CONFIG_UNICODE
utf8_unload(sb->s_encoding);
#endif
@ -1389,9 +1390,10 @@ static int ext4_set_context(struct inode *inode, const void *ctx, size_t len,
return res;
}
static bool ext4_dummy_context(struct inode *inode)
static const union fscrypt_context *
ext4_get_dummy_context(struct super_block *sb)
{
return DUMMY_ENCRYPTION_ENABLED(EXT4_SB(inode->i_sb));
return EXT4_SB(sb)->s_dummy_enc_ctx.ctx;
}
static bool ext4_has_stable_inodes(struct super_block *sb)
@ -1415,7 +1417,7 @@ static const struct fscrypt_operations ext4_cryptops = {
.key_prefix = "ext4:",
.get_context = ext4_get_context,
.set_context = ext4_set_context,
.dummy_context = ext4_dummy_context,
.get_dummy_context = ext4_get_dummy_context,
.empty_dir = ext4_empty_dir,
.max_namelen = EXT4_NAME_LEN,
.has_stable_inodes = ext4_has_stable_inodes,
@ -1612,6 +1614,7 @@ static const match_table_t tokens = {
{Opt_init_itable, "init_itable"},
{Opt_noinit_itable, "noinit_itable"},
{Opt_max_dir_size_kb, "max_dir_size_kb=%u"},
{Opt_test_dummy_encryption, "test_dummy_encryption=%s"},
{Opt_test_dummy_encryption, "test_dummy_encryption"},
{Opt_inlinecrypt, "inlinecrypt"},
{Opt_nombcache, "nombcache"},
@ -1824,7 +1827,7 @@ static const struct mount_opts {
{Opt_jqfmt_vfsv0, QFMT_VFS_V0, MOPT_QFMT},
{Opt_jqfmt_vfsv1, QFMT_VFS_V1, MOPT_QFMT},
{Opt_max_dir_size_kb, 0, MOPT_GTE0},
{Opt_test_dummy_encryption, 0, MOPT_GTE0},
{Opt_test_dummy_encryption, 0, MOPT_STRING},
#ifdef CONFIG_FS_ENCRYPTION_INLINE_CRYPT
{Opt_inlinecrypt, EXT4_MOUNT_INLINECRYPT, MOPT_SET},
#else
@ -1864,6 +1867,48 @@ static int ext4_sb_read_encoding(const struct ext4_super_block *es,
}
#endif
static int ext4_set_test_dummy_encryption(struct super_block *sb,
const char *opt,
const substring_t *arg,
bool is_remount)
{
#ifdef CONFIG_FS_ENCRYPTION
struct ext4_sb_info *sbi = EXT4_SB(sb);
int err;
/*
* This mount option is just for testing, and it's not worthwhile to
* implement the extra complexity (e.g. RCU protection) that would be
* needed to allow it to be set or changed during remount. We do allow
* it to be specified during remount, but only if there is no change.
*/
if (is_remount && !sbi->s_dummy_enc_ctx.ctx) {
ext4_msg(sb, KERN_WARNING,
"Can't set test_dummy_encryption on remount");
return -1;
}
err = fscrypt_set_test_dummy_encryption(sb, arg, &sbi->s_dummy_enc_ctx);
if (err) {
if (err == -EEXIST)
ext4_msg(sb, KERN_WARNING,
"Can't change test_dummy_encryption on remount");
else if (err == -EINVAL)
ext4_msg(sb, KERN_WARNING,
"Value of option \"%s\" is unrecognized", opt);
else
ext4_msg(sb, KERN_WARNING,
"Error processing option \"%s\" [%d]",
opt, err);
return -1;
}
ext4_msg(sb, KERN_WARNING, "Test dummy encryption mode enabled");
#else
ext4_msg(sb, KERN_WARNING,
"Test dummy encryption mount option ignored");
#endif
return 1;
}
static int handle_mount_opt(struct super_block *sb, char *opt, int token,
substring_t *args, unsigned long *journal_devnum,
unsigned int *journal_ioprio, int is_remount)
@ -2060,14 +2105,8 @@ static int handle_mount_opt(struct super_block *sb, char *opt, int token,
*journal_ioprio =
IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, arg);
} else if (token == Opt_test_dummy_encryption) {
#ifdef CONFIG_FS_ENCRYPTION
sbi->s_mount_flags |= EXT4_MF_TEST_DUMMY_ENCRYPTION;
ext4_msg(sb, KERN_WARNING,
"Test dummy encryption mode enabled");
#else
ext4_msg(sb, KERN_WARNING,
"Test dummy encryption mount option ignored");
#endif
return ext4_set_test_dummy_encryption(sb, opt, &args[0],
is_remount);
} else if (m->flags & MOPT_DATAJ) {
if (is_remount) {
if (!sbi->s_journal)
@ -2324,8 +2363,8 @@ static int _ext4_show_options(struct seq_file *seq, struct super_block *sb,
SEQ_OPTS_PRINT("max_dir_size_kb=%u", sbi->s_max_dir_size_kb);
if (test_opt(sb, DATA_ERR_ABORT))
SEQ_OPTS_PUTS("data_err=abort");
if (DUMMY_ENCRYPTION_ENABLED(sbi))
SEQ_OPTS_PUTS("test_dummy_encryption");
fscrypt_show_test_dummy_encryption(seq, sep, sb);
ext4_show_quota_options(seq, sb);
return 0;
@ -4788,6 +4827,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
for (i = 0; i < EXT4_MAXQUOTAS; i++)
kfree(get_qf_name(sb, sbi, i));
#endif
fscrypt_free_dummy_context(&sbi->s_dummy_enc_ctx);
ext4_blkdev_remove(sbi);
brelse(bh);
out_fail:

View File

@ -293,6 +293,7 @@ EXT4_ATTR_FEATURE(batched_discard);
EXT4_ATTR_FEATURE(meta_bg_resize);
#ifdef CONFIG_FS_ENCRYPTION
EXT4_ATTR_FEATURE(encryption);
EXT4_ATTR_FEATURE(test_dummy_encryption_v2);
#endif
#ifdef CONFIG_UNICODE
EXT4_ATTR_FEATURE(casefold);
@ -308,6 +309,7 @@ static struct attribute *ext4_feat_attrs[] = {
ATTR_LIST(meta_bg_resize),
#ifdef CONFIG_FS_ENCRYPTION
ATTR_LIST(encryption),
ATTR_LIST(test_dummy_encryption_v2),
#endif
#ifdef CONFIG_UNICODE
ATTR_LIST(casefold),

View File

@ -138,7 +138,7 @@ struct f2fs_mount_info {
int fsync_mode; /* fsync policy */
int fs_mode; /* fs mode: LFS or ADAPTIVE */
int bggc_mode; /* bggc mode: off, on or sync */
bool test_dummy_encryption; /* test dummy encryption */
struct fscrypt_dummy_context dummy_enc_ctx; /* test dummy encryption */
#ifdef CONFIG_FS_ENCRYPTION
bool inlinecrypt; /* inline encryption enabled */
#endif
@ -1262,7 +1262,7 @@ enum fsync_mode {
#ifdef CONFIG_FS_ENCRYPTION
#define DUMMY_ENCRYPTION_ENABLED(sbi) \
(unlikely(F2FS_OPTION(sbi).test_dummy_encryption))
(unlikely(F2FS_OPTION(sbi).dummy_enc_ctx.ctx != NULL))
#else
#define DUMMY_ENCRYPTION_ENABLED(sbi) (0)
#endif

View File

@ -203,6 +203,7 @@ static match_table_t f2fs_tokens = {
{Opt_whint, "whint_mode=%s"},
{Opt_alloc, "alloc_mode=%s"},
{Opt_fsync, "fsync_mode=%s"},
{Opt_test_dummy_encryption, "test_dummy_encryption=%s"},
{Opt_test_dummy_encryption, "test_dummy_encryption"},
{Opt_inlinecrypt, "inlinecrypt"},
{Opt_checkpoint_disable, "checkpoint=disable"},
@ -396,7 +397,52 @@ static int f2fs_check_quota_options(struct f2fs_sb_info *sbi)
}
#endif
static int parse_options(struct super_block *sb, char *options)
static int f2fs_set_test_dummy_encryption(struct super_block *sb,
const char *opt,
const substring_t *arg,
bool is_remount)
{
struct f2fs_sb_info *sbi = F2FS_SB(sb);
#ifdef CONFIG_FS_ENCRYPTION
int err;
if (!f2fs_sb_has_encrypt(sbi)) {
f2fs_err(sbi, "Encrypt feature is off");
return -EINVAL;
}
/*
* This mount option is just for testing, and it's not worthwhile to
* implement the extra complexity (e.g. RCU protection) that would be
* needed to allow it to be set or changed during remount. We do allow
* it to be specified during remount, but only if there is no change.
*/
if (is_remount && !F2FS_OPTION(sbi).dummy_enc_ctx.ctx) {
f2fs_warn(sbi, "Can't set test_dummy_encryption on remount");
return -EINVAL;
}
err = fscrypt_set_test_dummy_encryption(
sb, arg, &F2FS_OPTION(sbi).dummy_enc_ctx);
if (err) {
if (err == -EEXIST)
f2fs_warn(sbi,
"Can't change test_dummy_encryption on remount");
else if (err == -EINVAL)
f2fs_warn(sbi, "Value of option \"%s\" is unrecognized",
opt);
else
f2fs_warn(sbi, "Error processing option \"%s\" [%d]",
opt, err);
return -EINVAL;
}
f2fs_warn(sbi, "Test dummy encryption mode enabled");
#else
f2fs_warn(sbi, "Test dummy encryption mount option ignored");
#endif
return 0;
}
static int parse_options(struct super_block *sb, char *options, bool is_remount)
{
struct f2fs_sb_info *sbi = F2FS_SB(sb);
substring_t args[MAX_OPT_ARGS];
@ -405,9 +451,7 @@ static int parse_options(struct super_block *sb, char *options)
int arg = 0, ext_cnt;
kuid_t uid;
kgid_t gid;
#ifdef CONFIG_QUOTA
int ret;
#endif
if (!options)
return 0;
@ -780,17 +824,10 @@ static int parse_options(struct super_block *sb, char *options)
kvfree(name);
break;
case Opt_test_dummy_encryption:
#ifdef CONFIG_FS_ENCRYPTION
if (!f2fs_sb_has_encrypt(sbi)) {
f2fs_err(sbi, "Encrypt feature is off");
return -EINVAL;
}
F2FS_OPTION(sbi).test_dummy_encryption = true;
f2fs_info(sbi, "Test dummy encryption mode enabled");
#else
f2fs_info(sbi, "Test dummy encryption mount option ignored");
#endif
ret = f2fs_set_test_dummy_encryption(sb, p, &args[0],
is_remount);
if (ret)
return ret;
break;
case Opt_inlinecrypt:
#ifdef CONFIG_FS_ENCRYPTION_INLINE_CRYPT
@ -1222,6 +1259,7 @@ static void f2fs_put_super(struct super_block *sb)
for (i = 0; i < MAXQUOTAS; i++)
kvfree(F2FS_OPTION(sbi).s_qf_names[i]);
#endif
fscrypt_free_dummy_context(&F2FS_OPTION(sbi).dummy_enc_ctx);
destroy_percpu_info(sbi);
for (i = 0; i < NR_PAGE_TYPE; i++)
kvfree(sbi->write_io[i]);
@ -1552,9 +1590,10 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
seq_printf(seq, ",whint_mode=%s", "user-based");
else if (F2FS_OPTION(sbi).whint_mode == WHINT_MODE_FS)
seq_printf(seq, ",whint_mode=%s", "fs-based");
fscrypt_show_test_dummy_encryption(seq, ',', sbi->sb);
#ifdef CONFIG_FS_ENCRYPTION
if (F2FS_OPTION(sbi).test_dummy_encryption)
seq_puts(seq, ",test_dummy_encryption");
if (F2FS_OPTION(sbi).inlinecrypt)
seq_puts(seq, ",inlinecrypt");
#endif
@ -1586,7 +1625,6 @@ static void default_options(struct f2fs_sb_info *sbi)
F2FS_OPTION(sbi).whint_mode = WHINT_MODE_OFF;
F2FS_OPTION(sbi).alloc_mode = ALLOC_MODE_DEFAULT;
F2FS_OPTION(sbi).fsync_mode = FSYNC_MODE_POSIX;
F2FS_OPTION(sbi).test_dummy_encryption = false;
#ifdef CONFIG_FS_ENCRYPTION
F2FS_OPTION(sbi).inlinecrypt = false;
#endif
@ -1748,7 +1786,7 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
default_options(sbi);
/* parse mount options */
err = parse_options(sb, data);
err = parse_options(sb, data, true);
if (err)
goto restore_opts;
checkpoint_changed =
@ -2424,9 +2462,10 @@ static int f2fs_set_context(struct inode *inode, const void *ctx, size_t len,
ctx, len, fs_data, XATTR_CREATE);
}
static bool f2fs_dummy_context(struct inode *inode)
static const union fscrypt_context *
f2fs_get_dummy_context(struct super_block *sb)
{
return DUMMY_ENCRYPTION_ENABLED(F2FS_I_SB(inode));
return F2FS_OPTION(F2FS_SB(sb)).dummy_enc_ctx.ctx;
}
static bool f2fs_has_stable_inodes(struct super_block *sb)
@ -2469,7 +2508,7 @@ static const struct fscrypt_operations f2fs_cryptops = {
.key_prefix = "f2fs:",
.get_context = f2fs_get_context,
.set_context = f2fs_set_context,
.dummy_context = f2fs_dummy_context,
.get_dummy_context = f2fs_get_dummy_context,
.empty_dir = f2fs_empty_dir,
.max_namelen = F2FS_NAME_LEN,
.has_stable_inodes = f2fs_has_stable_inodes,
@ -3400,7 +3439,7 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
goto free_sb_buf;
}
err = parse_options(sb, options);
err = parse_options(sb, options, false);
if (err)
goto free_options;
@ -3803,6 +3842,7 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
for (i = 0; i < MAXQUOTAS; i++)
kvfree(F2FS_OPTION(sbi).s_qf_names[i]);
#endif
fscrypt_free_dummy_context(&F2FS_OPTION(sbi).dummy_enc_ctx);
kvfree(options);
free_sb_buf:
kvfree(raw_super);

View File

@ -448,6 +448,7 @@ enum feat_id {
FEAT_SB_CHECKSUM,
FEAT_CASEFOLD,
FEAT_COMPRESSION,
FEAT_TEST_DUMMY_ENCRYPTION_V2,
};
static ssize_t f2fs_feature_show(struct f2fs_attr *a,
@ -468,6 +469,7 @@ static ssize_t f2fs_feature_show(struct f2fs_attr *a,
case FEAT_SB_CHECKSUM:
case FEAT_CASEFOLD:
case FEAT_COMPRESSION:
case FEAT_TEST_DUMMY_ENCRYPTION_V2:
return sprintf(buf, "supported\n");
}
return 0;
@ -565,6 +567,7 @@ F2FS_GENERAL_RO_ATTR(avg_vblocks);
#ifdef CONFIG_FS_ENCRYPTION
F2FS_FEATURE_RO_ATTR(encryption, FEAT_CRYPTO);
F2FS_FEATURE_RO_ATTR(test_dummy_encryption_v2, FEAT_TEST_DUMMY_ENCRYPTION_V2);
#endif
#ifdef CONFIG_BLK_DEV_ZONED
F2FS_FEATURE_RO_ATTR(block_zoned, FEAT_BLKZONED);
@ -649,6 +652,7 @@ ATTRIBUTE_GROUPS(f2fs);
static struct attribute *f2fs_feat_attrs[] = {
#ifdef CONFIG_FS_ENCRYPTION
ATTR_LIST(encryption),
ATTR_LIST(test_dummy_encryption_v2),
#endif
#ifdef CONFIG_BLK_DEV_ZONED
ATTR_LIST(block_zoned),

View File

@ -153,3 +153,112 @@ config PSTORE_RAM
"ramoops.ko".
For more information, see Documentation/admin-guide/ramoops.rst.
config PSTORE_ZONE
tristate
depends on PSTORE
help
The common layer for pstore/blk (and pstore/ram in the future)
to manage storage in zones.
config PSTORE_BLK
tristate "Log panic/oops to a block device"
depends on PSTORE
depends on BLOCK
select PSTORE_ZONE
default n
help
This enables panic and oops message to be logged to a block dev
where it can be read back at some later point.
For more information, see Documentation/admin-guide/pstore-blk.rst
If unsure, say N.
config PSTORE_BLK_BLKDEV
string "block device identifier"
depends on PSTORE_BLK
default ""
help
Which block device should be used for pstore/blk.
It accepts the following variants:
1) <hex_major><hex_minor> device number in hexadecimal representation,
with no leading 0x, for example b302.
2) /dev/<disk_name> represents the device name of disk
3) /dev/<disk_name><decimal> represents the device name and number
of partition - device number of disk plus the partition number
4) /dev/<disk_name>p<decimal> - same as the above, this form is
used when disk name of partitioned disk ends with a digit.
5) PARTUUID=00112233-4455-6677-8899-AABBCCDDEEFF representing the
unique id of a partition if the partition table provides it.
The UUID may be either an EFI/GPT UUID, or refer to an MSDOS
partition using the format SSSSSSSS-PP, where SSSSSSSS is a zero-
filled hex representation of the 32-bit "NT disk signature", and PP
is a zero-filled hex representation of the 1-based partition number.
6) PARTUUID=<UUID>/PARTNROFF=<int> to select a partition in relation
to a partition with a known unique id.
7) <major>:<minor> major and minor number of the device separated by
a colon.
NOTE that, both Kconfig and module parameters can configure
pstore/blk, but module parameters have priority over Kconfig.
config PSTORE_BLK_KMSG_SIZE
int "Size in Kbytes of kmsg dump log to store"
depends on PSTORE_BLK
default 64
help
This just sets size of kmsg dump (oops, panic, etc) log for
pstore/blk. The size is in KB and must be a multiple of 4.
NOTE that, both Kconfig and module parameters can configure
pstore/blk, but module parameters have priority over Kconfig.
config PSTORE_BLK_MAX_REASON
int "Maximum kmsg dump reason to store"
depends on PSTORE_BLK
default 2
help
The maximum reason for kmsg dumps to store. The default is
2 (KMSG_DUMP_OOPS), see include/linux/kmsg_dump.h's
enum kmsg_dump_reason for more details.
NOTE that, both Kconfig and module parameters can configure
pstore/blk, but module parameters have priority over Kconfig.
config PSTORE_BLK_PMSG_SIZE
int "Size in Kbytes of pmsg to store"
depends on PSTORE_BLK
depends on PSTORE_PMSG
default 64
help
This just sets size of pmsg (pmsg_size) for pstore/blk. The size is
in KB and must be a multiple of 4.
NOTE that, both Kconfig and module parameters can configure
pstore/blk, but module parameters have priority over Kconfig.
config PSTORE_BLK_CONSOLE_SIZE
int "Size in Kbytes of console log to store"
depends on PSTORE_BLK
depends on PSTORE_CONSOLE
default 64
help
This just sets size of console log (console_size) to store via
pstore/blk. The size is in KB and must be a multiple of 4.
NOTE that, both Kconfig and module parameters can configure
pstore/blk, but module parameters have priority over Kconfig.
config PSTORE_BLK_FTRACE_SIZE
int "Size in Kbytes of ftrace log to store"
depends on PSTORE_BLK
depends on PSTORE_FTRACE
default 64
help
This just sets size of ftrace log (ftrace_size) for pstore/blk. The
size is in KB and must be a multiple of 4.
NOTE that, both Kconfig and module parameters can configure
pstore/blk, but module parameters have priority over Kconfig.

View File

@ -12,3 +12,9 @@ pstore-$(CONFIG_PSTORE_PMSG) += pmsg.o
ramoops-objs += ram.o ram_core.o
obj-$(CONFIG_PSTORE_RAM) += ramoops.o
pstore_zone-objs += zone.o
obj-$(CONFIG_PSTORE_ZONE) += pstore_zone.o
pstore_blk-objs += blk.o
obj-$(CONFIG_PSTORE_BLK) += pstore_blk.o

517
fs/pstore/blk.c Normal file
View File

@ -0,0 +1,517 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Implements pstore backend driver that write to block (or non-block) storage
* devices, using the pstore/zone API.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h>
#include <linux/module.h>
#include "../../block/blk.h"
#include <linux/blkdev.h>
#include <linux/string.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/pstore_blk.h>
#include <linux/mount.h>
#include <linux/uio.h>
static long kmsg_size = CONFIG_PSTORE_BLK_KMSG_SIZE;
module_param(kmsg_size, long, 0400);
MODULE_PARM_DESC(kmsg_size, "kmsg dump record size in kbytes");
static int max_reason = CONFIG_PSTORE_BLK_MAX_REASON;
module_param(max_reason, int, 0400);
MODULE_PARM_DESC(max_reason,
"maximum reason for kmsg dump (default 2: Oops and Panic)");
#if IS_ENABLED(CONFIG_PSTORE_PMSG)
static long pmsg_size = CONFIG_PSTORE_BLK_PMSG_SIZE;
#else
static long pmsg_size = -1;
#endif
module_param(pmsg_size, long, 0400);
MODULE_PARM_DESC(pmsg_size, "pmsg size in kbytes");
#if IS_ENABLED(CONFIG_PSTORE_CONSOLE)
static long console_size = CONFIG_PSTORE_BLK_CONSOLE_SIZE;
#else
static long console_size = -1;
#endif
module_param(console_size, long, 0400);
MODULE_PARM_DESC(console_size, "console size in kbytes");
#if IS_ENABLED(CONFIG_PSTORE_FTRACE)
static long ftrace_size = CONFIG_PSTORE_BLK_FTRACE_SIZE;
#else
static long ftrace_size = -1;
#endif
module_param(ftrace_size, long, 0400);
MODULE_PARM_DESC(ftrace_size, "ftrace size in kbytes");
static bool best_effort;
module_param(best_effort, bool, 0400);
MODULE_PARM_DESC(best_effort, "use best effort to write (i.e. do not require storage driver pstore support, default: off)");
/*
* blkdev - the block device to use for pstore storage
*
* Usually, this will be a partition of a block device.
*
* blkdev accepts the following variants:
* 1) <hex_major><hex_minor> device number in hexadecimal representation,
* with no leading 0x, for example b302.
* 2) /dev/<disk_name> represents the device number of disk
* 3) /dev/<disk_name><decimal> represents the device number
* of partition - device number of disk plus the partition number
* 4) /dev/<disk_name>p<decimal> - same as the above, that form is
* used when disk name of partitioned disk ends on a digit.
* 5) PARTUUID=00112233-4455-6677-8899-AABBCCDDEEFF representing the
* unique id of a partition if the partition table provides it.
* The UUID may be either an EFI/GPT UUID, or refer to an MSDOS
* partition using the format SSSSSSSS-PP, where SSSSSSSS is a zero-
* filled hex representation of the 32-bit "NT disk signature", and PP
* is a zero-filled hex representation of the 1-based partition number.
* 6) PARTUUID=<UUID>/PARTNROFF=<int> to select a partition in relation to
* a partition with a known unique id.
* 7) <major>:<minor> major and minor number of the device separated by
* a colon.
*/
static char blkdev[80] = CONFIG_PSTORE_BLK_BLKDEV;
module_param_string(blkdev, blkdev, 80, 0400);
MODULE_PARM_DESC(blkdev, "block device for pstore storage");
/*
* All globals must only be accessed under the pstore_blk_lock
* during the register/unregister functions.
*/
static DEFINE_MUTEX(pstore_blk_lock);
static struct block_device *psblk_bdev;
static struct pstore_zone_info *pstore_zone_info;
static pstore_blk_panic_write_op blkdev_panic_write;
struct bdev_info {
dev_t devt;
sector_t nr_sects;
sector_t start_sect;
};
#define check_size(name, alignsize) ({ \
long _##name_ = (name); \
_##name_ = _##name_ <= 0 ? 0 : (_##name_ * 1024); \
if (_##name_ & ((alignsize) - 1)) { \
pr_info(#name " must align to %d\n", \
(alignsize)); \
_##name_ = ALIGN(name, (alignsize)); \
} \
_##name_; \
})
static int __register_pstore_device(struct pstore_device_info *dev)
{
int ret;
lockdep_assert_held(&pstore_blk_lock);
if (!dev || !dev->total_size || !dev->read || !dev->write)
return -EINVAL;
/* someone already registered before */
if (pstore_zone_info)
return -EBUSY;
pstore_zone_info = kzalloc(sizeof(struct pstore_zone_info), GFP_KERNEL);
if (!pstore_zone_info)
return -ENOMEM;
/* zero means not limit on which backends to attempt to store. */
if (!dev->flags)
dev->flags = UINT_MAX;
#define verify_size(name, alignsize, enabled) { \
long _##name_; \
if (enabled) \
_##name_ = check_size(name, alignsize); \
else \
_##name_ = 0; \
name = _##name_ / 1024; \
pstore_zone_info->name = _##name_; \
}
verify_size(kmsg_size, 4096, dev->flags & PSTORE_FLAGS_DMESG);
verify_size(pmsg_size, 4096, dev->flags & PSTORE_FLAGS_PMSG);
verify_size(console_size, 4096, dev->flags & PSTORE_FLAGS_CONSOLE);
verify_size(ftrace_size, 4096, dev->flags & PSTORE_FLAGS_FTRACE);
#undef verify_size
pstore_zone_info->total_size = dev->total_size;
pstore_zone_info->max_reason = max_reason;
pstore_zone_info->read = dev->read;
pstore_zone_info->write = dev->write;
pstore_zone_info->erase = dev->erase;
pstore_zone_info->panic_write = dev->panic_write;
pstore_zone_info->name = KBUILD_MODNAME;
pstore_zone_info->owner = THIS_MODULE;
ret = register_pstore_zone(pstore_zone_info);
if (ret) {
kfree(pstore_zone_info);
pstore_zone_info = NULL;
}
return ret;
}
/**
* register_pstore_device() - register non-block device to pstore/blk
*
* @dev: non-block device information
*
* Return:
* * 0 - OK
* * Others - something error.
*/
int register_pstore_device(struct pstore_device_info *dev)
{
int ret;
mutex_lock(&pstore_blk_lock);
ret = __register_pstore_device(dev);
mutex_unlock(&pstore_blk_lock);
return ret;
}
EXPORT_SYMBOL_GPL(register_pstore_device);
static void __unregister_pstore_device(struct pstore_device_info *dev)
{
lockdep_assert_held(&pstore_blk_lock);
if (pstore_zone_info && pstore_zone_info->read == dev->read) {
unregister_pstore_zone(pstore_zone_info);
kfree(pstore_zone_info);
pstore_zone_info = NULL;
}
}
/**
* unregister_pstore_device() - unregister non-block device from pstore/blk
*
* @dev: non-block device information
*/
void unregister_pstore_device(struct pstore_device_info *dev)
{
mutex_lock(&pstore_blk_lock);
__unregister_pstore_device(dev);
mutex_unlock(&pstore_blk_lock);
}
EXPORT_SYMBOL_GPL(unregister_pstore_device);
/**
* psblk_get_bdev() - open block device
*
* @holder: Exclusive holder identifier
* @info: Information about bdev to fill in
*
* Return: pointer to block device on success and others on error.
*
* On success, the returned block_device has reference count of one.
*/
static struct block_device *psblk_get_bdev(void *holder,
struct bdev_info *info)
{
struct block_device *bdev = ERR_PTR(-ENODEV);
fmode_t mode = FMODE_READ | FMODE_WRITE;
sector_t nr_sects;
lockdep_assert_held(&pstore_blk_lock);
if (pstore_zone_info)
return ERR_PTR(-EBUSY);
if (!blkdev[0])
return ERR_PTR(-ENODEV);
if (holder)
mode |= FMODE_EXCL;
bdev = blkdev_get_by_path(blkdev, mode, holder);
if (IS_ERR(bdev)) {
dev_t devt;
devt = name_to_dev_t(blkdev);
if (devt == 0)
return ERR_PTR(-ENODEV);
bdev = blkdev_get_by_dev(devt, mode, holder);
if (IS_ERR(bdev))
return bdev;
}
nr_sects = part_nr_sects_read(bdev->bd_part);
if (!nr_sects) {
pr_err("not enough space for '%s'\n", blkdev);
blkdev_put(bdev, mode);
return ERR_PTR(-ENOSPC);
}
if (info) {
info->devt = bdev->bd_dev;
info->nr_sects = nr_sects;
info->start_sect = get_start_sect(bdev);
}
return bdev;
}
static void psblk_put_bdev(struct block_device *bdev, void *holder)
{
fmode_t mode = FMODE_READ | FMODE_WRITE;
lockdep_assert_held(&pstore_blk_lock);
if (!bdev)
return;
if (holder)
mode |= FMODE_EXCL;
blkdev_put(bdev, mode);
}
static ssize_t psblk_generic_blk_read(char *buf, size_t bytes, loff_t pos)
{
struct block_device *bdev = psblk_bdev;
struct file file;
struct kiocb kiocb;
struct iov_iter iter;
struct kvec iov = {.iov_base = buf, .iov_len = bytes};
if (!bdev)
return -ENODEV;
memset(&file, 0, sizeof(struct file));
file.f_mapping = bdev->bd_inode->i_mapping;
file.f_flags = O_DSYNC | __O_SYNC | O_NOATIME;
file.f_inode = bdev->bd_inode;
file_ra_state_init(&file.f_ra, file.f_mapping);
init_sync_kiocb(&kiocb, &file);
kiocb.ki_pos = pos;
iov_iter_kvec(&iter, READ, &iov, 1, bytes);
return generic_file_read_iter(&kiocb, &iter);
}
static ssize_t psblk_generic_blk_write(const char *buf, size_t bytes,
loff_t pos)
{
struct block_device *bdev = psblk_bdev;
struct iov_iter iter;
struct kiocb kiocb;
struct file file;
ssize_t ret;
struct kvec iov = {.iov_base = (void *)buf, .iov_len = bytes};
if (!bdev)
return -ENODEV;
/* Console/Ftrace backend may handle buffer until flush dirty zones */
if (in_interrupt() || irqs_disabled())
return -EBUSY;
memset(&file, 0, sizeof(struct file));
file.f_mapping = bdev->bd_inode->i_mapping;
file.f_flags = O_DSYNC | __O_SYNC | O_NOATIME;
file.f_inode = bdev->bd_inode;
init_sync_kiocb(&kiocb, &file);
kiocb.ki_pos = pos;
iov_iter_kvec(&iter, WRITE, &iov, 1, bytes);
inode_lock(bdev->bd_inode);
ret = generic_write_checks(&kiocb, &iter);
if (ret > 0)
ret = generic_perform_write(&file, &iter, pos);
inode_unlock(bdev->bd_inode);
if (likely(ret > 0)) {
const struct file_operations f_op = {.fsync = blkdev_fsync};
file.f_op = &f_op;
kiocb.ki_pos += ret;
ret = generic_write_sync(&kiocb, ret);
}
return ret;
}
static ssize_t psblk_blk_panic_write(const char *buf, size_t size,
loff_t off)
{
int ret;
if (!blkdev_panic_write)
return -EOPNOTSUPP;
/* size and off must align to SECTOR_SIZE for block device */
ret = blkdev_panic_write(buf, off >> SECTOR_SHIFT,
size >> SECTOR_SHIFT);
/* try next zone */
if (ret == -ENOMSG)
return ret;
return ret ? -EIO : size;
}
static int __register_pstore_blk(struct pstore_blk_info *info)
{
char bdev_name[BDEVNAME_SIZE];
struct block_device *bdev;
struct pstore_device_info dev;
struct bdev_info binfo;
void *holder = blkdev;
int ret = -ENODEV;
lockdep_assert_held(&pstore_blk_lock);
/* hold bdev exclusively */
memset(&binfo, 0, sizeof(binfo));
bdev = psblk_get_bdev(holder, &binfo);
if (IS_ERR(bdev)) {
pr_err("failed to open '%s'!\n", blkdev);
return PTR_ERR(bdev);
}
/* only allow driver matching the @blkdev */
if (!binfo.devt || (!best_effort &&
MAJOR(binfo.devt) != info->major)) {
pr_debug("invalid major %u (expect %u)\n",
info->major, MAJOR(binfo.devt));
ret = -ENODEV;
goto err_put_bdev;
}
/* psblk_bdev must be assigned before register to pstore/blk */
psblk_bdev = bdev;
blkdev_panic_write = info->panic_write;
/* Copy back block device details. */
info->devt = binfo.devt;
info->nr_sects = binfo.nr_sects;
info->start_sect = binfo.start_sect;
memset(&dev, 0, sizeof(dev));
dev.total_size = info->nr_sects << SECTOR_SHIFT;
dev.flags = info->flags;
dev.read = psblk_generic_blk_read;
dev.write = psblk_generic_blk_write;
dev.erase = NULL;
dev.panic_write = info->panic_write ? psblk_blk_panic_write : NULL;
ret = __register_pstore_device(&dev);
if (ret)
goto err_put_bdev;
bdevname(bdev, bdev_name);
pr_info("attached %s%s\n", bdev_name,
info->panic_write ? "" : " (no dedicated panic_write!)");
return 0;
err_put_bdev:
psblk_bdev = NULL;
blkdev_panic_write = NULL;
psblk_put_bdev(bdev, holder);
return ret;
}
/**
* register_pstore_blk() - register block device to pstore/blk
*
* @info: details on the desired block device interface
*
* Return:
* * 0 - OK
* * Others - something error.
*/
int register_pstore_blk(struct pstore_blk_info *info)
{
int ret;
mutex_lock(&pstore_blk_lock);
ret = __register_pstore_blk(info);
mutex_unlock(&pstore_blk_lock);
return ret;
}
EXPORT_SYMBOL_GPL(register_pstore_blk);
static void __unregister_pstore_blk(unsigned int major)
{
struct pstore_device_info dev = { .read = psblk_generic_blk_read };
void *holder = blkdev;
lockdep_assert_held(&pstore_blk_lock);
if (psblk_bdev && MAJOR(psblk_bdev->bd_dev) == major) {
__unregister_pstore_device(&dev);
psblk_put_bdev(psblk_bdev, holder);
blkdev_panic_write = NULL;
psblk_bdev = NULL;
}
}
/**
* unregister_pstore_blk() - unregister block device from pstore/blk
*
* @major: the major device number of device
*/
void unregister_pstore_blk(unsigned int major)
{
mutex_lock(&pstore_blk_lock);
__unregister_pstore_blk(major);
mutex_unlock(&pstore_blk_lock);
}
EXPORT_SYMBOL_GPL(unregister_pstore_blk);
/* get information of pstore/blk */
int pstore_blk_get_config(struct pstore_blk_config *info)
{
strncpy(info->device, blkdev, 80);
info->max_reason = max_reason;
info->kmsg_size = check_size(kmsg_size, 4096);
info->pmsg_size = check_size(pmsg_size, 4096);
info->ftrace_size = check_size(ftrace_size, 4096);
info->console_size = check_size(console_size, 4096);
return 0;
}
EXPORT_SYMBOL_GPL(pstore_blk_get_config);
static int __init pstore_blk_init(void)
{
struct pstore_blk_info info = { };
int ret = 0;
mutex_lock(&pstore_blk_lock);
if (!pstore_zone_info && best_effort && blkdev[0])
ret = __register_pstore_blk(&info);
mutex_unlock(&pstore_blk_lock);
return ret;
}
late_initcall(pstore_blk_init);
static void __exit pstore_blk_exit(void)
{
mutex_lock(&pstore_blk_lock);
if (psblk_bdev)
__unregister_pstore_blk(MAJOR(psblk_bdev->bd_dev));
else {
struct pstore_device_info dev = { };
if (pstore_zone_info)
dev.read = pstore_zone_info->read;
__unregister_pstore_device(&dev);
}
mutex_unlock(&pstore_blk_lock);
}
module_exit(pstore_blk_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("WeiXiong Liao <liaoweixiong@allwinnertech.com>");
MODULE_AUTHOR("Kees Cook <keescook@chromium.org>");
MODULE_DESCRIPTION("pstore backend for block devices");

View File

@ -16,6 +16,7 @@
#include <linux/debugfs.h>
#include <linux/err.h>
#include <linux/cache.h>
#include <linux/slab.h>
#include <asm/barrier.h>
#include "internal.h"
@ -132,3 +133,56 @@ void pstore_unregister_ftrace(void)
debugfs_remove_recursive(pstore_ftrace_dir);
}
ssize_t pstore_ftrace_combine_log(char **dest_log, size_t *dest_log_size,
const char *src_log, size_t src_log_size)
{
size_t dest_size, src_size, total, dest_off, src_off;
size_t dest_idx = 0, src_idx = 0, merged_idx = 0;
void *merged_buf;
struct pstore_ftrace_record *drec, *srec, *mrec;
size_t record_size = sizeof(struct pstore_ftrace_record);
dest_off = *dest_log_size % record_size;
dest_size = *dest_log_size - dest_off;
src_off = src_log_size % record_size;
src_size = src_log_size - src_off;
total = dest_size + src_size;
merged_buf = kmalloc(total, GFP_KERNEL);
if (!merged_buf)
return -ENOMEM;
drec = (struct pstore_ftrace_record *)(*dest_log + dest_off);
srec = (struct pstore_ftrace_record *)(src_log + src_off);
mrec = (struct pstore_ftrace_record *)(merged_buf);
while (dest_size > 0 && src_size > 0) {
if (pstore_ftrace_read_timestamp(&drec[dest_idx]) <
pstore_ftrace_read_timestamp(&srec[src_idx])) {
mrec[merged_idx++] = drec[dest_idx++];
dest_size -= record_size;
} else {
mrec[merged_idx++] = srec[src_idx++];
src_size -= record_size;
}
}
while (dest_size > 0) {
mrec[merged_idx++] = drec[dest_idx++];
dest_size -= record_size;
}
while (src_size > 0) {
mrec[merged_idx++] = srec[src_idx++];
src_size -= record_size;
}
kfree(*dest_log);
*dest_log = merged_buf;
*dest_log_size = total;
return 0;
}
EXPORT_SYMBOL_GPL(pstore_ftrace_combine_log);

View File

@ -22,18 +22,21 @@
#include <linux/magic.h>
#include <linux/pstore.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/uaccess.h>
#include "internal.h"
#define PSTORE_NAMELEN 64
static DEFINE_SPINLOCK(allpstore_lock);
static LIST_HEAD(allpstore);
static DEFINE_MUTEX(records_list_lock);
static LIST_HEAD(records_list);
static DEFINE_MUTEX(pstore_sb_lock);
static struct super_block *pstore_sb;
struct pstore_private {
struct list_head list;
struct dentry *dentry;
struct pstore_record *record;
size_t total_size;
};
@ -178,10 +181,22 @@ static int pstore_unlink(struct inode *dir, struct dentry *dentry)
{
struct pstore_private *p = d_inode(dentry)->i_private;
struct pstore_record *record = p->record;
int rc = 0;
if (!record->psi->erase)
return -EPERM;
/* Make sure we can't race while removing this file. */
mutex_lock(&records_list_lock);
if (!list_empty(&p->list))
list_del_init(&p->list);
else
rc = -ENOENT;
p->dentry = NULL;
mutex_unlock(&records_list_lock);
if (rc)
return rc;
mutex_lock(&record->psi->read_mutex);
record->psi->erase(record);
mutex_unlock(&record->psi->read_mutex);
@ -192,15 +207,9 @@ static int pstore_unlink(struct inode *dir, struct dentry *dentry)
static void pstore_evict_inode(struct inode *inode)
{
struct pstore_private *p = inode->i_private;
unsigned long flags;
clear_inode(inode);
if (p) {
spin_lock_irqsave(&allpstore_lock, flags);
list_del(&p->list);
spin_unlock_irqrestore(&allpstore_lock, flags);
free_pstore_private(p);
}
free_pstore_private(p);
}
static const struct inode_operations pstore_dir_inode_operations = {
@ -278,11 +287,54 @@ static const struct super_operations pstore_ops = {
.show_options = pstore_show_options,
};
static struct super_block *pstore_sb;
bool pstore_is_mounted(void)
static struct dentry *psinfo_lock_root(void)
{
return pstore_sb != NULL;
struct dentry *root;
mutex_lock(&pstore_sb_lock);
/*
* Having no backend is fine -- no records appear.
* Not being mounted is fine -- nothing to do.
*/
if (!psinfo || !pstore_sb) {
mutex_unlock(&pstore_sb_lock);
return NULL;
}
root = pstore_sb->s_root;
inode_lock(d_inode(root));
mutex_unlock(&pstore_sb_lock);
return root;
}
int pstore_put_backend_records(struct pstore_info *psi)
{
struct pstore_private *pos, *tmp;
struct dentry *root;
int rc = 0;
root = psinfo_lock_root();
if (!root)
return 0;
mutex_lock(&records_list_lock);
list_for_each_entry_safe(pos, tmp, &records_list, list) {
if (pos->record->psi == psi) {
list_del_init(&pos->list);
rc = simple_unlink(d_inode(root), pos->dentry);
if (WARN_ON(rc))
break;
d_drop(pos->dentry);
dput(pos->dentry);
pos->dentry = NULL;
}
}
mutex_unlock(&records_list_lock);
inode_unlock(d_inode(root));
return rc;
}
/*
@ -297,23 +349,20 @@ int pstore_mkfile(struct dentry *root, struct pstore_record *record)
int rc = 0;
char name[PSTORE_NAMELEN];
struct pstore_private *private, *pos;
unsigned long flags;
size_t size = record->size + record->ecc_notice_size;
WARN_ON(!inode_is_locked(d_inode(root)));
if (WARN_ON(!inode_is_locked(d_inode(root))))
return -EINVAL;
spin_lock_irqsave(&allpstore_lock, flags);
list_for_each_entry(pos, &allpstore, list) {
rc = -EEXIST;
/* Skip records that are already present in the filesystem. */
mutex_lock(&records_list_lock);
list_for_each_entry(pos, &records_list, list) {
if (pos->record->type == record->type &&
pos->record->id == record->id &&
pos->record->psi == record->psi) {
rc = -EEXIST;
break;
}
pos->record->psi == record->psi)
goto fail;
}
spin_unlock_irqrestore(&allpstore_lock, flags);
if (rc)
return rc;
rc = -ENOMEM;
inode = pstore_get_inode(root->d_sb);
@ -334,6 +383,7 @@ int pstore_mkfile(struct dentry *root, struct pstore_record *record)
if (!dentry)
goto fail_private;
private->dentry = dentry;
private->record = record;
inode->i_size = private->total_size = size;
inode->i_private = private;
@ -343,9 +393,8 @@ int pstore_mkfile(struct dentry *root, struct pstore_record *record)
d_add(dentry, inode);
spin_lock_irqsave(&allpstore_lock, flags);
list_add(&private->list, &allpstore);
spin_unlock_irqrestore(&allpstore_lock, flags);
list_add(&private->list, &records_list);
mutex_unlock(&records_list_lock);
return 0;
@ -353,8 +402,8 @@ int pstore_mkfile(struct dentry *root, struct pstore_record *record)
free_pstore_private(private);
fail_inode:
iput(inode);
fail:
mutex_unlock(&records_list_lock);
return rc;
}
@ -366,16 +415,13 @@ int pstore_mkfile(struct dentry *root, struct pstore_record *record)
*/
void pstore_get_records(int quiet)
{
struct pstore_info *psi = psinfo;
struct dentry *root;
if (!psi || !pstore_sb)
root = psinfo_lock_root();
if (!root)
return;
root = pstore_sb->s_root;
inode_lock(d_inode(root));
pstore_get_backend_records(psi, root, quiet);
pstore_get_backend_records(psinfo, root, quiet);
inode_unlock(d_inode(root));
}
@ -383,8 +429,6 @@ static int pstore_fill_super(struct super_block *sb, void *data, int silent)
{
struct inode *inode;
pstore_sb = sb;
sb->s_maxbytes = MAX_LFS_FILESIZE;
sb->s_blocksize = PAGE_SIZE;
sb->s_blocksize_bits = PAGE_SHIFT;
@ -405,6 +449,10 @@ static int pstore_fill_super(struct super_block *sb, void *data, int silent)
if (!sb->s_root)
return -ENOMEM;
mutex_lock(&pstore_sb_lock);
pstore_sb = sb;
mutex_unlock(&pstore_sb_lock);
pstore_get_records(0);
return 0;
@ -418,8 +466,17 @@ static struct dentry *pstore_mount(struct file_system_type *fs_type,
static void pstore_kill_sb(struct super_block *sb)
{
mutex_lock(&pstore_sb_lock);
WARN_ON(pstore_sb != sb);
kill_litter_super(sb);
pstore_sb = NULL;
mutex_lock(&records_list_lock);
INIT_LIST_HEAD(&records_list);
mutex_unlock(&records_list_lock);
mutex_unlock(&pstore_sb_lock);
}
static struct file_system_type pstore_fs_type = {

View File

@ -12,9 +12,18 @@ extern unsigned long kmsg_bytes;
#ifdef CONFIG_PSTORE_FTRACE
extern void pstore_register_ftrace(void);
extern void pstore_unregister_ftrace(void);
ssize_t pstore_ftrace_combine_log(char **dest_log, size_t *dest_log_size,
const char *src_log, size_t src_log_size);
#else
static inline void pstore_register_ftrace(void) {}
static inline void pstore_unregister_ftrace(void) {}
static inline ssize_t
pstore_ftrace_combine_log(char **dest_log, size_t *dest_log_size,
const char *src_log, size_t src_log_size)
{
*dest_log_size = 0;
return 0;
}
#endif
#ifdef CONFIG_PSTORE_PMSG
@ -31,9 +40,9 @@ extern void pstore_set_kmsg_bytes(int);
extern void pstore_get_records(int);
extern void pstore_get_backend_records(struct pstore_info *psi,
struct dentry *root, int quiet);
extern int pstore_put_backend_records(struct pstore_info *psi);
extern int pstore_mkfile(struct dentry *root,
struct pstore_record *record);
extern bool pstore_is_mounted(void);
extern void pstore_record_init(struct pstore_record *record,
struct pstore_info *psi);

View File

@ -44,7 +44,7 @@ static int pstore_update_ms = -1;
module_param_named(update_ms, pstore_update_ms, int, 0600);
MODULE_PARM_DESC(update_ms, "milliseconds before pstore updates its content "
"(default is -1, which means runtime updates are disabled; "
"enabling this option is not safe, it may lead to further "
"enabling this option may not be safe; it may lead to further "
"corruption on Oopses)");
/* Names should be in the same order as the enum pstore_type_id */
@ -69,19 +69,25 @@ static void pstore_dowork(struct work_struct *);
static DECLARE_WORK(pstore_work, pstore_dowork);
/*
* pstore_lock just protects "psinfo" during
* calls to pstore_register()
* psinfo_lock protects "psinfo" during calls to
* pstore_register(), pstore_unregister(), and
* the filesystem mount/unmount routines.
*/
static DEFINE_SPINLOCK(pstore_lock);
static DEFINE_MUTEX(psinfo_lock);
struct pstore_info *psinfo;
static char *backend;
module_param(backend, charp, 0444);
MODULE_PARM_DESC(backend, "specific backend to use");
static char *compress =
#ifdef CONFIG_PSTORE_COMPRESS_DEFAULT
CONFIG_PSTORE_COMPRESS_DEFAULT;
#else
NULL;
#endif
module_param(compress, charp, 0444);
MODULE_PARM_DESC(compress, "compression to use");
/* Compression parameters */
static struct crypto_comp *tfm;
@ -129,24 +135,12 @@ enum pstore_type_id pstore_name_to_type(const char *name)
}
EXPORT_SYMBOL_GPL(pstore_name_to_type);
static const char *get_reason_str(enum kmsg_dump_reason reason)
static void pstore_timer_kick(void)
{
switch (reason) {
case KMSG_DUMP_PANIC:
return "Panic";
case KMSG_DUMP_OOPS:
return "Oops";
case KMSG_DUMP_EMERG:
return "Emergency";
case KMSG_DUMP_RESTART:
return "Restart";
case KMSG_DUMP_HALT:
return "Halt";
case KMSG_DUMP_POWEROFF:
return "Poweroff";
default:
return "Unknown";
}
if (pstore_update_ms < 0)
return;
mod_timer(&pstore_timer, jiffies + msecs_to_jiffies(pstore_update_ms));
}
/*
@ -393,7 +387,7 @@ static void pstore_dump(struct kmsg_dumper *dumper,
unsigned int part = 1;
int ret;
why = get_reason_str(reason);
why = kmsg_dump_reason_str(reason);
if (down_trylock(&psinfo->buf_lock)) {
/* Failed to acquire lock: give up if we cannot wait. */
@ -459,8 +453,10 @@ static void pstore_dump(struct kmsg_dumper *dumper,
}
ret = psinfo->write(&record);
if (ret == 0 && reason == KMSG_DUMP_OOPS && pstore_is_mounted())
if (ret == 0 && reason == KMSG_DUMP_OOPS) {
pstore_new_entry = 1;
pstore_timer_kick();
}
total += record.size;
part++;
@ -503,14 +499,20 @@ static void pstore_console_write(struct console *con, const char *s, unsigned c)
}
static struct console pstore_console = {
.name = "pstore",
.write = pstore_console_write,
.flags = CON_PRINTBUFFER | CON_ENABLED | CON_ANYTIME,
.index = -1,
};
static void pstore_register_console(void)
{
/* Show which backend is going to get console writes. */
strscpy(pstore_console.name, psinfo->name,
sizeof(pstore_console.name));
/*
* Always initialize flags here since prior unregister_console()
* calls may have changed settings (specifically CON_ENABLED).
*/
pstore_console.flags = CON_PRINTBUFFER | CON_ENABLED | CON_ANYTIME;
register_console(&pstore_console);
}
@ -555,8 +557,6 @@ static int pstore_write_user_compat(struct pstore_record *record,
*/
int pstore_register(struct pstore_info *psi)
{
struct module *owner = psi->owner;
if (backend && strcmp(backend, psi->name)) {
pr_warn("ignoring unexpected backend '%s'\n", psi->name);
return -EPERM;
@ -576,11 +576,11 @@ int pstore_register(struct pstore_info *psi)
return -EINVAL;
}
spin_lock(&pstore_lock);
mutex_lock(&psinfo_lock);
if (psinfo) {
pr_warn("backend '%s' already loaded: ignoring '%s'\n",
psinfo->name, psi->name);
spin_unlock(&pstore_lock);
mutex_unlock(&psinfo_lock);
return -EBUSY;
}
@ -589,21 +589,16 @@ int pstore_register(struct pstore_info *psi)
psinfo = psi;
mutex_init(&psinfo->read_mutex);
sema_init(&psinfo->buf_lock, 1);
spin_unlock(&pstore_lock);
if (owner && !try_module_get(owner)) {
psinfo = NULL;
return -EINVAL;
}
if (psi->flags & PSTORE_FLAGS_DMESG)
allocate_buf_for_compression();
if (pstore_is_mounted())
pstore_get_records(0);
pstore_get_records(0);
if (psi->flags & PSTORE_FLAGS_DMESG)
if (psi->flags & PSTORE_FLAGS_DMESG) {
pstore_dumper.max_reason = psinfo->max_reason;
pstore_register_kmsg();
}
if (psi->flags & PSTORE_FLAGS_CONSOLE)
pstore_register_console();
if (psi->flags & PSTORE_FLAGS_FTRACE)
@ -612,33 +607,36 @@ int pstore_register(struct pstore_info *psi)
pstore_register_pmsg();
/* Start watching for new records, if desired. */
if (pstore_update_ms >= 0) {
pstore_timer.expires = jiffies +
msecs_to_jiffies(pstore_update_ms);
add_timer(&pstore_timer);
}
pstore_timer_kick();
/*
* Update the module parameter backend, so it is visible
* through /sys/module/pstore/parameters/backend
*/
backend = psi->name;
backend = kstrdup(psi->name, GFP_KERNEL);
pr_info("Registered %s as persistent store backend\n", psi->name);
module_put(owner);
mutex_unlock(&psinfo_lock);
return 0;
}
EXPORT_SYMBOL_GPL(pstore_register);
void pstore_unregister(struct pstore_info *psi)
{
/* Stop timer and make sure all work has finished. */
pstore_update_ms = -1;
del_timer_sync(&pstore_timer);
flush_work(&pstore_work);
/* It's okay to unregister nothing. */
if (!psi)
return;
mutex_lock(&psinfo_lock);
/* Only one backend can be registered at a time. */
if (WARN_ON(psi != psinfo)) {
mutex_unlock(&psinfo_lock);
return;
}
/* Unregister all callbacks. */
if (psi->flags & PSTORE_FLAGS_PMSG)
pstore_unregister_pmsg();
if (psi->flags & PSTORE_FLAGS_FTRACE)
@ -648,10 +646,19 @@ void pstore_unregister(struct pstore_info *psi)
if (psi->flags & PSTORE_FLAGS_DMESG)
pstore_unregister_kmsg();
/* Stop timer and make sure all work has finished. */
del_timer_sync(&pstore_timer);
flush_work(&pstore_work);
/* Remove all backend records from filesystem tree. */
pstore_put_backend_records(psi);
free_buf_for_compression();
psinfo = NULL;
kfree(backend);
backend = NULL;
mutex_unlock(&psinfo_lock);
}
EXPORT_SYMBOL_GPL(pstore_unregister);
@ -788,9 +795,7 @@ static void pstore_timefunc(struct timer_list *unused)
schedule_work(&pstore_work);
}
if (pstore_update_ms >= 0)
mod_timer(&pstore_timer,
jiffies + msecs_to_jiffies(pstore_update_ms));
pstore_timer_kick();
}
static void __init pstore_choose_compression(void)
@ -835,11 +840,5 @@ static void __exit pstore_exit(void)
}
module_exit(pstore_exit)
module_param(compress, charp, 0444);
MODULE_PARM_DESC(compress, "Pstore compression to use");
module_param(backend, charp, 0444);
MODULE_PARM_DESC(backend, "Pstore backend to use");
MODULE_AUTHOR("Tony Luck <tony.luck@intel.com>");
MODULE_LICENSE("GPL");

View File

@ -21,6 +21,7 @@
#include <linux/pstore_ram.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include "internal.h"
#define RAMOOPS_KERNMSG_HDR "===="
#define MIN_MEM_SIZE 4096UL
@ -53,22 +54,27 @@ MODULE_PARM_DESC(mem_size,
"size of reserved RAM used to store oops/panic logs");
static unsigned int mem_type;
module_param(mem_type, uint, 0600);
module_param(mem_type, uint, 0400);
MODULE_PARM_DESC(mem_type,
"set to 1 to try to use unbuffered memory (default 0)");
static int dump_oops = 1;
module_param(dump_oops, int, 0600);
MODULE_PARM_DESC(dump_oops,
"set to 1 to dump oopses, 0 to only dump panics (default 1)");
static int ramoops_max_reason = -1;
module_param_named(max_reason, ramoops_max_reason, int, 0400);
MODULE_PARM_DESC(max_reason,
"maximum reason for kmsg dump (default 2: Oops and Panic) ");
static int ramoops_ecc;
module_param_named(ecc, ramoops_ecc, int, 0600);
module_param_named(ecc, ramoops_ecc, int, 0400);
MODULE_PARM_DESC(ramoops_ecc,
"if non-zero, the option enables ECC support and specifies "
"ECC buffer size in bytes (1 is a special value, means 16 "
"bytes ECC)");
static int ramoops_dump_oops = -1;
module_param_named(dump_oops, ramoops_dump_oops, int, 0400);
MODULE_PARM_DESC(dump_oops,
"(deprecated: use max_reason instead) set to 1 to dump oopses & panics, 0 to only dump panics");
struct ramoops_context {
struct persistent_ram_zone **dprzs; /* Oops dump zones */
struct persistent_ram_zone *cprz; /* Console zone */
@ -81,7 +87,6 @@ struct ramoops_context {
size_t console_size;
size_t ftrace_size;
size_t pmsg_size;
int dump_oops;
u32 flags;
struct persistent_ram_ecc_info ecc_info;
unsigned int max_dump_cnt;
@ -168,58 +173,6 @@ static bool prz_ok(struct persistent_ram_zone *prz)
persistent_ram_ecc_string(prz, NULL, 0));
}
static ssize_t ftrace_log_combine(struct persistent_ram_zone *dest,
struct persistent_ram_zone *src)
{
size_t dest_size, src_size, total, dest_off, src_off;
size_t dest_idx = 0, src_idx = 0, merged_idx = 0;
void *merged_buf;
struct pstore_ftrace_record *drec, *srec, *mrec;
size_t record_size = sizeof(struct pstore_ftrace_record);
dest_off = dest->old_log_size % record_size;
dest_size = dest->old_log_size - dest_off;
src_off = src->old_log_size % record_size;
src_size = src->old_log_size - src_off;
total = dest_size + src_size;
merged_buf = kmalloc(total, GFP_KERNEL);
if (!merged_buf)
return -ENOMEM;
drec = (struct pstore_ftrace_record *)(dest->old_log + dest_off);
srec = (struct pstore_ftrace_record *)(src->old_log + src_off);
mrec = (struct pstore_ftrace_record *)(merged_buf);
while (dest_size > 0 && src_size > 0) {
if (pstore_ftrace_read_timestamp(&drec[dest_idx]) <
pstore_ftrace_read_timestamp(&srec[src_idx])) {
mrec[merged_idx++] = drec[dest_idx++];
dest_size -= record_size;
} else {
mrec[merged_idx++] = srec[src_idx++];
src_size -= record_size;
}
}
while (dest_size > 0) {
mrec[merged_idx++] = drec[dest_idx++];
dest_size -= record_size;
}
while (src_size > 0) {
mrec[merged_idx++] = srec[src_idx++];
src_size -= record_size;
}
kfree(dest->old_log);
dest->old_log = merged_buf;
dest->old_log_size = total;
return 0;
}
static ssize_t ramoops_pstore_read(struct pstore_record *record)
{
ssize_t size = 0;
@ -291,7 +244,12 @@ static ssize_t ramoops_pstore_read(struct pstore_record *record)
tmp_prz->corrected_bytes +=
prz_next->corrected_bytes;
tmp_prz->bad_blocks += prz_next->bad_blocks;
size = ftrace_log_combine(tmp_prz, prz_next);
size = pstore_ftrace_combine_log(
&tmp_prz->old_log,
&tmp_prz->old_log_size,
prz_next->old_log,
prz_next->old_log_size);
if (size)
goto out;
}
@ -382,16 +340,14 @@ static int notrace ramoops_pstore_write(struct pstore_record *record)
return -EINVAL;
/*
* Out of the various dmesg dump types, ramoops is currently designed
* to only store crash logs, rather than storing general kernel logs.
* We could filter on record->reason here if we wanted to (which
* would duplicate what happened before the "max_reason" setting
* was added), but that would defeat the purpose of a system
* changing printk.always_kmsg_dump, so instead log everything that
* the kmsg dumper sends us, since it should be doing the filtering
* based on the combination of printk.always_kmsg_dump and our
* requested "max_reason".
*/
if (record->reason != KMSG_DUMP_OOPS &&
record->reason != KMSG_DUMP_PANIC)
return -EINVAL;
/* Skip Oopes when configured to do so. */
if (record->reason == KMSG_DUMP_OOPS && !cxt->dump_oops)
return -EINVAL;
/*
* Explicitly only take the first part of any new crash.
@ -644,19 +600,25 @@ static int ramoops_init_prz(const char *name,
return 0;
}
static int ramoops_parse_dt_size(struct platform_device *pdev,
const char *propname, u32 *value)
/* Read a u32 from a dt property and make sure it's safe for an int. */
static int ramoops_parse_dt_u32(struct platform_device *pdev,
const char *propname,
u32 default_value, u32 *value)
{
u32 val32 = 0;
int ret;
ret = of_property_read_u32(pdev->dev.of_node, propname, &val32);
if (ret < 0 && ret != -EINVAL) {
if (ret == -EINVAL) {
/* field is missing, use default value. */
val32 = default_value;
} else if (ret < 0) {
dev_err(&pdev->dev, "failed to parse property %s: %d\n",
propname, ret);
return ret;
}
/* Sanity check our results. */
if (val32 > INT_MAX) {
dev_err(&pdev->dev, "%s %u > INT_MAX\n", propname, val32);
return -EOVERFLOW;
@ -687,23 +649,32 @@ static int ramoops_parse_dt(struct platform_device *pdev,
pdata->mem_size = resource_size(res);
pdata->mem_address = res->start;
pdata->mem_type = of_property_read_bool(of_node, "unbuffered");
pdata->dump_oops = !of_property_read_bool(of_node, "no-dump-oops");
/*
* Setting "no-dump-oops" is deprecated and will be ignored if
* "max_reason" is also specified.
*/
if (of_property_read_bool(of_node, "no-dump-oops"))
pdata->max_reason = KMSG_DUMP_PANIC;
else
pdata->max_reason = KMSG_DUMP_OOPS;
#define parse_size(name, field) { \
ret = ramoops_parse_dt_size(pdev, name, &value); \
#define parse_u32(name, field, default_value) { \
ret = ramoops_parse_dt_u32(pdev, name, default_value, \
&value); \
if (ret < 0) \
return ret; \
field = value; \
}
parse_size("record-size", pdata->record_size);
parse_size("console-size", pdata->console_size);
parse_size("ftrace-size", pdata->ftrace_size);
parse_size("pmsg-size", pdata->pmsg_size);
parse_size("ecc-size", pdata->ecc_info.ecc_size);
parse_size("flags", pdata->flags);
parse_u32("record-size", pdata->record_size, 0);
parse_u32("console-size", pdata->console_size, 0);
parse_u32("ftrace-size", pdata->ftrace_size, 0);
parse_u32("pmsg-size", pdata->pmsg_size, 0);
parse_u32("ecc-size", pdata->ecc_info.ecc_size, 0);
parse_u32("flags", pdata->flags, 0);
parse_u32("max-reason", pdata->max_reason, pdata->max_reason);
#undef parse_size
#undef parse_u32
/*
* Some old Chromebooks relied on the kernel setting the
@ -785,7 +756,6 @@ static int ramoops_probe(struct platform_device *pdev)
cxt->console_size = pdata->console_size;
cxt->ftrace_size = pdata->ftrace_size;
cxt->pmsg_size = pdata->pmsg_size;
cxt->dump_oops = pdata->dump_oops;
cxt->flags = pdata->flags;
cxt->ecc_info = pdata->ecc_info;
@ -828,8 +798,10 @@ static int ramoops_probe(struct platform_device *pdev)
* the single region size is how to check.
*/
cxt->pstore.flags = 0;
if (cxt->max_dump_cnt)
if (cxt->max_dump_cnt) {
cxt->pstore.flags |= PSTORE_FLAGS_DMESG;
cxt->pstore.max_reason = pdata->max_reason;
}
if (cxt->console_size)
cxt->pstore.flags |= PSTORE_FLAGS_CONSOLE;
if (cxt->max_ftrace_cnt)
@ -865,7 +837,7 @@ static int ramoops_probe(struct platform_device *pdev)
mem_size = pdata->mem_size;
mem_address = pdata->mem_address;
record_size = pdata->record_size;
dump_oops = pdata->dump_oops;
ramoops_max_reason = pdata->max_reason;
ramoops_console_size = pdata->console_size;
ramoops_pmsg_size = pdata->pmsg_size;
ramoops_ftrace_size = pdata->ftrace_size;
@ -948,7 +920,16 @@ static void __init ramoops_register_dummy(void)
pdata.console_size = ramoops_console_size;
pdata.ftrace_size = ramoops_ftrace_size;
pdata.pmsg_size = ramoops_pmsg_size;
pdata.dump_oops = dump_oops;
/* If "max_reason" is set, its value has priority over "dump_oops". */
if (ramoops_max_reason >= 0)
pdata.max_reason = ramoops_max_reason;
/* Otherwise, if "dump_oops" is set, parse it into "max_reason". */
else if (ramoops_dump_oops != -1)
pdata.max_reason = ramoops_dump_oops ? KMSG_DUMP_OOPS
: KMSG_DUMP_PANIC;
/* And if neither are explicitly set, use the default. */
else
pdata.max_reason = KMSG_DUMP_OOPS;
pdata.flags = RAMOOPS_FLAG_FTRACE_PER_CPU;
/*

1465
fs/pstore/zone.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -15,12 +15,15 @@
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/parser.h>
#include <linux/slab.h>
#include <uapi/linux/fscrypt.h>
#define FS_CRYPTO_BLOCK_SIZE 16
union fscrypt_context;
struct fscrypt_info;
struct seq_file;
struct fscrypt_str {
unsigned char *name;
@ -56,10 +59,12 @@ struct fscrypt_name {
struct fscrypt_operations {
unsigned int flags;
const char *key_prefix;
int (*get_context)(struct inode *, void *, size_t);
int (*set_context)(struct inode *, const void *, size_t, void *);
bool (*dummy_context)(struct inode *);
bool (*empty_dir)(struct inode *);
int (*get_context)(struct inode *inode, void *ctx, size_t len);
int (*set_context)(struct inode *inode, const void *ctx, size_t len,
void *fs_data);
const union fscrypt_context *(*get_dummy_context)(
struct super_block *sb);
bool (*empty_dir)(struct inode *inode);
unsigned int max_namelen;
bool (*has_stable_inodes)(struct super_block *sb);
void (*get_ino_and_lblk_bits)(struct super_block *sb,
@ -79,6 +84,7 @@ static inline bool fscrypt_has_encryption_key(const struct inode *inode)
/**
* fscrypt_needs_contents_encryption() - check whether an inode needs
* contents encryption
* @inode: the inode to check
*
* Return: %true iff the inode is an encrypted regular file and the kernel was
* built with fscrypt support.
@ -91,10 +97,12 @@ static inline bool fscrypt_needs_contents_encryption(const struct inode *inode)
return IS_ENCRYPTED(inode) && S_ISREG(inode->i_mode);
}
static inline bool fscrypt_dummy_context_enabled(struct inode *inode)
static inline const union fscrypt_context *
fscrypt_get_dummy_context(struct super_block *sb)
{
return inode->i_sb->s_cop->dummy_context &&
inode->i_sb->s_cop->dummy_context(inode);
if (!sb->s_cop->get_dummy_context)
return NULL;
return sb->s_cop->get_dummy_context(sb);
}
/*
@ -110,22 +118,21 @@ static inline void fscrypt_handle_d_move(struct dentry *dentry)
}
/* crypto.c */
extern void fscrypt_enqueue_decrypt_work(struct work_struct *);
void fscrypt_enqueue_decrypt_work(struct work_struct *);
extern struct page *fscrypt_encrypt_pagecache_blocks(struct page *page,
unsigned int len,
unsigned int offs,
gfp_t gfp_flags);
extern int fscrypt_encrypt_block_inplace(const struct inode *inode,
struct page *page, unsigned int len,
unsigned int offs, u64 lblk_num,
gfp_t gfp_flags);
struct page *fscrypt_encrypt_pagecache_blocks(struct page *page,
unsigned int len,
unsigned int offs,
gfp_t gfp_flags);
int fscrypt_encrypt_block_inplace(const struct inode *inode, struct page *page,
unsigned int len, unsigned int offs,
u64 lblk_num, gfp_t gfp_flags);
extern int fscrypt_decrypt_pagecache_blocks(struct page *page, unsigned int len,
unsigned int offs);
extern int fscrypt_decrypt_block_inplace(const struct inode *inode,
struct page *page, unsigned int len,
unsigned int offs, u64 lblk_num);
int fscrypt_decrypt_pagecache_blocks(struct page *page, unsigned int len,
unsigned int offs);
int fscrypt_decrypt_block_inplace(const struct inode *inode, struct page *page,
unsigned int len, unsigned int offs,
u64 lblk_num);
static inline bool fscrypt_is_bounce_page(struct page *page)
{
@ -137,81 +144,93 @@ static inline struct page *fscrypt_pagecache_page(struct page *bounce_page)
return (struct page *)page_private(bounce_page);
}
extern void fscrypt_free_bounce_page(struct page *bounce_page);
extern int fscrypt_d_revalidate(struct dentry *dentry, unsigned int flags);
void fscrypt_free_bounce_page(struct page *bounce_page);
int fscrypt_d_revalidate(struct dentry *dentry, unsigned int flags);
/* policy.c */
extern int fscrypt_ioctl_set_policy(struct file *, const void __user *);
extern int fscrypt_ioctl_get_policy(struct file *, void __user *);
extern int fscrypt_ioctl_get_policy_ex(struct file *, void __user *);
extern int fscrypt_ioctl_get_nonce(struct file *filp, void __user *arg);
extern int fscrypt_has_permitted_context(struct inode *, struct inode *);
extern int fscrypt_inherit_context(struct inode *, struct inode *,
void *, bool);
int fscrypt_ioctl_set_policy(struct file *filp, const void __user *arg);
int fscrypt_ioctl_get_policy(struct file *filp, void __user *arg);
int fscrypt_ioctl_get_policy_ex(struct file *filp, void __user *arg);
int fscrypt_ioctl_get_nonce(struct file *filp, void __user *arg);
int fscrypt_has_permitted_context(struct inode *parent, struct inode *child);
int fscrypt_inherit_context(struct inode *parent, struct inode *child,
void *fs_data, bool preload);
struct fscrypt_dummy_context {
const union fscrypt_context *ctx;
};
int fscrypt_set_test_dummy_encryption(struct super_block *sb,
const substring_t *arg,
struct fscrypt_dummy_context *dummy_ctx);
void fscrypt_show_test_dummy_encryption(struct seq_file *seq, char sep,
struct super_block *sb);
static inline void
fscrypt_free_dummy_context(struct fscrypt_dummy_context *dummy_ctx)
{
kfree(dummy_ctx->ctx);
dummy_ctx->ctx = NULL;
}
/* keyring.c */
extern void fscrypt_sb_free(struct super_block *sb);
extern int fscrypt_ioctl_add_key(struct file *filp, void __user *arg);
extern int fscrypt_ioctl_remove_key(struct file *filp, void __user *arg);
extern int fscrypt_ioctl_remove_key_all_users(struct file *filp,
void __user *arg);
extern int fscrypt_ioctl_get_key_status(struct file *filp, void __user *arg);
extern int fscrypt_register_key_removal_notifier(struct notifier_block *nb);
extern int fscrypt_unregister_key_removal_notifier(struct notifier_block *nb);
void fscrypt_sb_free(struct super_block *sb);
int fscrypt_ioctl_add_key(struct file *filp, void __user *arg);
int fscrypt_ioctl_remove_key(struct file *filp, void __user *arg);
int fscrypt_ioctl_remove_key_all_users(struct file *filp, void __user *arg);
int fscrypt_ioctl_get_key_status(struct file *filp, void __user *arg);
int fscrypt_register_key_removal_notifier(struct notifier_block *nb);
int fscrypt_unregister_key_removal_notifier(struct notifier_block *nb);
/* keysetup.c */
extern int fscrypt_get_encryption_info(struct inode *);
extern void fscrypt_put_encryption_info(struct inode *);
extern void fscrypt_free_inode(struct inode *);
extern int fscrypt_drop_inode(struct inode *inode);
int fscrypt_get_encryption_info(struct inode *inode);
void fscrypt_put_encryption_info(struct inode *inode);
void fscrypt_free_inode(struct inode *inode);
int fscrypt_drop_inode(struct inode *inode);
/* fname.c */
extern int fscrypt_setup_filename(struct inode *, const struct qstr *,
int lookup, struct fscrypt_name *);
int fscrypt_setup_filename(struct inode *inode, const struct qstr *iname,
int lookup, struct fscrypt_name *fname);
static inline void fscrypt_free_filename(struct fscrypt_name *fname)
{
kfree(fname->crypto_buf.name);
}
extern int fscrypt_fname_alloc_buffer(const struct inode *, u32,
struct fscrypt_str *);
extern void fscrypt_fname_free_buffer(struct fscrypt_str *);
extern int fscrypt_fname_disk_to_usr(const struct inode *inode,
u32 hash, u32 minor_hash,
const struct fscrypt_str *iname,
struct fscrypt_str *oname);
extern bool fscrypt_match_name(const struct fscrypt_name *fname,
const u8 *de_name, u32 de_name_len);
extern u64 fscrypt_fname_siphash(const struct inode *dir,
const struct qstr *name);
int fscrypt_fname_alloc_buffer(const struct inode *inode, u32 max_encrypted_len,
struct fscrypt_str *crypto_str);
void fscrypt_fname_free_buffer(struct fscrypt_str *crypto_str);
int fscrypt_fname_disk_to_usr(const struct inode *inode,
u32 hash, u32 minor_hash,
const struct fscrypt_str *iname,
struct fscrypt_str *oname);
bool fscrypt_match_name(const struct fscrypt_name *fname,
const u8 *de_name, u32 de_name_len);
u64 fscrypt_fname_siphash(const struct inode *dir, const struct qstr *name);
/* bio.c */
extern void fscrypt_decrypt_bio(struct bio *);
extern int fscrypt_zeroout_range(const struct inode *, pgoff_t, sector_t,
unsigned int);
void fscrypt_decrypt_bio(struct bio *bio);
int fscrypt_zeroout_range(const struct inode *inode, pgoff_t lblk,
sector_t pblk, unsigned int len);
/* hooks.c */
extern int fscrypt_file_open(struct inode *inode, struct file *filp);
extern int __fscrypt_prepare_link(struct inode *inode, struct inode *dir,
struct dentry *dentry);
extern int __fscrypt_prepare_rename(struct inode *old_dir,
struct dentry *old_dentry,
struct inode *new_dir,
struct dentry *new_dentry,
unsigned int flags);
extern int __fscrypt_prepare_lookup(struct inode *dir, struct dentry *dentry,
struct fscrypt_name *fname);
extern int fscrypt_prepare_setflags(struct inode *inode,
unsigned int oldflags, unsigned int flags);
extern int __fscrypt_prepare_symlink(struct inode *dir, unsigned int len,
unsigned int max_len,
struct fscrypt_str *disk_link);
extern int __fscrypt_encrypt_symlink(struct inode *inode, const char *target,
unsigned int len,
struct fscrypt_str *disk_link);
extern const char *fscrypt_get_symlink(struct inode *inode, const void *caddr,
unsigned int max_size,
struct delayed_call *done);
int fscrypt_file_open(struct inode *inode, struct file *filp);
int __fscrypt_prepare_link(struct inode *inode, struct inode *dir,
struct dentry *dentry);
int __fscrypt_prepare_rename(struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry,
unsigned int flags);
int __fscrypt_prepare_lookup(struct inode *dir, struct dentry *dentry,
struct fscrypt_name *fname);
int fscrypt_prepare_setflags(struct inode *inode,
unsigned int oldflags, unsigned int flags);
int __fscrypt_prepare_symlink(struct inode *dir, unsigned int len,
unsigned int max_len,
struct fscrypt_str *disk_link);
int __fscrypt_encrypt_symlink(struct inode *inode, const char *target,
unsigned int len, struct fscrypt_str *disk_link);
const char *fscrypt_get_symlink(struct inode *inode, const void *caddr,
unsigned int max_size,
struct delayed_call *done);
static inline void fscrypt_set_ops(struct super_block *sb,
const struct fscrypt_operations *s_cop)
{
@ -229,9 +248,10 @@ static inline bool fscrypt_needs_contents_encryption(const struct inode *inode)
return false;
}
static inline bool fscrypt_dummy_context_enabled(struct inode *inode)
static inline const union fscrypt_context *
fscrypt_get_dummy_context(struct super_block *sb)
{
return false;
return NULL;
}
static inline void fscrypt_handle_d_move(struct dentry *dentry)
@ -326,6 +346,20 @@ static inline int fscrypt_inherit_context(struct inode *parent,
return -EOPNOTSUPP;
}
struct fscrypt_dummy_context {
};
static inline void fscrypt_show_test_dummy_encryption(struct seq_file *seq,
char sep,
struct super_block *sb)
{
}
static inline void
fscrypt_free_dummy_context(struct fscrypt_dummy_context *dummy_ctx)
{
}
/* keyring.c */
static inline void fscrypt_sb_free(struct super_block *sb)
{
@ -610,7 +644,7 @@ fscrypt_inode_should_skip_dm_default_key(const struct inode *inode)
#endif
/**
* fscrypt_require_key - require an inode's encryption key
* fscrypt_require_key() - require an inode's encryption key
* @inode: the inode we need the key for
*
* If the inode is encrypted, set up its encryption key if not already done.
@ -636,7 +670,8 @@ static inline int fscrypt_require_key(struct inode *inode)
}
/**
* fscrypt_prepare_link - prepare to link an inode into a possibly-encrypted directory
* fscrypt_prepare_link() - prepare to link an inode into a possibly-encrypted
* directory
* @old_dentry: an existing dentry for the inode being linked
* @dir: the target directory
* @dentry: negative dentry for the target filename
@ -663,7 +698,8 @@ static inline int fscrypt_prepare_link(struct dentry *old_dentry,
}
/**
* fscrypt_prepare_rename - prepare for a rename between possibly-encrypted directories
* fscrypt_prepare_rename() - prepare for a rename between possibly-encrypted
* directories
* @old_dir: source directory
* @old_dentry: dentry for source file
* @new_dir: target directory
@ -696,7 +732,8 @@ static inline int fscrypt_prepare_rename(struct inode *old_dir,
}
/**
* fscrypt_prepare_lookup - prepare to lookup a name in a possibly-encrypted directory
* fscrypt_prepare_lookup() - prepare to lookup a name in a possibly-encrypted
* directory
* @dir: directory being searched
* @dentry: filename being looked up
* @fname: (output) the name to use to search the on-disk directory
@ -730,7 +767,8 @@ static inline int fscrypt_prepare_lookup(struct inode *dir,
}
/**
* fscrypt_prepare_setattr - prepare to change a possibly-encrypted inode's attributes
* fscrypt_prepare_setattr() - prepare to change a possibly-encrypted inode's
* attributes
* @dentry: dentry through which the inode is being changed
* @attr: attributes to change
*
@ -755,7 +793,7 @@ static inline int fscrypt_prepare_setattr(struct dentry *dentry,
}
/**
* fscrypt_prepare_symlink - prepare to create a possibly-encrypted symlink
* fscrypt_prepare_symlink() - prepare to create a possibly-encrypted symlink
* @dir: directory in which the symlink is being created
* @target: plaintext symlink target
* @len: length of @target excluding null terminator
@ -783,7 +821,7 @@ static inline int fscrypt_prepare_symlink(struct inode *dir,
unsigned int max_len,
struct fscrypt_str *disk_link)
{
if (IS_ENCRYPTED(dir) || fscrypt_dummy_context_enabled(dir))
if (IS_ENCRYPTED(dir) || fscrypt_get_dummy_context(dir->i_sb) != NULL)
return __fscrypt_prepare_symlink(dir, len, max_len, disk_link);
disk_link->name = (unsigned char *)target;
@ -794,7 +832,7 @@ static inline int fscrypt_prepare_symlink(struct inode *dir,
}
/**
* fscrypt_encrypt_symlink - encrypt the symlink target if needed
* fscrypt_encrypt_symlink() - encrypt the symlink target if needed
* @inode: symlink inode
* @target: plaintext symlink target
* @len: length of @target excluding null terminator

View File

@ -25,9 +25,8 @@ enum kmsg_dump_reason {
KMSG_DUMP_PANIC,
KMSG_DUMP_OOPS,
KMSG_DUMP_EMERG,
KMSG_DUMP_RESTART,
KMSG_DUMP_HALT,
KMSG_DUMP_POWEROFF,
KMSG_DUMP_SHUTDOWN,
KMSG_DUMP_MAX
};
/**
@ -71,6 +70,8 @@ void kmsg_dump_rewind(struct kmsg_dumper *dumper);
int kmsg_dump_register(struct kmsg_dumper *dumper);
int kmsg_dump_unregister(struct kmsg_dumper *dumper);
const char *kmsg_dump_reason_str(enum kmsg_dump_reason reason);
#else
static inline void kmsg_dump(enum kmsg_dump_reason reason)
{
@ -112,6 +113,11 @@ static inline int kmsg_dump_unregister(struct kmsg_dumper *dumper)
{
return -EINVAL;
}
static inline const char *kmsg_dump_reason_str(enum kmsg_dump_reason reason)
{
return "Disabled";
}
#endif
#endif /* _LINUX_KMSG_DUMP_H */

View File

@ -7,7 +7,8 @@
* but could potentially be used anywhere else that simple option=arg
* parsing is required.
*/
#ifndef _LINUX_PARSER_H
#define _LINUX_PARSER_H
/* associates an integer enumerator with a pattern string. */
struct match_token {
@ -34,3 +35,5 @@ int match_hex(substring_t *, int *result);
bool match_wildcard(const char *pattern, const char *str);
size_t match_strlcpy(char *, const substring_t *, size_t);
char *match_strdup(const substring_t *);
#endif /* _LINUX_PARSER_H */

View File

@ -96,6 +96,12 @@ struct pstore_record {
*
* @read_mutex: serializes @open, @read, @close, and @erase callbacks
* @flags: bitfield of frontends the backend can accept writes for
* @max_reason: Used when PSTORE_FLAGS_DMESG is set. Contains the
* kmsg_dump_reason enum value. KMSG_DUMP_UNDEF means
* "use existing kmsg_dump() filtering, based on the
* printk.always_kmsg_dump boot param" (which is either
* KMSG_DUMP_OOPS when false, or KMSG_DUMP_MAX when
* true); see printk.always_kmsg_dump for more details.
* @data: backend-private pointer passed back during callbacks
*
* Callbacks:
@ -170,7 +176,7 @@ struct pstore_record {
*/
struct pstore_info {
struct module *owner;
char *name;
const char *name;
struct semaphore buf_lock;
char *buf;
@ -179,6 +185,7 @@ struct pstore_info {
struct mutex read_mutex;
int flags;
int max_reason;
void *data;
int (*open)(struct pstore_info *psi);

118
include/linux/pstore_blk.h Normal file
View File

@ -0,0 +1,118 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __PSTORE_BLK_H_
#define __PSTORE_BLK_H_
#include <linux/types.h>
#include <linux/pstore.h>
#include <linux/pstore_zone.h>
/**
* typedef pstore_blk_panic_write_op - panic write operation to block device
*
* @buf: the data to write
* @start_sect: start sector to block device
* @sects: sectors count on buf
*
* Return: On success, zero should be returned. Others excluding -ENOMSG
* mean error. -ENOMSG means to try next zone.
*
* Panic write to block device must be aligned to SECTOR_SIZE.
*/
typedef int (*pstore_blk_panic_write_op)(const char *buf, sector_t start_sect,
sector_t sects);
/**
* struct pstore_blk_info - pstore/blk registration details
*
* @major: Which major device number to support with pstore/blk
* @flags: The supported PSTORE_FLAGS_* from linux/pstore.h.
* @panic_write:The write operation only used for the panic case.
* This can be NULL, but is recommended to avoid losing
* crash data if the kernel's IO path or work queues are
* broken during a panic.
* @devt: The dev_t that pstore/blk has attached to.
* @nr_sects: Number of sectors on @devt.
* @start_sect: Starting sector on @devt.
*/
struct pstore_blk_info {
unsigned int major;
unsigned int flags;
pstore_blk_panic_write_op panic_write;
/* Filled in by pstore/blk after registration. */
dev_t devt;
sector_t nr_sects;
sector_t start_sect;
};
int register_pstore_blk(struct pstore_blk_info *info);
void unregister_pstore_blk(unsigned int major);
/**
* struct pstore_device_info - back-end pstore/blk driver structure.
*
* @total_size: The total size in bytes pstore/blk can use. It must be greater
* than 4096 and be multiple of 4096.
* @flags: Refer to macro starting with PSTORE_FLAGS defined in
* linux/pstore.h. It means what front-ends this device support.
* Zero means all backends for compatible.
* @read: The general read operation. Both of the function parameters
* @size and @offset are relative value to bock device (not the
* whole disk).
* On success, the number of bytes should be returned, others
* means error.
* @write: The same as @read, but the following error number:
* -EBUSY means try to write again later.
* -ENOMSG means to try next zone.
* @erase: The general erase operation for device with special removing
* job. Both of the function parameters @size and @offset are
* relative value to storage.
* Return 0 on success and others on failure.
* @panic_write:The write operation only used for panic case. It's optional
* if you do not care panic log. The parameters are relative
* value to storage.
* On success, the number of bytes should be returned, others
* excluding -ENOMSG mean error. -ENOMSG means to try next zone.
*/
struct pstore_device_info {
unsigned long total_size;
unsigned int flags;
pstore_zone_read_op read;
pstore_zone_write_op write;
pstore_zone_erase_op erase;
pstore_zone_write_op panic_write;
};
int register_pstore_device(struct pstore_device_info *dev);
void unregister_pstore_device(struct pstore_device_info *dev);
/**
* struct pstore_blk_config - the pstore_blk backend configuration
*
* @device: Name of the desired block device
* @max_reason: Maximum kmsg dump reason to store to block device
* @kmsg_size: Total size of for kmsg dumps
* @pmsg_size: Total size of the pmsg storage area
* @console_size: Total size of the console storage area
* @ftrace_size: Total size for ftrace logging data (for all CPUs)
*/
struct pstore_blk_config {
char device[80];
enum kmsg_dump_reason max_reason;
unsigned long kmsg_size;
unsigned long pmsg_size;
unsigned long console_size;
unsigned long ftrace_size;
};
/**
* pstore_blk_get_config - get a copy of the pstore_blk backend configuration
*
* @info: The sturct pstore_blk_config to be filled in
*
* Failure returns negative error code, and success returns 0.
*/
int pstore_blk_get_config(struct pstore_blk_config *info);
#endif

View File

@ -133,7 +133,7 @@ struct ramoops_platform_data {
unsigned long console_size;
unsigned long ftrace_size;
unsigned long pmsg_size;
int dump_oops;
int max_reason;
u32 flags;
struct persistent_ram_ecc_info ecc_info;
};

View File

@ -0,0 +1,60 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __PSTORE_ZONE_H_
#define __PSTORE_ZONE_H_
#include <linux/types.h>
typedef ssize_t (*pstore_zone_read_op)(char *, size_t, loff_t);
typedef ssize_t (*pstore_zone_write_op)(const char *, size_t, loff_t);
typedef ssize_t (*pstore_zone_erase_op)(size_t, loff_t);
/**
* struct pstore_zone_info - pstore/zone back-end driver structure
*
* @owner: Module which is responsible for this back-end driver.
* @name: Name of the back-end driver.
* @total_size: The total size in bytes pstore/zone can use. It must be greater
* than 4096 and be multiple of 4096.
* @kmsg_size: The size of oops/panic zone. Zero means disabled, otherwise,
* it must be multiple of SECTOR_SIZE(512 Bytes).
* @max_reason: Maximum kmsg dump reason to store.
* @pmsg_size: The size of pmsg zone which is the same as @kmsg_size.
* @console_size:The size of console zone which is the same as @kmsg_size.
* @ftrace_size:The size of ftrace zone which is the same as @kmsg_size.
* @read: The general read operation. Both of the function parameters
* @size and @offset are relative value to storage.
* On success, the number of bytes should be returned, others
* mean error.
* @write: The same as @read, but the following error number:
* -EBUSY means try to write again later.
* -ENOMSG means to try next zone.
* @erase: The general erase operation for device with special removing
* job. Both of the function parameters @size and @offset are
* relative value to storage.
* Return 0 on success and others on failure.
* @panic_write:The write operation only used for panic case. It's optional
* if you do not care panic log. The parameters are relative
* value to storage.
* On success, the number of bytes should be returned, others
* excluding -ENOMSG mean error. -ENOMSG means to try next zone.
*/
struct pstore_zone_info {
struct module *owner;
const char *name;
unsigned long total_size;
unsigned long kmsg_size;
int max_reason;
unsigned long pmsg_size;
unsigned long console_size;
unsigned long ftrace_size;
pstore_zone_read_op read;
pstore_zone_write_op write;
pstore_zone_erase_op erase;
pstore_zone_write_op panic_write;
};
extern int register_pstore_zone(struct pstore_zone_info *info);
extern void unregister_pstore_zone(struct pstore_zone_info *info);
#endif

View File

@ -3144,6 +3144,23 @@ EXPORT_SYMBOL_GPL(kmsg_dump_unregister);
static bool always_kmsg_dump;
module_param_named(always_kmsg_dump, always_kmsg_dump, bool, S_IRUGO | S_IWUSR);
const char *kmsg_dump_reason_str(enum kmsg_dump_reason reason)
{
switch (reason) {
case KMSG_DUMP_PANIC:
return "Panic";
case KMSG_DUMP_OOPS:
return "Oops";
case KMSG_DUMP_EMERG:
return "Emergency";
case KMSG_DUMP_SHUTDOWN:
return "Shutdown";
default:
return "Unknown";
}
}
EXPORT_SYMBOL_GPL(kmsg_dump_reason_str);
/**
* kmsg_dump - dump kernel log to kernel message dumpers.
* @reason: the reason (oops, panic etc) for dumping
@ -3157,12 +3174,19 @@ void kmsg_dump(enum kmsg_dump_reason reason)
struct kmsg_dumper *dumper;
unsigned long flags;
if ((reason > KMSG_DUMP_OOPS) && !always_kmsg_dump)
return;
rcu_read_lock();
list_for_each_entry_rcu(dumper, &dump_list, list) {
if (dumper->max_reason && reason > dumper->max_reason)
enum kmsg_dump_reason max_reason = dumper->max_reason;
/*
* If client has not provided a specific max_reason, default
* to KMSG_DUMP_OOPS, unless always_kmsg_dump was set.
*/
if (max_reason == KMSG_DUMP_UNDEF) {
max_reason = always_kmsg_dump ? KMSG_DUMP_MAX :
KMSG_DUMP_OOPS;
}
if (reason > max_reason)
continue;
/* initialize iterator with data about the stored records */

View File

@ -252,7 +252,7 @@ void kernel_restart(char *cmd)
pr_emerg("Restarting system\n");
else
pr_emerg("Restarting system with command '%s'\n", cmd);
kmsg_dump(KMSG_DUMP_RESTART);
kmsg_dump(KMSG_DUMP_SHUTDOWN);
machine_restart(cmd);
}
EXPORT_SYMBOL_GPL(kernel_restart);
@ -276,7 +276,7 @@ void kernel_halt(void)
migrate_to_reboot_cpu();
syscore_shutdown();
pr_emerg("System halted\n");
kmsg_dump(KMSG_DUMP_HALT);
kmsg_dump(KMSG_DUMP_SHUTDOWN);
machine_halt();
}
EXPORT_SYMBOL_GPL(kernel_halt);
@ -294,7 +294,7 @@ void kernel_power_off(void)
migrate_to_reboot_cpu();
syscore_shutdown();
pr_emerg("Power down\n");
kmsg_dump(KMSG_DUMP_POWEROFF);
kmsg_dump(KMSG_DUMP_SHUTDOWN);
machine_power_off();
}
EXPORT_SYMBOL_GPL(kernel_power_off);

View File

@ -10,7 +10,7 @@
. ./common_tests
prlog -n "Checking pstore console is registered ... "
dmesg | grep -q "console \[pstore"
dmesg | grep -Eq "console \[(pstore|${backend})"
show_result $?
prlog -n "Checking /dev/pmsg0 exists ... "