mirror of
https://github.com/torvalds/linux.git
synced 2024-11-25 13:41:51 +00:00
de848da12f
string: - add mem_is_zero() core: - support more device numbers - use XArray for minor ids - add backlight constants - Split dma fence array creation into alloc and arm fbdev: - remove usage of old fbdev hooks kms: - Add might_fault() to drm_modeset_lock priming - Add dynamic per-crtc vblank configuration support dma-buf: - docs cleanup buddy: - Add start address support for trim function printk: - pass description to kmsg_dump scheduler; - Remove full_recover from drm_sched_start ttm: - Make LRU walk restartable after dropping locks - Allow direct reclaim to allocate local memory panic: - add display QR code (in rust) displayport: - mst: GUID improvements bridge: - Silence error message on -EPROBE_DEFER - analogix: Clean aup - bridge-connector: Fix double free - lt6505: Disable interrupt when powered off - tc358767: Make default DP port preemphasis configurable - lt9611uxc: require DRM_BRIDGE_ATTACH_NO_CONNECTOR - anx7625: simplify OF array handling - dw-hdmi: simplify clock handling - lontium-lt8912b: fix mode validation - nwl-dsi: fix mode vsync/hsync polarity xe: - Enable LunarLake and Battlemage support - Introducing Xe2 ccs modifiers for integrated and discrete graphics - rename xe perf to xe observation - use wb caching on DGFX for system memory - add fence timeouts - Lunar Lake graphics/media/display workarounds - Battlemage workarounds - Battlemage GSC support - GSC and HuC fw updates for LL/BM - use dma_fence_chain_free - refactor hw engine lookup and mmio access - enable priority mem read for Xe2 - Add first GuC BMG fw - fix dma-resv lock - Fix DGFX display suspend/resume - Use xe_managed for kernel BOs - Use reserved copy engine for user binds on faulting devices - Allow mixing dma-fence jobs and long-running faulting jobs - fix media TLB invalidation - fix rpm in TTM swapout path - track resources and VF state by PF i915: - Type-C programming fix for MTL+ - FBC cleanup - Calc vblank delay more accurately - On DP MST, Enable LT fallback for UHBR<->non-UHBR rates - Fix DP LTTPR detection - limit relocations to INT_MAX - fix long hangs in buddy allocator on DG2/A380 amdgpu: - Per-queue reset support - SDMA devcoredump support - DCN 4.0.1 updates - GFX12/VCN4/JPEG4 updates - Convert vbios embedded EDID to drm_edid - GFX9.3/9.4 devcoredump support - process isolation framework for GFX 9.4.3/4 - take IOMMU mappings into account for P2P DMA amdkfd: - CRIU fixes - HMM fix - Enable process isolation support for GFX 9.4.3/4 - Allow users to target recommended SDMA engines - KFD support for targetting queues on recommended SDMA engines radeon: - remove .load and drm_dev_alloc - Fix vbios embedded EDID size handling - Convert vbios embedded EDID to drm_edid - Use GEM references instead of TTM - r100 cp init cleanup - Fix potential overflows in evergreen CS offset tracking msm: - DPU: - implement DP/PHY mapping on SC8180X - Enable writeback on SM8150, SC8180X, SM6125, SM6350 - DP: - Enable widebus on all relevant chipsets - MSM8998 HDMI support - GPU: - A642L speedbin support - A615/A306/A621 support - A7xx devcoredump support ast: - astdp: Support AST2600 with VGA - Clean up HPD - Fix timeout loop for DP link training - reorganize output code by type (VGA, DP, etc) - convert to struct drm_edid - fix BMC handling for all outputs exynos: - drop stale MAINTAINERS pattern - constify struct loongson: - use GEM refcount over TTM mgag200: - Improve BMC handling - Support VBLANK intterupts - transparently support BMC outputs nouveau: - Refactor and clean up internals - Use GEM refcount over TTM's gm12u320: - convert to struct drm_edid gma500: - update i2c terms lcdif: - pixel clock fix host1x: - fix syncpoint IRQ during resume - use iommu_paging_domain_alloc() imx: - ipuv3: convert to struct drm_edid omapdrm: - improve error handling - use common helper for_each_endpoint_of_node() panel: - add support for BOE TV101WUM-LL2 plus DT bindings - novatek-nt35950: improve error handling - nv3051d: improve error handling - panel-edp: add support for BOE NE140WUM-N6G; revert support for SDC ATNA45AF01 - visionox-vtdr6130: improve error handling; use devm_regulator_bulk_get_const() - boe-th101mb31ig002: Support for starry-er88577 MIPI-DSI panel plus DT; Fix porch parameter - edp: Support AOU B116XTN02.3, AUO B116XAN06.1, AOU B116XAT04.1, BOE NV140WUM-N41, BOE NV133WUM-N63, BOE NV116WHM-A4D, CMN N116BCA-EA2, CMN N116BCP-EA2, CSW MNB601LS1-4 - himax-hx8394: Support Microchip AC40T08A MIPI Display panel plus DT - ilitek-ili9806e: Support Densitron DMT028VGHMCMI-1D TFT plus DT - jd9365da: Support Melfas lmfbx101117480 MIPI-DSI panel plus DT; Refactor for code sharing - panel-edp: fix name for HKC MB116AN01 - jd9365da: fix "exit sleep" commands - jdi-fhd-r63452: simplify error handling with DSI multi-style helpers - mantix-mlaf057we51: simplify error handling with DSI multi-style helpers - simple: support Innolux G070ACE-LH3 plus DT bindings support On Tat Industrial Company KD50G21-40NT-A1 plus DT bindings - st7701: decouple DSI and DRM code add SPI support support Anbernic RG28XX plus DT bindings mediatek: - support alpha blending - remove cl in struct cmdq_pkt - ovl adaptor fix - add power domain binding for mediatek DPI controller renesas: - rz-du: add support for RZ/G2UL plus DT bindings rockchip: - Improve DP sink-capability reporting - dw_hdmi: Support 4k@60Hz - vop: Support RGB display on Rockchip RK3066; Support 4096px width sti: - convert to struct drm_edid stm: - Avoid UAF wih managed plane and CRTC helpers - Fix module owner - Fix error handling in probe - Depend on COMMON_CLK - ltdc: Fix transparency after disabling plane; Remove unused interrupt tegra: - gr3d: improve PM domain handling - convert to struct drm_edid - Call drm_atomic_helper_shutdown() vc4: - fix PM during detect - replace DRM_ERROR() with drm_error() - v3d: simplify clock retrieval v3d: - Clean up perfmon virtio: - add DRM capset -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEEEKbZHaGwW9KfbeusDHTzWXnEhr4FAmbq43gACgkQDHTzWXnE hr4+lg/+O/r41E7ioitcM0DWeWem0dTlvQr41pJ8jujHvw+bXNdg0BMGWtsTyTLA eOft2AwofsFjg+O7l8IFXOT37mQLdIdfjb3+w5brI198InL3OWC3QV8ZSwY9VGET n8crO9jFoxNmHZnFniBZbtI6egTyl6H+2ey3E0MTnKiPUKZQvsK/4+x532yVLPob UUOze5wcjyGZc7LJEIZPohPVneCb9ki7sabDQqh4cxIQ0Eg+nqPpWjYM4XVd+lTS 8QmssbR49LrJ7z9m90qVE+8TjYUCn+ChDPMs61KZAAnc8k++nK41btjGZ23mDKPb YEguahCYthWJ4U8K18iXBPnLPxZv5+harQ8OIWAUYqdIOWSXHozvuJ2Z84eHV13a 9mQ5vIymXang8G1nEXwX/vml9uhVhBCeWu3qfdse2jfaTWYUb1YzhqUoFvqI0R0K 8wT03MyNdx965CSqAhpH5Jd559ueZmpd+jsHOfhAS+1gxfD6NgoPXv7lpnMUmGWX SnaeC9RLD4cgy7j2Swo7TEqQHrvK5XhZSwX94kU6RPmFE5RRKqWgFVQmwuikDMId UpNqDnPT5NL2UX4TNG4V4coyTXvKgVcSB9TA7j8NSLfwdGHhiz73pkYosaZXKyxe u6qKMwMONfZiT20nhD7RhH0AFnnKosAcO14dhn0TKFZPY6Ce9O8= =7jR+ -----END PGP SIGNATURE----- Merge tag 'drm-next-2024-09-19' of https://gitlab.freedesktop.org/drm/kernel Pull drm updates from Dave Airlie: "This adds a couple of patches outside the drm core, all should be acked appropriately, the string and pstore ones are the main ones that come to mind. Otherwise it's the usual drivers, xe is getting enabled by default on some new hardware, we've changed the device number handling to allow more devices, and we added some optional rust code to create QR codes in the panic handler, an idea first suggested I think 10 years ago :-) string: - add mem_is_zero() core: - support more device numbers - use XArray for minor ids - add backlight constants - Split dma fence array creation into alloc and arm fbdev: - remove usage of old fbdev hooks kms: - Add might_fault() to drm_modeset_lock priming - Add dynamic per-crtc vblank configuration support dma-buf: - docs cleanup buddy: - Add start address support for trim function printk: - pass description to kmsg_dump scheduler: - Remove full_recover from drm_sched_start ttm: - Make LRU walk restartable after dropping locks - Allow direct reclaim to allocate local memory panic: - add display QR code (in rust) displayport: - mst: GUID improvements bridge: - Silence error message on -EPROBE_DEFER - analogix: Clean aup - bridge-connector: Fix double free - lt6505: Disable interrupt when powered off - tc358767: Make default DP port preemphasis configurable - lt9611uxc: require DRM_BRIDGE_ATTACH_NO_CONNECTOR - anx7625: simplify OF array handling - dw-hdmi: simplify clock handling - lontium-lt8912b: fix mode validation - nwl-dsi: fix mode vsync/hsync polarity xe: - Enable LunarLake and Battlemage support - Introducing Xe2 ccs modifiers for integrated and discrete graphics - rename xe perf to xe observation - use wb caching on DGFX for system memory - add fence timeouts - Lunar Lake graphics/media/display workarounds - Battlemage workarounds - Battlemage GSC support - GSC and HuC fw updates for LL/BM - use dma_fence_chain_free - refactor hw engine lookup and mmio access - enable priority mem read for Xe2 - Add first GuC BMG fw - fix dma-resv lock - Fix DGFX display suspend/resume - Use xe_managed for kernel BOs - Use reserved copy engine for user binds on faulting devices - Allow mixing dma-fence jobs and long-running faulting jobs - fix media TLB invalidation - fix rpm in TTM swapout path - track resources and VF state by PF i915: - Type-C programming fix for MTL+ - FBC cleanup - Calc vblank delay more accurately - On DP MST, Enable LT fallback for UHBR<->non-UHBR rates - Fix DP LTTPR detection - limit relocations to INT_MAX - fix long hangs in buddy allocator on DG2/A380 amdgpu: - Per-queue reset support - SDMA devcoredump support - DCN 4.0.1 updates - GFX12/VCN4/JPEG4 updates - Convert vbios embedded EDID to drm_edid - GFX9.3/9.4 devcoredump support - process isolation framework for GFX 9.4.3/4 - take IOMMU mappings into account for P2P DMA amdkfd: - CRIU fixes - HMM fix - Enable process isolation support for GFX 9.4.3/4 - Allow users to target recommended SDMA engines - KFD support for targetting queues on recommended SDMA engines radeon: - remove .load and drm_dev_alloc - Fix vbios embedded EDID size handling - Convert vbios embedded EDID to drm_edid - Use GEM references instead of TTM - r100 cp init cleanup - Fix potential overflows in evergreen CS offset tracking msm: - DPU: - implement DP/PHY mapping on SC8180X - Enable writeback on SM8150, SC8180X, SM6125, SM6350 - DP: - Enable widebus on all relevant chipsets - MSM8998 HDMI support - GPU: - A642L speedbin support - A615/A306/A621 support - A7xx devcoredump support ast: - astdp: Support AST2600 with VGA - Clean up HPD - Fix timeout loop for DP link training - reorganize output code by type (VGA, DP, etc) - convert to struct drm_edid - fix BMC handling for all outputs exynos: - drop stale MAINTAINERS pattern - constify struct loongson: - use GEM refcount over TTM mgag200: - Improve BMC handling - Support VBLANK intterupts - transparently support BMC outputs nouveau: - Refactor and clean up internals - Use GEM refcount over TTM's gm12u320: - convert to struct drm_edid gma500: - update i2c terms lcdif: - pixel clock fix host1x: - fix syncpoint IRQ during resume - use iommu_paging_domain_alloc() imx: - ipuv3: convert to struct drm_edid omapdrm: - improve error handling - use common helper for_each_endpoint_of_node() panel: - add support for BOE TV101WUM-LL2 plus DT bindings - novatek-nt35950: improve error handling - nv3051d: improve error handling - panel-edp: - add support for BOE NE140WUM-N6G - revert support for SDC ATNA45AF01 - visionox-vtdr6130: - improve error handling - use devm_regulator_bulk_get_const() - boe-th101mb31ig002: - Support for starry-er88577 MIPI-DSI panel plus DT - Fix porch parameter - edp: Support AOU B116XTN02.3, AUO B116XAN06.1, AOU B116XAT04.1, BOE NV140WUM-N41, BOE NV133WUM-N63, BOE NV116WHM-A4D, CMN N116BCA-EA2, CMN N116BCP-EA2, CSW MNB601LS1-4 - himax-hx8394: Support Microchip AC40T08A MIPI Display panel plus DT - ilitek-ili9806e: Support Densitron DMT028VGHMCMI-1D TFT plus DT - jd9365da: - Support Melfas lmfbx101117480 MIPI-DSI panel plus DT - Refactor for code sharing - panel-edp: fix name for HKC MB116AN01 - jd9365da: fix "exit sleep" commands - jdi-fhd-r63452: simplify error handling with DSI multi-style helpers - mantix-mlaf057we51: simplify error handling with DSI multi-style helpers - simple: - support Innolux G070ACE-LH3 plus DT bindings - support On Tat Industrial Company KD50G21-40NT-A1 plus DT bindings - st7701: - decouple DSI and DRM code - add SPI support - support Anbernic RG28XX plus DT bindings mediatek: - support alpha blending - remove cl in struct cmdq_pkt - ovl adaptor fix - add power domain binding for mediatek DPI controller renesas: - rz-du: add support for RZ/G2UL plus DT bindings rockchip: - Improve DP sink-capability reporting - dw_hdmi: Support 4k@60Hz - vop: - Support RGB display on Rockchip RK3066 - Support 4096px width sti: - convert to struct drm_edid stm: - Avoid UAF wih managed plane and CRTC helpers - Fix module owner - Fix error handling in probe - Depend on COMMON_CLK - ltdc: - Fix transparency after disabling plane - Remove unused interrupt tegra: - gr3d: improve PM domain handling - convert to struct drm_edid - Call drm_atomic_helper_shutdown() vc4: - fix PM during detect - replace DRM_ERROR() with drm_error() - v3d: simplify clock retrieval v3d: - Clean up perfmon virtio: - add DRM capset" * tag 'drm-next-2024-09-19' of https://gitlab.freedesktop.org/drm/kernel: (1326 commits) drm/xe: Fix missing conversion to xe_display_pm_runtime_resume drm/xe/xe2hpg: Add Wa_15016589081 drm/xe: Don't keep stale pointer to bo->ggtt_node drm/xe: fix missing 'xe_vm_put' drm/xe: fix build warning with CONFIG_PM=n drm/xe: Suppress missing outer rpm protection warning drm/xe: prevent potential UAF in pf_provision_vf_ggtt() drm/amd/display: Add all planes on CRTC to state for overlay cursor drm/i915/bios: fix printk format width drm/i915/display: Fix BMG CCS modifiers drm/amdgpu: get rid of bogus includes of fdtable.h drm/amdkfd: CRIU fixes drm/amdgpu: fix a race in kfd_mem_export_dmabuf() drm: new helper: drm_gem_prime_handle_to_dmabuf() drm/amdgpu/atomfirmware: Silence UBSAN warning drm/amdgpu: Fix kdoc entry in 'amdgpu_vm_cpu_prepare' drm/amd/amdgpu: apply command submission parser for JPEG v1 drm/amd/amdgpu: apply command submission parser for JPEG v2+ drm/amd/pm: fix the pp_dpm_pcie issue on smu v14.0.2/3 drm/amd/pm: update the features set on smu v14.0.2/3 ...
766 lines
18 KiB
C
766 lines
18 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Persistent Storage - platform driver interface parts.
|
|
*
|
|
* Copyright (C) 2007-2008 Google, Inc.
|
|
* Copyright (C) 2010 Intel Corporation <tony.luck@intel.com>
|
|
*/
|
|
|
|
#define pr_fmt(fmt) "pstore: " fmt
|
|
|
|
#include <linux/atomic.h>
|
|
#include <linux/types.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/init.h>
|
|
#include <linux/kmsg_dump.h>
|
|
#include <linux/console.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/module.h>
|
|
#include <linux/pstore.h>
|
|
#include <linux/string.h>
|
|
#include <linux/timer.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/jiffies.h>
|
|
#include <linux/vmalloc.h>
|
|
#include <linux/workqueue.h>
|
|
#include <linux/zlib.h>
|
|
|
|
#include "internal.h"
|
|
|
|
/*
|
|
* We defer making "oops" entries appear in pstore - see
|
|
* whether the system is actually still running well enough
|
|
* to let someone see the entry
|
|
*/
|
|
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 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 */
|
|
static const char * const pstore_type_names[] = {
|
|
"dmesg",
|
|
"mce",
|
|
"console",
|
|
"ftrace",
|
|
"rtas",
|
|
"powerpc-ofw",
|
|
"powerpc-common",
|
|
"pmsg",
|
|
"powerpc-opal",
|
|
};
|
|
|
|
static int pstore_new_entry;
|
|
|
|
static void pstore_timefunc(struct timer_list *);
|
|
static DEFINE_TIMER(pstore_timer, pstore_timefunc);
|
|
|
|
static void pstore_dowork(struct work_struct *);
|
|
static DECLARE_WORK(pstore_work, pstore_dowork);
|
|
|
|
/*
|
|
* psinfo_lock protects "psinfo" during calls to
|
|
* pstore_register(), pstore_unregister(), and
|
|
* the filesystem mount/unmount routines.
|
|
*/
|
|
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");
|
|
|
|
/*
|
|
* pstore no longer implements compression via the crypto API, and only
|
|
* supports zlib deflate compression implemented using the zlib library
|
|
* interface. This removes additional complexity which is hard to justify for a
|
|
* diagnostic facility that has to operate in conditions where the system may
|
|
* have become unstable. Zlib deflate is comparatively small in terms of code
|
|
* size, and compresses ASCII text comparatively well. In terms of compression
|
|
* speed, deflate is not the best performer but for recording the log output on
|
|
* a kernel panic, this is not considered critical.
|
|
*
|
|
* The only remaining arguments supported by the compress= module parameter are
|
|
* 'deflate' and 'none'. To retain compatibility with existing installations,
|
|
* all other values are logged and replaced with 'deflate'.
|
|
*/
|
|
static char *compress = "deflate";
|
|
module_param(compress, charp, 0444);
|
|
MODULE_PARM_DESC(compress, "compression to use");
|
|
|
|
/* How much of the kernel log to snapshot */
|
|
unsigned long kmsg_bytes = CONFIG_PSTORE_DEFAULT_KMSG_BYTES;
|
|
module_param(kmsg_bytes, ulong, 0444);
|
|
MODULE_PARM_DESC(kmsg_bytes, "amount of kernel log to snapshot (in bytes)");
|
|
|
|
static void *compress_workspace;
|
|
|
|
/*
|
|
* Compression is only used for dmesg output, which consists of low-entropy
|
|
* ASCII text, and so we can assume worst-case 60%.
|
|
*/
|
|
#define DMESG_COMP_PERCENT 60
|
|
|
|
static char *big_oops_buf;
|
|
static size_t max_compressed_size;
|
|
|
|
void pstore_set_kmsg_bytes(int bytes)
|
|
{
|
|
kmsg_bytes = bytes;
|
|
}
|
|
|
|
/* Tag each group of saved records with a sequence number */
|
|
static int oopscount;
|
|
|
|
const char *pstore_type_to_name(enum pstore_type_id type)
|
|
{
|
|
BUILD_BUG_ON(ARRAY_SIZE(pstore_type_names) != PSTORE_TYPE_MAX);
|
|
|
|
if (WARN_ON_ONCE(type >= PSTORE_TYPE_MAX))
|
|
return "unknown";
|
|
|
|
return pstore_type_names[type];
|
|
}
|
|
EXPORT_SYMBOL_GPL(pstore_type_to_name);
|
|
|
|
enum pstore_type_id pstore_name_to_type(const char *name)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < PSTORE_TYPE_MAX; i++) {
|
|
if (!strcmp(pstore_type_names[i], name))
|
|
return i;
|
|
}
|
|
|
|
return PSTORE_TYPE_MAX;
|
|
}
|
|
EXPORT_SYMBOL_GPL(pstore_name_to_type);
|
|
|
|
static void pstore_timer_kick(void)
|
|
{
|
|
if (pstore_update_ms < 0)
|
|
return;
|
|
|
|
mod_timer(&pstore_timer, jiffies + msecs_to_jiffies(pstore_update_ms));
|
|
}
|
|
|
|
static bool pstore_cannot_block_path(enum kmsg_dump_reason reason)
|
|
{
|
|
/*
|
|
* In case of NMI path, pstore shouldn't be blocked
|
|
* regardless of reason.
|
|
*/
|
|
if (in_nmi())
|
|
return true;
|
|
|
|
switch (reason) {
|
|
/* In panic case, other cpus are stopped by smp_send_stop(). */
|
|
case KMSG_DUMP_PANIC:
|
|
/*
|
|
* Emergency restart shouldn't be blocked by spinning on
|
|
* pstore_info::buf_lock.
|
|
*/
|
|
case KMSG_DUMP_EMERG:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static int pstore_compress(const void *in, void *out,
|
|
unsigned int inlen, unsigned int outlen)
|
|
{
|
|
struct z_stream_s zstream = {
|
|
.next_in = in,
|
|
.avail_in = inlen,
|
|
.next_out = out,
|
|
.avail_out = outlen,
|
|
.workspace = compress_workspace,
|
|
};
|
|
int ret;
|
|
|
|
if (!IS_ENABLED(CONFIG_PSTORE_COMPRESS))
|
|
return -EINVAL;
|
|
|
|
ret = zlib_deflateInit2(&zstream, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
|
|
-MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
|
|
if (ret != Z_OK)
|
|
return -EINVAL;
|
|
|
|
ret = zlib_deflate(&zstream, Z_FINISH);
|
|
if (ret != Z_STREAM_END)
|
|
return -EINVAL;
|
|
|
|
ret = zlib_deflateEnd(&zstream);
|
|
if (ret != Z_OK)
|
|
pr_warn_once("zlib_deflateEnd() failed: %d\n", ret);
|
|
|
|
return zstream.total_out;
|
|
}
|
|
|
|
static void allocate_buf_for_compression(void)
|
|
{
|
|
size_t compressed_size;
|
|
char *buf;
|
|
|
|
/* Skip if not built-in or compression disabled. */
|
|
if (!IS_ENABLED(CONFIG_PSTORE_COMPRESS) || !compress ||
|
|
!strcmp(compress, "none")) {
|
|
compress = NULL;
|
|
return;
|
|
}
|
|
|
|
if (strcmp(compress, "deflate")) {
|
|
pr_err("Unsupported compression '%s', falling back to deflate\n",
|
|
compress);
|
|
compress = "deflate";
|
|
}
|
|
|
|
/*
|
|
* The compression buffer only needs to be as large as the maximum
|
|
* uncompressed record size, since any record that would be expanded by
|
|
* compression is just stored uncompressed.
|
|
*/
|
|
compressed_size = (psinfo->bufsize * 100) / DMESG_COMP_PERCENT;
|
|
buf = kvzalloc(compressed_size, GFP_KERNEL);
|
|
if (!buf) {
|
|
pr_err("Failed %zu byte compression buffer allocation for: %s\n",
|
|
psinfo->bufsize, compress);
|
|
return;
|
|
}
|
|
|
|
compress_workspace =
|
|
vmalloc(zlib_deflate_workspacesize(MAX_WBITS, DEF_MEM_LEVEL));
|
|
if (!compress_workspace) {
|
|
pr_err("Failed to allocate zlib deflate workspace\n");
|
|
kvfree(buf);
|
|
return;
|
|
}
|
|
|
|
/* A non-NULL big_oops_buf indicates compression is available. */
|
|
big_oops_buf = buf;
|
|
max_compressed_size = compressed_size;
|
|
|
|
pr_info("Using crash dump compression: %s\n", compress);
|
|
}
|
|
|
|
static void free_buf_for_compression(void)
|
|
{
|
|
if (IS_ENABLED(CONFIG_PSTORE_COMPRESS) && compress_workspace) {
|
|
vfree(compress_workspace);
|
|
compress_workspace = NULL;
|
|
}
|
|
|
|
kvfree(big_oops_buf);
|
|
big_oops_buf = NULL;
|
|
max_compressed_size = 0;
|
|
}
|
|
|
|
void pstore_record_init(struct pstore_record *record,
|
|
struct pstore_info *psinfo)
|
|
{
|
|
memset(record, 0, sizeof(*record));
|
|
|
|
record->psi = psinfo;
|
|
|
|
/* Report zeroed timestamp if called before timekeeping has resumed. */
|
|
record->time = ns_to_timespec64(ktime_get_real_fast_ns());
|
|
}
|
|
|
|
/*
|
|
* callback from kmsg_dump. Save as much as we can (up to kmsg_bytes) from the
|
|
* end of the buffer.
|
|
*/
|
|
static void pstore_dump(struct kmsg_dumper *dumper,
|
|
struct kmsg_dump_detail *detail)
|
|
{
|
|
struct kmsg_dump_iter iter;
|
|
unsigned long total = 0;
|
|
const char *why;
|
|
unsigned int part = 1;
|
|
unsigned long flags = 0;
|
|
int saved_ret = 0;
|
|
int ret;
|
|
|
|
why = kmsg_dump_reason_str(detail->reason);
|
|
|
|
if (pstore_cannot_block_path(detail->reason)) {
|
|
if (!raw_spin_trylock_irqsave(&psinfo->buf_lock, flags)) {
|
|
pr_err("dump skipped in %s path because of concurrent dump\n",
|
|
in_nmi() ? "NMI" : why);
|
|
return;
|
|
}
|
|
} else {
|
|
raw_spin_lock_irqsave(&psinfo->buf_lock, flags);
|
|
}
|
|
|
|
kmsg_dump_rewind(&iter);
|
|
|
|
oopscount++;
|
|
while (total < kmsg_bytes) {
|
|
char *dst;
|
|
size_t dst_size;
|
|
int header_size;
|
|
int zipped_len = -1;
|
|
size_t dump_size;
|
|
struct pstore_record record;
|
|
|
|
pstore_record_init(&record, psinfo);
|
|
record.type = PSTORE_TYPE_DMESG;
|
|
record.count = oopscount;
|
|
record.reason = detail->reason;
|
|
record.part = part;
|
|
record.buf = psinfo->buf;
|
|
|
|
dst = big_oops_buf ?: psinfo->buf;
|
|
dst_size = max_compressed_size ?: psinfo->bufsize;
|
|
|
|
/* Write dump header. */
|
|
header_size = snprintf(dst, dst_size, "%s#%d Part%u\n", why,
|
|
oopscount, part);
|
|
dst_size -= header_size;
|
|
|
|
/* Write dump contents. */
|
|
if (!kmsg_dump_get_buffer(&iter, true, dst + header_size,
|
|
dst_size, &dump_size))
|
|
break;
|
|
|
|
if (big_oops_buf) {
|
|
zipped_len = pstore_compress(dst, psinfo->buf,
|
|
header_size + dump_size,
|
|
psinfo->bufsize);
|
|
|
|
if (zipped_len > 0) {
|
|
record.compressed = true;
|
|
record.size = zipped_len;
|
|
} else {
|
|
/*
|
|
* Compression failed, so the buffer is most
|
|
* likely filled with binary data that does not
|
|
* compress as well as ASCII text. Copy as much
|
|
* of the uncompressed data as possible into
|
|
* the pstore record, and discard the rest.
|
|
*/
|
|
record.size = psinfo->bufsize;
|
|
memcpy(psinfo->buf, dst, psinfo->bufsize);
|
|
}
|
|
} else {
|
|
record.size = header_size + dump_size;
|
|
}
|
|
|
|
ret = psinfo->write(&record);
|
|
if (ret == 0 && detail->reason == KMSG_DUMP_OOPS) {
|
|
pstore_new_entry = 1;
|
|
pstore_timer_kick();
|
|
} else {
|
|
/* Preserve only the first non-zero returned value. */
|
|
if (!saved_ret)
|
|
saved_ret = ret;
|
|
}
|
|
|
|
total += record.size;
|
|
part++;
|
|
}
|
|
raw_spin_unlock_irqrestore(&psinfo->buf_lock, flags);
|
|
|
|
if (saved_ret) {
|
|
pr_err_once("backend (%s) writing error (%d)\n", psinfo->name,
|
|
saved_ret);
|
|
}
|
|
}
|
|
|
|
static struct kmsg_dumper pstore_dumper = {
|
|
.dump = pstore_dump,
|
|
};
|
|
|
|
/*
|
|
* Register with kmsg_dump to save last part of console log on panic.
|
|
*/
|
|
static void pstore_register_kmsg(void)
|
|
{
|
|
kmsg_dump_register(&pstore_dumper);
|
|
}
|
|
|
|
static void pstore_unregister_kmsg(void)
|
|
{
|
|
kmsg_dump_unregister(&pstore_dumper);
|
|
}
|
|
|
|
#ifdef CONFIG_PSTORE_CONSOLE
|
|
static void pstore_console_write(struct console *con, const char *s, unsigned c)
|
|
{
|
|
struct pstore_record record;
|
|
|
|
if (!c)
|
|
return;
|
|
|
|
pstore_record_init(&record, psinfo);
|
|
record.type = PSTORE_TYPE_CONSOLE;
|
|
|
|
record.buf = (char *)s;
|
|
record.size = c;
|
|
psinfo->write(&record);
|
|
}
|
|
|
|
static struct console pstore_console = {
|
|
.write = pstore_console_write,
|
|
.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);
|
|
}
|
|
|
|
static void pstore_unregister_console(void)
|
|
{
|
|
unregister_console(&pstore_console);
|
|
}
|
|
#else
|
|
static void pstore_register_console(void) {}
|
|
static void pstore_unregister_console(void) {}
|
|
#endif
|
|
|
|
static int pstore_write_user_compat(struct pstore_record *record,
|
|
const char __user *buf)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (record->buf)
|
|
return -EINVAL;
|
|
|
|
record->buf = vmemdup_user(buf, record->size);
|
|
if (IS_ERR(record->buf)) {
|
|
ret = PTR_ERR(record->buf);
|
|
goto out;
|
|
}
|
|
|
|
ret = record->psi->write(record);
|
|
|
|
kvfree(record->buf);
|
|
out:
|
|
record->buf = NULL;
|
|
|
|
return unlikely(ret < 0) ? ret : record->size;
|
|
}
|
|
|
|
/*
|
|
* platform specific persistent storage driver registers with
|
|
* us here. If pstore is already mounted, call the platform
|
|
* read function right away to populate the file system. If not
|
|
* then the pstore mount code will call us later to fill out
|
|
* the file system.
|
|
*/
|
|
int pstore_register(struct pstore_info *psi)
|
|
{
|
|
char *new_backend;
|
|
|
|
if (backend && strcmp(backend, psi->name)) {
|
|
pr_warn("backend '%s' already in use: ignoring '%s'\n",
|
|
backend, psi->name);
|
|
return -EBUSY;
|
|
}
|
|
|
|
/* Sanity check flags. */
|
|
if (!psi->flags) {
|
|
pr_warn("backend '%s' must support at least one frontend\n",
|
|
psi->name);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Check for required functions. */
|
|
if (!psi->read || !psi->write) {
|
|
pr_warn("backend '%s' must implement read() and write()\n",
|
|
psi->name);
|
|
return -EINVAL;
|
|
}
|
|
|
|
new_backend = kstrdup(psi->name, GFP_KERNEL);
|
|
if (!new_backend)
|
|
return -ENOMEM;
|
|
|
|
mutex_lock(&psinfo_lock);
|
|
if (psinfo) {
|
|
pr_warn("backend '%s' already loaded: ignoring '%s'\n",
|
|
psinfo->name, psi->name);
|
|
mutex_unlock(&psinfo_lock);
|
|
kfree(new_backend);
|
|
return -EBUSY;
|
|
}
|
|
|
|
if (!psi->write_user)
|
|
psi->write_user = pstore_write_user_compat;
|
|
psinfo = psi;
|
|
mutex_init(&psinfo->read_mutex);
|
|
raw_spin_lock_init(&psinfo->buf_lock);
|
|
|
|
if (psi->flags & PSTORE_FLAGS_DMESG)
|
|
allocate_buf_for_compression();
|
|
|
|
pstore_get_records(0);
|
|
|
|
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)
|
|
pstore_register_ftrace();
|
|
if (psi->flags & PSTORE_FLAGS_PMSG)
|
|
pstore_register_pmsg();
|
|
|
|
/* Start watching for new records, if desired. */
|
|
pstore_timer_kick();
|
|
|
|
/*
|
|
* Update the module parameter backend, so it is visible
|
|
* through /sys/module/pstore/parameters/backend
|
|
*/
|
|
backend = new_backend;
|
|
|
|
pr_info("Registered %s as persistent store backend\n", psi->name);
|
|
|
|
mutex_unlock(&psinfo_lock);
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(pstore_register);
|
|
|
|
void pstore_unregister(struct pstore_info *psi)
|
|
{
|
|
/* 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)
|
|
pstore_unregister_ftrace();
|
|
if (psi->flags & PSTORE_FLAGS_CONSOLE)
|
|
pstore_unregister_console();
|
|
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;
|
|
|
|
pr_info("Unregistered %s as persistent store backend\n", psi->name);
|
|
mutex_unlock(&psinfo_lock);
|
|
}
|
|
EXPORT_SYMBOL_GPL(pstore_unregister);
|
|
|
|
static void decompress_record(struct pstore_record *record,
|
|
struct z_stream_s *zstream)
|
|
{
|
|
int ret;
|
|
int unzipped_len;
|
|
char *unzipped, *workspace;
|
|
size_t max_uncompressed_size;
|
|
|
|
if (!IS_ENABLED(CONFIG_PSTORE_COMPRESS) || !record->compressed)
|
|
return;
|
|
|
|
/* Only PSTORE_TYPE_DMESG support compression. */
|
|
if (record->type != PSTORE_TYPE_DMESG) {
|
|
pr_warn("ignored compressed record type %d\n", record->type);
|
|
return;
|
|
}
|
|
|
|
/* Missing compression buffer means compression was not initialized. */
|
|
if (!zstream->workspace) {
|
|
pr_warn("no decompression method initialized!\n");
|
|
return;
|
|
}
|
|
|
|
ret = zlib_inflateReset(zstream);
|
|
if (ret != Z_OK) {
|
|
pr_err("zlib_inflateReset() failed, ret = %d!\n", ret);
|
|
return;
|
|
}
|
|
|
|
/* Allocate enough space to hold max decompression and ECC. */
|
|
max_uncompressed_size = 3 * psinfo->bufsize;
|
|
workspace = kvzalloc(max_uncompressed_size + record->ecc_notice_size,
|
|
GFP_KERNEL);
|
|
if (!workspace)
|
|
return;
|
|
|
|
zstream->next_in = record->buf;
|
|
zstream->avail_in = record->size;
|
|
zstream->next_out = workspace;
|
|
zstream->avail_out = max_uncompressed_size;
|
|
|
|
ret = zlib_inflate(zstream, Z_FINISH);
|
|
if (ret != Z_STREAM_END) {
|
|
pr_err_ratelimited("zlib_inflate() failed, ret = %d!\n", ret);
|
|
kvfree(workspace);
|
|
return;
|
|
}
|
|
|
|
unzipped_len = zstream->total_out;
|
|
|
|
/* Append ECC notice to decompressed buffer. */
|
|
memcpy(workspace + unzipped_len, record->buf + record->size,
|
|
record->ecc_notice_size);
|
|
|
|
/* Copy decompressed contents into an minimum-sized allocation. */
|
|
unzipped = kvmemdup(workspace, unzipped_len + record->ecc_notice_size,
|
|
GFP_KERNEL);
|
|
kvfree(workspace);
|
|
if (!unzipped)
|
|
return;
|
|
|
|
/* Swap out compressed contents with decompressed contents. */
|
|
kvfree(record->buf);
|
|
record->buf = unzipped;
|
|
record->size = unzipped_len;
|
|
record->compressed = false;
|
|
}
|
|
|
|
/*
|
|
* Read all the records from one persistent store backend. Create
|
|
* files in our filesystem. Don't warn about -EEXIST errors
|
|
* when we are re-scanning the backing store looking to add new
|
|
* error records.
|
|
*/
|
|
void pstore_get_backend_records(struct pstore_info *psi,
|
|
struct dentry *root, int quiet)
|
|
{
|
|
int failed = 0;
|
|
unsigned int stop_loop = 65536;
|
|
struct z_stream_s zstream = {};
|
|
|
|
if (!psi || !root)
|
|
return;
|
|
|
|
if (IS_ENABLED(CONFIG_PSTORE_COMPRESS) && compress) {
|
|
zstream.workspace = kvmalloc(zlib_inflate_workspacesize(),
|
|
GFP_KERNEL);
|
|
zlib_inflateInit2(&zstream, -DEF_WBITS);
|
|
}
|
|
|
|
mutex_lock(&psi->read_mutex);
|
|
if (psi->open && psi->open(psi))
|
|
goto out;
|
|
|
|
/*
|
|
* Backend callback read() allocates record.buf. decompress_record()
|
|
* may reallocate record.buf. On success, pstore_mkfile() will keep
|
|
* the record.buf, so free it only on failure.
|
|
*/
|
|
for (; stop_loop; stop_loop--) {
|
|
struct pstore_record *record;
|
|
int rc;
|
|
|
|
record = kzalloc(sizeof(*record), GFP_KERNEL);
|
|
if (!record) {
|
|
pr_err("out of memory creating record\n");
|
|
break;
|
|
}
|
|
pstore_record_init(record, psi);
|
|
|
|
record->size = psi->read(record);
|
|
|
|
/* No more records left in backend? */
|
|
if (record->size <= 0) {
|
|
kfree(record);
|
|
break;
|
|
}
|
|
|
|
decompress_record(record, &zstream);
|
|
rc = pstore_mkfile(root, record);
|
|
if (rc) {
|
|
/* pstore_mkfile() did not take record, so free it. */
|
|
kvfree(record->buf);
|
|
kfree(record->priv);
|
|
kfree(record);
|
|
if (rc != -EEXIST || !quiet)
|
|
failed++;
|
|
}
|
|
}
|
|
if (psi->close)
|
|
psi->close(psi);
|
|
out:
|
|
mutex_unlock(&psi->read_mutex);
|
|
|
|
if (IS_ENABLED(CONFIG_PSTORE_COMPRESS) && compress) {
|
|
if (zlib_inflateEnd(&zstream) != Z_OK)
|
|
pr_warn("zlib_inflateEnd() failed\n");
|
|
kvfree(zstream.workspace);
|
|
}
|
|
|
|
if (failed)
|
|
pr_warn("failed to create %d record(s) from '%s'\n",
|
|
failed, psi->name);
|
|
if (!stop_loop)
|
|
pr_err("looping? Too many records seen from '%s'\n",
|
|
psi->name);
|
|
}
|
|
|
|
static void pstore_dowork(struct work_struct *work)
|
|
{
|
|
pstore_get_records(1);
|
|
}
|
|
|
|
static void pstore_timefunc(struct timer_list *unused)
|
|
{
|
|
if (pstore_new_entry) {
|
|
pstore_new_entry = 0;
|
|
schedule_work(&pstore_work);
|
|
}
|
|
|
|
pstore_timer_kick();
|
|
}
|
|
|
|
static int __init pstore_init(void)
|
|
{
|
|
int ret;
|
|
|
|
ret = pstore_init_fs();
|
|
if (ret)
|
|
free_buf_for_compression();
|
|
|
|
return ret;
|
|
}
|
|
late_initcall(pstore_init);
|
|
|
|
static void __exit pstore_exit(void)
|
|
{
|
|
pstore_exit_fs();
|
|
}
|
|
module_exit(pstore_exit)
|
|
|
|
MODULE_AUTHOR("Tony Luck <tony.luck@intel.com>");
|
|
MODULE_DESCRIPTION("Persistent Storage - platform driver interface");
|
|
MODULE_LICENSE("GPL");
|