117639f67SWeiXiong Liao // SPDX-License-Identifier: GPL-2.0
217639f67SWeiXiong Liao /*
317639f67SWeiXiong Liao * Implements pstore backend driver that write to block (or non-block) storage
417639f67SWeiXiong Liao * devices, using the pstore/zone API.
517639f67SWeiXiong Liao */
617639f67SWeiXiong Liao
717639f67SWeiXiong Liao #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
817639f67SWeiXiong Liao
917639f67SWeiXiong Liao #include <linux/kernel.h>
1017639f67SWeiXiong Liao #include <linux/module.h>
1117639f67SWeiXiong Liao #include <linux/blkdev.h>
1217639f67SWeiXiong Liao #include <linux/string.h>
1317639f67SWeiXiong Liao #include <linux/of.h>
1417639f67SWeiXiong Liao #include <linux/of_address.h>
1517639f67SWeiXiong Liao #include <linux/platform_device.h>
1617639f67SWeiXiong Liao #include <linux/pstore_blk.h>
177bb9557bSKees Cook #include <linux/fs.h>
187bb9557bSKees Cook #include <linux/file.h>
197bb9557bSKees Cook #include <linux/init_syscalls.h>
2017639f67SWeiXiong Liao #include <linux/mount.h>
2117639f67SWeiXiong Liao
2217639f67SWeiXiong Liao static long kmsg_size = CONFIG_PSTORE_BLK_KMSG_SIZE;
2317639f67SWeiXiong Liao module_param(kmsg_size, long, 0400);
2417639f67SWeiXiong Liao MODULE_PARM_DESC(kmsg_size, "kmsg dump record size in kbytes");
2517639f67SWeiXiong Liao
2617639f67SWeiXiong Liao static int max_reason = CONFIG_PSTORE_BLK_MAX_REASON;
2717639f67SWeiXiong Liao module_param(max_reason, int, 0400);
2817639f67SWeiXiong Liao MODULE_PARM_DESC(max_reason,
2917639f67SWeiXiong Liao "maximum reason for kmsg dump (default 2: Oops and Panic)");
3017639f67SWeiXiong Liao
310dc06826SWeiXiong Liao #if IS_ENABLED(CONFIG_PSTORE_PMSG)
320dc06826SWeiXiong Liao static long pmsg_size = CONFIG_PSTORE_BLK_PMSG_SIZE;
330dc06826SWeiXiong Liao #else
340dc06826SWeiXiong Liao static long pmsg_size = -1;
350dc06826SWeiXiong Liao #endif
360dc06826SWeiXiong Liao module_param(pmsg_size, long, 0400);
370dc06826SWeiXiong Liao MODULE_PARM_DESC(pmsg_size, "pmsg size in kbytes");
380dc06826SWeiXiong Liao
39cc9c4d1bSWeiXiong Liao #if IS_ENABLED(CONFIG_PSTORE_CONSOLE)
40cc9c4d1bSWeiXiong Liao static long console_size = CONFIG_PSTORE_BLK_CONSOLE_SIZE;
41cc9c4d1bSWeiXiong Liao #else
42cc9c4d1bSWeiXiong Liao static long console_size = -1;
43cc9c4d1bSWeiXiong Liao #endif
44cc9c4d1bSWeiXiong Liao module_param(console_size, long, 0400);
45cc9c4d1bSWeiXiong Liao MODULE_PARM_DESC(console_size, "console size in kbytes");
46cc9c4d1bSWeiXiong Liao
4734327e9fSWeiXiong Liao #if IS_ENABLED(CONFIG_PSTORE_FTRACE)
4834327e9fSWeiXiong Liao static long ftrace_size = CONFIG_PSTORE_BLK_FTRACE_SIZE;
4934327e9fSWeiXiong Liao #else
5034327e9fSWeiXiong Liao static long ftrace_size = -1;
5134327e9fSWeiXiong Liao #endif
5234327e9fSWeiXiong Liao module_param(ftrace_size, long, 0400);
5334327e9fSWeiXiong Liao MODULE_PARM_DESC(ftrace_size, "ftrace size in kbytes");
5434327e9fSWeiXiong Liao
55f8feafeaSKees Cook static bool best_effort;
56f8feafeaSKees Cook module_param(best_effort, bool, 0400);
57f8feafeaSKees Cook MODULE_PARM_DESC(best_effort, "use best effort to write (i.e. do not require storage driver pstore support, default: off)");
58f8feafeaSKees Cook
5917639f67SWeiXiong Liao /*
6017639f67SWeiXiong Liao * blkdev - the block device to use for pstore storage
61c811659bSKees Cook * See Documentation/admin-guide/pstore-blk.rst for details.
6217639f67SWeiXiong Liao */
6317639f67SWeiXiong Liao static char blkdev[80] = CONFIG_PSTORE_BLK_BLKDEV;
6417639f67SWeiXiong Liao module_param_string(blkdev, blkdev, 80, 0400);
6517639f67SWeiXiong Liao MODULE_PARM_DESC(blkdev, "block device for pstore storage");
6617639f67SWeiXiong Liao
6717639f67SWeiXiong Liao /*
6817639f67SWeiXiong Liao * All globals must only be accessed under the pstore_blk_lock
6917639f67SWeiXiong Liao * during the register/unregister functions.
7017639f67SWeiXiong Liao */
7117639f67SWeiXiong Liao static DEFINE_MUTEX(pstore_blk_lock);
727bb9557bSKees Cook static struct file *psblk_file;
731d1f6cc5SKees Cook static struct pstore_device_info *pstore_device_info;
7417639f67SWeiXiong Liao
751525fb3bSWeiXiong Liao #define check_size(name, alignsize) ({ \
761525fb3bSWeiXiong Liao long _##name_ = (name); \
771525fb3bSWeiXiong Liao _##name_ = _##name_ <= 0 ? 0 : (_##name_ * 1024); \
781525fb3bSWeiXiong Liao if (_##name_ & ((alignsize) - 1)) { \
791525fb3bSWeiXiong Liao pr_info(#name " must align to %d\n", \
801525fb3bSWeiXiong Liao (alignsize)); \
811525fb3bSWeiXiong Liao _##name_ = ALIGN(name, (alignsize)); \
821525fb3bSWeiXiong Liao } \
831525fb3bSWeiXiong Liao _##name_; \
841525fb3bSWeiXiong Liao })
851525fb3bSWeiXiong Liao
862a03ddbdSKees Cook #define verify_size(name, alignsize, enabled) { \
872a03ddbdSKees Cook long _##name_; \
882a03ddbdSKees Cook if (enabled) \
892a03ddbdSKees Cook _##name_ = check_size(name, alignsize); \
902a03ddbdSKees Cook else \
912a03ddbdSKees Cook _##name_ = 0; \
92*df62fac3SEugen Hristev /* Synchronize module parameters with results. */ \
932a03ddbdSKees Cook name = _##name_ / 1024; \
941d1f6cc5SKees Cook dev->zone.name = _##name_; \
952a03ddbdSKees Cook }
962a03ddbdSKees Cook
__register_pstore_device(struct pstore_device_info * dev)977dcb7848SWeiXiong Liao static int __register_pstore_device(struct pstore_device_info *dev)
9817639f67SWeiXiong Liao {
9917639f67SWeiXiong Liao int ret;
10017639f67SWeiXiong Liao
1017dcb7848SWeiXiong Liao lockdep_assert_held(&pstore_blk_lock);
1027dcb7848SWeiXiong Liao
1036eed261fSKees Cook if (!dev) {
1046eed261fSKees Cook pr_err("NULL device info\n");
10517639f67SWeiXiong Liao return -EINVAL;
1066eed261fSKees Cook }
1071d1f6cc5SKees Cook if (!dev->zone.total_size) {
1086eed261fSKees Cook pr_err("zero sized device\n");
1096eed261fSKees Cook return -EINVAL;
1106eed261fSKees Cook }
1111d1f6cc5SKees Cook if (!dev->zone.read) {
1126eed261fSKees Cook pr_err("no read handler for device\n");
1136eed261fSKees Cook return -EINVAL;
1146eed261fSKees Cook }
1151d1f6cc5SKees Cook if (!dev->zone.write) {
1166eed261fSKees Cook pr_err("no write handler for device\n");
1176eed261fSKees Cook return -EINVAL;
1186eed261fSKees Cook }
11917639f67SWeiXiong Liao
12017639f67SWeiXiong Liao /* someone already registered before */
1211d1f6cc5SKees Cook if (pstore_device_info)
12217639f67SWeiXiong Liao return -EBUSY;
1237dcb7848SWeiXiong Liao
124*df62fac3SEugen Hristev /* zero means no limit on which backends attempt to store. */
12517639f67SWeiXiong Liao if (!dev->flags)
12617639f67SWeiXiong Liao dev->flags = UINT_MAX;
12717639f67SWeiXiong Liao
1281d1f6cc5SKees Cook /* Copy in module parameters. */
12917639f67SWeiXiong Liao verify_size(kmsg_size, 4096, dev->flags & PSTORE_FLAGS_DMESG);
1300dc06826SWeiXiong Liao verify_size(pmsg_size, 4096, dev->flags & PSTORE_FLAGS_PMSG);
131cc9c4d1bSWeiXiong Liao verify_size(console_size, 4096, dev->flags & PSTORE_FLAGS_CONSOLE);
13234327e9fSWeiXiong Liao verify_size(ftrace_size, 4096, dev->flags & PSTORE_FLAGS_FTRACE);
1331d1f6cc5SKees Cook dev->zone.max_reason = max_reason;
13417639f67SWeiXiong Liao
1351d1f6cc5SKees Cook /* Initialize required zone ownership details. */
1361d1f6cc5SKees Cook dev->zone.name = KBUILD_MODNAME;
1371d1f6cc5SKees Cook dev->zone.owner = THIS_MODULE;
13817639f67SWeiXiong Liao
1391d1f6cc5SKees Cook ret = register_pstore_zone(&dev->zone);
1401d1f6cc5SKees Cook if (ret == 0)
1411d1f6cc5SKees Cook pstore_device_info = dev;
1421d1f6cc5SKees Cook
14317639f67SWeiXiong Liao return ret;
14417639f67SWeiXiong Liao }
1457dcb7848SWeiXiong Liao /**
1467dcb7848SWeiXiong Liao * register_pstore_device() - register non-block device to pstore/blk
1477dcb7848SWeiXiong Liao *
1487dcb7848SWeiXiong Liao * @dev: non-block device information
1497dcb7848SWeiXiong Liao *
1507dcb7848SWeiXiong Liao * Return:
1517dcb7848SWeiXiong Liao * * 0 - OK
1527dcb7848SWeiXiong Liao * * Others - something error.
1537dcb7848SWeiXiong Liao */
register_pstore_device(struct pstore_device_info * dev)1547dcb7848SWeiXiong Liao int register_pstore_device(struct pstore_device_info *dev)
15517639f67SWeiXiong Liao {
1567dcb7848SWeiXiong Liao int ret;
1577dcb7848SWeiXiong Liao
15817639f67SWeiXiong Liao mutex_lock(&pstore_blk_lock);
1597dcb7848SWeiXiong Liao ret = __register_pstore_device(dev);
1607dcb7848SWeiXiong Liao mutex_unlock(&pstore_blk_lock);
1617dcb7848SWeiXiong Liao
1627dcb7848SWeiXiong Liao return ret;
1637dcb7848SWeiXiong Liao }
1647dcb7848SWeiXiong Liao EXPORT_SYMBOL_GPL(register_pstore_device);
1657dcb7848SWeiXiong Liao
__unregister_pstore_device(struct pstore_device_info * dev)1667dcb7848SWeiXiong Liao static void __unregister_pstore_device(struct pstore_device_info *dev)
1677dcb7848SWeiXiong Liao {
1687dcb7848SWeiXiong Liao lockdep_assert_held(&pstore_blk_lock);
1691d1f6cc5SKees Cook if (pstore_device_info && pstore_device_info == dev) {
1701d1f6cc5SKees Cook unregister_pstore_zone(&dev->zone);
1711d1f6cc5SKees Cook pstore_device_info = NULL;
17217639f67SWeiXiong Liao }
1737dcb7848SWeiXiong Liao }
1747dcb7848SWeiXiong Liao
1757dcb7848SWeiXiong Liao /**
1767dcb7848SWeiXiong Liao * unregister_pstore_device() - unregister non-block device from pstore/blk
1777dcb7848SWeiXiong Liao *
1787dcb7848SWeiXiong Liao * @dev: non-block device information
1797dcb7848SWeiXiong Liao */
unregister_pstore_device(struct pstore_device_info * dev)1807dcb7848SWeiXiong Liao void unregister_pstore_device(struct pstore_device_info *dev)
1817dcb7848SWeiXiong Liao {
1827dcb7848SWeiXiong Liao mutex_lock(&pstore_blk_lock);
1837dcb7848SWeiXiong Liao __unregister_pstore_device(dev);
18417639f67SWeiXiong Liao mutex_unlock(&pstore_blk_lock);
18517639f67SWeiXiong Liao }
1867dcb7848SWeiXiong Liao EXPORT_SYMBOL_GPL(unregister_pstore_device);
18717639f67SWeiXiong Liao
psblk_generic_blk_read(char * buf,size_t bytes,loff_t pos)18817639f67SWeiXiong Liao static ssize_t psblk_generic_blk_read(char *buf, size_t bytes, loff_t pos)
18917639f67SWeiXiong Liao {
1907bb9557bSKees Cook return kernel_read(psblk_file, buf, bytes, &pos);
19117639f67SWeiXiong Liao }
19217639f67SWeiXiong Liao
psblk_generic_blk_write(const char * buf,size_t bytes,loff_t pos)19317639f67SWeiXiong Liao static ssize_t psblk_generic_blk_write(const char *buf, size_t bytes,
19417639f67SWeiXiong Liao loff_t pos)
19517639f67SWeiXiong Liao {
19617639f67SWeiXiong Liao /* Console/Ftrace backend may handle buffer until flush dirty zones */
19717639f67SWeiXiong Liao if (in_interrupt() || irqs_disabled())
19817639f67SWeiXiong Liao return -EBUSY;
1997bb9557bSKees Cook return kernel_write(psblk_file, buf, bytes, &pos);
20017639f67SWeiXiong Liao }
20117639f67SWeiXiong Liao
202b6f8ed33SChristoph Hellwig /*
203b6f8ed33SChristoph Hellwig * This takes its configuration only from the module parameters now.
204b6f8ed33SChristoph Hellwig */
__register_pstore_blk(struct pstore_device_info * dev,const char * devpath)2051d1f6cc5SKees Cook static int __register_pstore_blk(struct pstore_device_info *dev,
2061d1f6cc5SKees Cook const char *devpath)
20717639f67SWeiXiong Liao {
20817639f67SWeiXiong Liao int ret = -ENODEV;
20917639f67SWeiXiong Liao
21017639f67SWeiXiong Liao lockdep_assert_held(&pstore_blk_lock);
21117639f67SWeiXiong Liao
2127bb9557bSKees Cook psblk_file = filp_open(devpath, O_RDWR | O_DSYNC | O_NOATIME | O_EXCL, 0);
2137bb9557bSKees Cook if (IS_ERR(psblk_file)) {
2147bb9557bSKees Cook ret = PTR_ERR(psblk_file);
2157bb9557bSKees Cook pr_err("failed to open '%s': %d!\n", devpath, ret);
2167bb9557bSKees Cook goto err;
21717639f67SWeiXiong Liao }
21817639f67SWeiXiong Liao
21946461985SChristoph Hellwig if (!S_ISBLK(file_inode(psblk_file)->i_mode)) {
2207bb9557bSKees Cook pr_err("'%s' is not block device!\n", devpath);
2217bb9557bSKees Cook goto err_fput;
22217639f67SWeiXiong Liao }
22317639f67SWeiXiong Liao
22446461985SChristoph Hellwig dev->zone.total_size =
22546461985SChristoph Hellwig bdev_nr_bytes(I_BDEV(psblk_file->f_mapping->host));
22617639f67SWeiXiong Liao
2271d1f6cc5SKees Cook ret = __register_pstore_device(dev);
22817639f67SWeiXiong Liao if (ret)
2297bb9557bSKees Cook goto err_fput;
23017639f67SWeiXiong Liao
23117639f67SWeiXiong Liao return 0;
23217639f67SWeiXiong Liao
2337bb9557bSKees Cook err_fput:
2347bb9557bSKees Cook fput(psblk_file);
2357bb9557bSKees Cook err:
2367bb9557bSKees Cook psblk_file = NULL;
2377bb9557bSKees Cook
23817639f67SWeiXiong Liao return ret;
23917639f67SWeiXiong Liao }
24017639f67SWeiXiong Liao
2411525fb3bSWeiXiong Liao /* get information of pstore/blk */
pstore_blk_get_config(struct pstore_blk_config * info)2421525fb3bSWeiXiong Liao int pstore_blk_get_config(struct pstore_blk_config *info)
2431525fb3bSWeiXiong Liao {
2441525fb3bSWeiXiong Liao strncpy(info->device, blkdev, 80);
2451525fb3bSWeiXiong Liao info->max_reason = max_reason;
2461525fb3bSWeiXiong Liao info->kmsg_size = check_size(kmsg_size, 4096);
2471525fb3bSWeiXiong Liao info->pmsg_size = check_size(pmsg_size, 4096);
2481525fb3bSWeiXiong Liao info->ftrace_size = check_size(ftrace_size, 4096);
2491525fb3bSWeiXiong Liao info->console_size = check_size(console_size, 4096);
2501525fb3bSWeiXiong Liao
2511525fb3bSWeiXiong Liao return 0;
2521525fb3bSWeiXiong Liao }
2531525fb3bSWeiXiong Liao EXPORT_SYMBOL_GPL(pstore_blk_get_config);
2541525fb3bSWeiXiong Liao
2557bb9557bSKees Cook
2567bb9557bSKees Cook #ifndef MODULE
2577bb9557bSKees Cook static const char devname[] = "/dev/pstore-blk";
early_boot_devpath(const char * initial_devname)2587bb9557bSKees Cook static __init const char *early_boot_devpath(const char *initial_devname)
2597bb9557bSKees Cook {
2607bb9557bSKees Cook /*
2617bb9557bSKees Cook * During early boot the real root file system hasn't been
2627bb9557bSKees Cook * mounted yet, and no device nodes are present yet. Use the
2637bb9557bSKees Cook * same scheme to find the device that we use for mounting
2647bb9557bSKees Cook * the root file system.
2657bb9557bSKees Cook */
266cf056a43SChristoph Hellwig dev_t dev;
2677bb9557bSKees Cook
268cf056a43SChristoph Hellwig if (early_lookup_bdev(initial_devname, &dev)) {
2697bb9557bSKees Cook pr_err("failed to resolve '%s'!\n", initial_devname);
2707bb9557bSKees Cook return initial_devname;
2717bb9557bSKees Cook }
2727bb9557bSKees Cook
2737bb9557bSKees Cook init_unlink(devname);
2747bb9557bSKees Cook init_mknod(devname, S_IFBLK | 0600, new_encode_dev(dev));
2757bb9557bSKees Cook
2767bb9557bSKees Cook return devname;
2777bb9557bSKees Cook }
2787bb9557bSKees Cook #else
early_boot_devpath(const char * initial_devname)2797bb9557bSKees Cook static inline const char *early_boot_devpath(const char *initial_devname)
2807bb9557bSKees Cook {
2817bb9557bSKees Cook return initial_devname;
2827bb9557bSKees Cook }
2837bb9557bSKees Cook #endif
2847bb9557bSKees Cook
__best_effort_init(void)2851d1f6cc5SKees Cook static int __init __best_effort_init(void)
2861d1f6cc5SKees Cook {
2871d1f6cc5SKees Cook struct pstore_device_info *best_effort_dev;
2881d1f6cc5SKees Cook int ret;
2891d1f6cc5SKees Cook
2901d1f6cc5SKees Cook /* No best-effort mode requested. */
2911d1f6cc5SKees Cook if (!best_effort)
2921d1f6cc5SKees Cook return 0;
2931d1f6cc5SKees Cook
2941d1f6cc5SKees Cook /* Reject an empty blkdev. */
2951d1f6cc5SKees Cook if (!blkdev[0]) {
2961d1f6cc5SKees Cook pr_err("blkdev empty with best_effort=Y\n");
2971d1f6cc5SKees Cook return -EINVAL;
2981d1f6cc5SKees Cook }
2991d1f6cc5SKees Cook
3001d1f6cc5SKees Cook best_effort_dev = kzalloc(sizeof(*best_effort_dev), GFP_KERNEL);
3011d1f6cc5SKees Cook if (!best_effort_dev)
3021d1f6cc5SKees Cook return -ENOMEM;
3031d1f6cc5SKees Cook
3041d1f6cc5SKees Cook best_effort_dev->zone.read = psblk_generic_blk_read;
3051d1f6cc5SKees Cook best_effort_dev->zone.write = psblk_generic_blk_write;
3061d1f6cc5SKees Cook
3071d1f6cc5SKees Cook ret = __register_pstore_blk(best_effort_dev,
3081d1f6cc5SKees Cook early_boot_devpath(blkdev));
3091d1f6cc5SKees Cook if (ret)
3101d1f6cc5SKees Cook kfree(best_effort_dev);
3111d1f6cc5SKees Cook else
31261eb495cSGeert Uytterhoeven pr_info("attached %s (%lu) (no dedicated panic_write!)\n",
3131d1f6cc5SKees Cook blkdev, best_effort_dev->zone.total_size);
3141d1f6cc5SKees Cook
3151d1f6cc5SKees Cook return ret;
3161d1f6cc5SKees Cook }
3171d1f6cc5SKees Cook
__best_effort_exit(void)3181d1f6cc5SKees Cook static void __exit __best_effort_exit(void)
3191d1f6cc5SKees Cook {
3201d1f6cc5SKees Cook /*
3211d1f6cc5SKees Cook * Currently, the only user of psblk_file is best_effort, so
3221d1f6cc5SKees Cook * we can assume that pstore_device_info is associated with it.
3231d1f6cc5SKees Cook * Once there are "real" blk devices, there will need to be a
3241d1f6cc5SKees Cook * dedicated pstore_blk_info, etc.
3251d1f6cc5SKees Cook */
3261d1f6cc5SKees Cook if (psblk_file) {
3271d1f6cc5SKees Cook struct pstore_device_info *dev = pstore_device_info;
3281d1f6cc5SKees Cook
3291d1f6cc5SKees Cook __unregister_pstore_device(dev);
3301d1f6cc5SKees Cook kfree(dev);
3311d1f6cc5SKees Cook fput(psblk_file);
3321d1f6cc5SKees Cook psblk_file = NULL;
3331d1f6cc5SKees Cook }
3341d1f6cc5SKees Cook }
3351d1f6cc5SKees Cook
pstore_blk_init(void)336f8feafeaSKees Cook static int __init pstore_blk_init(void)
337f8feafeaSKees Cook {
3381d1f6cc5SKees Cook int ret;
339f8feafeaSKees Cook
340f8feafeaSKees Cook mutex_lock(&pstore_blk_lock);
3411d1f6cc5SKees Cook ret = __best_effort_init();
342f8feafeaSKees Cook mutex_unlock(&pstore_blk_lock);
343f8feafeaSKees Cook
344f8feafeaSKees Cook return ret;
345f8feafeaSKees Cook }
346f8feafeaSKees Cook late_initcall(pstore_blk_init);
347f8feafeaSKees Cook
pstore_blk_exit(void)34817639f67SWeiXiong Liao static void __exit pstore_blk_exit(void)
34917639f67SWeiXiong Liao {
35017639f67SWeiXiong Liao mutex_lock(&pstore_blk_lock);
3511d1f6cc5SKees Cook __best_effort_exit();
3521d1f6cc5SKees Cook /* If we've been asked to unload, unregister any remaining device. */
3531d1f6cc5SKees Cook __unregister_pstore_device(pstore_device_info);
35417639f67SWeiXiong Liao mutex_unlock(&pstore_blk_lock);
35517639f67SWeiXiong Liao }
35617639f67SWeiXiong Liao module_exit(pstore_blk_exit);
35717639f67SWeiXiong Liao
35817639f67SWeiXiong Liao MODULE_LICENSE("GPL");
35917639f67SWeiXiong Liao MODULE_AUTHOR("WeiXiong Liao <liaoweixiong@allwinnertech.com>");
36017639f67SWeiXiong Liao MODULE_AUTHOR("Kees Cook <keescook@chromium.org>");
36117639f67SWeiXiong Liao MODULE_DESCRIPTION("pstore backend for block devices");
362