18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Implements pstore backend driver that write to block (or non-block) storage 48c2ecf20Sopenharmony_ci * devices, using the pstore/zone API. 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/kernel.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include "../../block/blk.h" 128c2ecf20Sopenharmony_ci#include <linux/blkdev.h> 138c2ecf20Sopenharmony_ci#include <linux/string.h> 148c2ecf20Sopenharmony_ci#include <linux/of.h> 158c2ecf20Sopenharmony_ci#include <linux/of_address.h> 168c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 178c2ecf20Sopenharmony_ci#include <linux/pstore_blk.h> 188c2ecf20Sopenharmony_ci#include <linux/mount.h> 198c2ecf20Sopenharmony_ci#include <linux/uio.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistatic long kmsg_size = CONFIG_PSTORE_BLK_KMSG_SIZE; 228c2ecf20Sopenharmony_cimodule_param(kmsg_size, long, 0400); 238c2ecf20Sopenharmony_ciMODULE_PARM_DESC(kmsg_size, "kmsg dump record size in kbytes"); 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic int max_reason = CONFIG_PSTORE_BLK_MAX_REASON; 268c2ecf20Sopenharmony_cimodule_param(max_reason, int, 0400); 278c2ecf20Sopenharmony_ciMODULE_PARM_DESC(max_reason, 288c2ecf20Sopenharmony_ci "maximum reason for kmsg dump (default 2: Oops and Panic)"); 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_PSTORE_PMSG) 318c2ecf20Sopenharmony_cistatic long pmsg_size = CONFIG_PSTORE_BLK_PMSG_SIZE; 328c2ecf20Sopenharmony_ci#else 338c2ecf20Sopenharmony_cistatic long pmsg_size = -1; 348c2ecf20Sopenharmony_ci#endif 358c2ecf20Sopenharmony_cimodule_param(pmsg_size, long, 0400); 368c2ecf20Sopenharmony_ciMODULE_PARM_DESC(pmsg_size, "pmsg size in kbytes"); 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_PSTORE_CONSOLE) 398c2ecf20Sopenharmony_cistatic long console_size = CONFIG_PSTORE_BLK_CONSOLE_SIZE; 408c2ecf20Sopenharmony_ci#else 418c2ecf20Sopenharmony_cistatic long console_size = -1; 428c2ecf20Sopenharmony_ci#endif 438c2ecf20Sopenharmony_cimodule_param(console_size, long, 0400); 448c2ecf20Sopenharmony_ciMODULE_PARM_DESC(console_size, "console size in kbytes"); 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_PSTORE_FTRACE) 478c2ecf20Sopenharmony_cistatic long ftrace_size = CONFIG_PSTORE_BLK_FTRACE_SIZE; 488c2ecf20Sopenharmony_ci#else 498c2ecf20Sopenharmony_cistatic long ftrace_size = -1; 508c2ecf20Sopenharmony_ci#endif 518c2ecf20Sopenharmony_cimodule_param(ftrace_size, long, 0400); 528c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ftrace_size, "ftrace size in kbytes"); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_PSTORE_BLACKBOX) 558c2ecf20Sopenharmony_cistatic long blackbox_size = CONFIG_PSTORE_BLK_BLACKBOX_SIZE; 568c2ecf20Sopenharmony_cibool pstore_ready; 578c2ecf20Sopenharmony_ci#else 588c2ecf20Sopenharmony_cistatic long blackbox_size = -1; 598c2ecf20Sopenharmony_ci#endif 608c2ecf20Sopenharmony_cimodule_param(blackbox_size, long, 0400); 618c2ecf20Sopenharmony_ciMODULE_PARM_DESC(blackbox_size, "blackbox size in kbytes"); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic bool best_effort; 648c2ecf20Sopenharmony_cimodule_param(best_effort, bool, 0400); 658c2ecf20Sopenharmony_ciMODULE_PARM_DESC(best_effort, "use best effort to write (i.e. do not require storage driver pstore support, default: off)"); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci/* 688c2ecf20Sopenharmony_ci * blkdev - the block device to use for pstore storage 698c2ecf20Sopenharmony_ci * 708c2ecf20Sopenharmony_ci * Usually, this will be a partition of a block device. 718c2ecf20Sopenharmony_ci * 728c2ecf20Sopenharmony_ci * blkdev accepts the following variants: 738c2ecf20Sopenharmony_ci * 1) <hex_major><hex_minor> device number in hexadecimal representation, 748c2ecf20Sopenharmony_ci * with no leading 0x, for example b302. 758c2ecf20Sopenharmony_ci * 2) /dev/<disk_name> represents the device number of disk 768c2ecf20Sopenharmony_ci * 3) /dev/<disk_name><decimal> represents the device number 778c2ecf20Sopenharmony_ci * of partition - device number of disk plus the partition number 788c2ecf20Sopenharmony_ci * 4) /dev/<disk_name>p<decimal> - same as the above, that form is 798c2ecf20Sopenharmony_ci * used when disk name of partitioned disk ends on a digit. 808c2ecf20Sopenharmony_ci * 5) PARTUUID=00112233-4455-6677-8899-AABBCCDDEEFF representing the 818c2ecf20Sopenharmony_ci * unique id of a partition if the partition table provides it. 828c2ecf20Sopenharmony_ci * The UUID may be either an EFI/GPT UUID, or refer to an MSDOS 838c2ecf20Sopenharmony_ci * partition using the format SSSSSSSS-PP, where SSSSSSSS is a zero- 848c2ecf20Sopenharmony_ci * filled hex representation of the 32-bit "NT disk signature", and PP 858c2ecf20Sopenharmony_ci * is a zero-filled hex representation of the 1-based partition number. 868c2ecf20Sopenharmony_ci * 6) PARTUUID=<UUID>/PARTNROFF=<int> to select a partition in relation to 878c2ecf20Sopenharmony_ci * a partition with a known unique id. 888c2ecf20Sopenharmony_ci * 7) <major>:<minor> major and minor number of the device separated by 898c2ecf20Sopenharmony_ci * a colon. 908c2ecf20Sopenharmony_ci */ 918c2ecf20Sopenharmony_cistatic char blkdev[80] = CONFIG_PSTORE_BLK_BLKDEV; 928c2ecf20Sopenharmony_cimodule_param_string(blkdev, blkdev, 80, 0400); 938c2ecf20Sopenharmony_ciMODULE_PARM_DESC(blkdev, "block device for pstore storage"); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci/* 968c2ecf20Sopenharmony_ci * All globals must only be accessed under the pstore_blk_lock 978c2ecf20Sopenharmony_ci * during the register/unregister functions. 988c2ecf20Sopenharmony_ci */ 998c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(pstore_blk_lock); 1008c2ecf20Sopenharmony_cistatic struct block_device *psblk_bdev; 1018c2ecf20Sopenharmony_cistatic struct pstore_zone_info *pstore_zone_info; 1028c2ecf20Sopenharmony_cistatic pstore_blk_panic_write_op blkdev_panic_write; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistruct bdev_info { 1058c2ecf20Sopenharmony_ci dev_t devt; 1068c2ecf20Sopenharmony_ci sector_t nr_sects; 1078c2ecf20Sopenharmony_ci sector_t start_sect; 1088c2ecf20Sopenharmony_ci}; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci#define check_size(name, alignsize) ({ \ 1118c2ecf20Sopenharmony_ci long _##name_ = (name); \ 1128c2ecf20Sopenharmony_ci _##name_ = _##name_ <= 0 ? 0 : (_##name_ * 1024); \ 1138c2ecf20Sopenharmony_ci if (_##name_ & ((alignsize) - 1)) { \ 1148c2ecf20Sopenharmony_ci pr_info(#name " must align to %d\n", \ 1158c2ecf20Sopenharmony_ci (alignsize)); \ 1168c2ecf20Sopenharmony_ci _##name_ = ALIGN(name, (alignsize)); \ 1178c2ecf20Sopenharmony_ci } \ 1188c2ecf20Sopenharmony_ci _##name_; \ 1198c2ecf20Sopenharmony_ci}) 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic int __register_pstore_device(struct pstore_device_info *dev) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci int ret; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci lockdep_assert_held(&pstore_blk_lock); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci if (!dev || !dev->total_size || !dev->read || !dev->write) 1288c2ecf20Sopenharmony_ci return -EINVAL; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci /* someone already registered before */ 1318c2ecf20Sopenharmony_ci if (pstore_zone_info) 1328c2ecf20Sopenharmony_ci return -EBUSY; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci pstore_zone_info = kzalloc(sizeof(struct pstore_zone_info), GFP_KERNEL); 1358c2ecf20Sopenharmony_ci if (!pstore_zone_info) 1368c2ecf20Sopenharmony_ci return -ENOMEM; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci /* zero means not limit on which backends to attempt to store. */ 1398c2ecf20Sopenharmony_ci if (!dev->flags) 1408c2ecf20Sopenharmony_ci dev->flags = UINT_MAX; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci#define verify_size(name, alignsize, enabled) { \ 1438c2ecf20Sopenharmony_ci long _##name_; \ 1448c2ecf20Sopenharmony_ci if (enabled) \ 1458c2ecf20Sopenharmony_ci _##name_ = check_size(name, alignsize); \ 1468c2ecf20Sopenharmony_ci else \ 1478c2ecf20Sopenharmony_ci _##name_ = 0; \ 1488c2ecf20Sopenharmony_ci name = _##name_ / 1024; \ 1498c2ecf20Sopenharmony_ci pstore_zone_info->name = _##name_; \ 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci verify_size(kmsg_size, 4096, dev->flags & PSTORE_FLAGS_DMESG); 1538c2ecf20Sopenharmony_ci verify_size(pmsg_size, 4096, dev->flags & PSTORE_FLAGS_PMSG); 1548c2ecf20Sopenharmony_ci verify_size(console_size, 4096, dev->flags & PSTORE_FLAGS_CONSOLE); 1558c2ecf20Sopenharmony_ci verify_size(ftrace_size, 4096, dev->flags & PSTORE_FLAGS_FTRACE); 1568c2ecf20Sopenharmony_ci verify_size(blackbox_size, 4096, dev->flags & PSTORE_FLAGS_BLACKBOX); 1578c2ecf20Sopenharmony_ci#undef verify_size 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci pstore_zone_info->total_size = dev->total_size; 1608c2ecf20Sopenharmony_ci pstore_zone_info->max_reason = max_reason; 1618c2ecf20Sopenharmony_ci pstore_zone_info->read = dev->read; 1628c2ecf20Sopenharmony_ci pstore_zone_info->write = dev->write; 1638c2ecf20Sopenharmony_ci pstore_zone_info->erase = dev->erase; 1648c2ecf20Sopenharmony_ci pstore_zone_info->panic_write = dev->panic_write; 1658c2ecf20Sopenharmony_ci pstore_zone_info->name = KBUILD_MODNAME; 1668c2ecf20Sopenharmony_ci pstore_zone_info->owner = THIS_MODULE; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci ret = register_pstore_zone(pstore_zone_info); 1698c2ecf20Sopenharmony_ci if (ret) { 1708c2ecf20Sopenharmony_ci kfree(pstore_zone_info); 1718c2ecf20Sopenharmony_ci pstore_zone_info = NULL; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci return ret; 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci/** 1768c2ecf20Sopenharmony_ci * register_pstore_device() - register non-block device to pstore/blk 1778c2ecf20Sopenharmony_ci * 1788c2ecf20Sopenharmony_ci * @dev: non-block device information 1798c2ecf20Sopenharmony_ci * 1808c2ecf20Sopenharmony_ci * Return: 1818c2ecf20Sopenharmony_ci * * 0 - OK 1828c2ecf20Sopenharmony_ci * * Others - something error. 1838c2ecf20Sopenharmony_ci */ 1848c2ecf20Sopenharmony_ciint register_pstore_device(struct pstore_device_info *dev) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci int ret; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci mutex_lock(&pstore_blk_lock); 1898c2ecf20Sopenharmony_ci ret = __register_pstore_device(dev); 1908c2ecf20Sopenharmony_ci mutex_unlock(&pstore_blk_lock); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci return ret; 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(register_pstore_device); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic void __unregister_pstore_device(struct pstore_device_info *dev) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci lockdep_assert_held(&pstore_blk_lock); 1998c2ecf20Sopenharmony_ci if (pstore_zone_info && pstore_zone_info->read == dev->read) { 2008c2ecf20Sopenharmony_ci unregister_pstore_zone(pstore_zone_info); 2018c2ecf20Sopenharmony_ci kfree(pstore_zone_info); 2028c2ecf20Sopenharmony_ci pstore_zone_info = NULL; 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci/** 2078c2ecf20Sopenharmony_ci * unregister_pstore_device() - unregister non-block device from pstore/blk 2088c2ecf20Sopenharmony_ci * 2098c2ecf20Sopenharmony_ci * @dev: non-block device information 2108c2ecf20Sopenharmony_ci */ 2118c2ecf20Sopenharmony_civoid unregister_pstore_device(struct pstore_device_info *dev) 2128c2ecf20Sopenharmony_ci{ 2138c2ecf20Sopenharmony_ci mutex_lock(&pstore_blk_lock); 2148c2ecf20Sopenharmony_ci __unregister_pstore_device(dev); 2158c2ecf20Sopenharmony_ci mutex_unlock(&pstore_blk_lock); 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(unregister_pstore_device); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci/** 2208c2ecf20Sopenharmony_ci * psblk_get_bdev() - open block device 2218c2ecf20Sopenharmony_ci * 2228c2ecf20Sopenharmony_ci * @holder: Exclusive holder identifier 2238c2ecf20Sopenharmony_ci * @info: Information about bdev to fill in 2248c2ecf20Sopenharmony_ci * 2258c2ecf20Sopenharmony_ci * Return: pointer to block device on success and others on error. 2268c2ecf20Sopenharmony_ci * 2278c2ecf20Sopenharmony_ci * On success, the returned block_device has reference count of one. 2288c2ecf20Sopenharmony_ci */ 2298c2ecf20Sopenharmony_cistatic struct block_device *psblk_get_bdev(void *holder, 2308c2ecf20Sopenharmony_ci struct bdev_info *info) 2318c2ecf20Sopenharmony_ci{ 2328c2ecf20Sopenharmony_ci struct block_device *bdev = ERR_PTR(-ENODEV); 2338c2ecf20Sopenharmony_ci fmode_t mode = FMODE_READ | FMODE_WRITE; 2348c2ecf20Sopenharmony_ci sector_t nr_sects; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci lockdep_assert_held(&pstore_blk_lock); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci if (pstore_zone_info) 2398c2ecf20Sopenharmony_ci return ERR_PTR(-EBUSY); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci if (!blkdev[0]) 2428c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci if (holder) 2458c2ecf20Sopenharmony_ci mode |= FMODE_EXCL; 2468c2ecf20Sopenharmony_ci bdev = blkdev_get_by_path(blkdev, mode, holder); 2478c2ecf20Sopenharmony_ci if (IS_ERR(bdev)) { 2488c2ecf20Sopenharmony_ci dev_t devt; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci devt = name_to_dev_t(blkdev); 2518c2ecf20Sopenharmony_ci if (devt == 0) 2528c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 2538c2ecf20Sopenharmony_ci bdev = blkdev_get_by_dev(devt, mode, holder); 2548c2ecf20Sopenharmony_ci if (IS_ERR(bdev)) 2558c2ecf20Sopenharmony_ci return bdev; 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci nr_sects = part_nr_sects_read(bdev->bd_part); 2598c2ecf20Sopenharmony_ci if (!nr_sects) { 2608c2ecf20Sopenharmony_ci pr_err("not enough space for '%s'\n", blkdev); 2618c2ecf20Sopenharmony_ci blkdev_put(bdev, mode); 2628c2ecf20Sopenharmony_ci return ERR_PTR(-ENOSPC); 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci if (info) { 2668c2ecf20Sopenharmony_ci info->devt = bdev->bd_dev; 2678c2ecf20Sopenharmony_ci info->nr_sects = nr_sects; 2688c2ecf20Sopenharmony_ci info->start_sect = get_start_sect(bdev); 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci return bdev; 2728c2ecf20Sopenharmony_ci} 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_cistatic void psblk_put_bdev(struct block_device *bdev, void *holder) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci fmode_t mode = FMODE_READ | FMODE_WRITE; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci lockdep_assert_held(&pstore_blk_lock); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci if (!bdev) 2818c2ecf20Sopenharmony_ci return; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci if (holder) 2848c2ecf20Sopenharmony_ci mode |= FMODE_EXCL; 2858c2ecf20Sopenharmony_ci blkdev_put(bdev, mode); 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_cistatic ssize_t psblk_generic_blk_read(char *buf, size_t bytes, loff_t pos) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci struct block_device *bdev = psblk_bdev; 2918c2ecf20Sopenharmony_ci struct file file; 2928c2ecf20Sopenharmony_ci struct kiocb kiocb; 2938c2ecf20Sopenharmony_ci struct iov_iter iter; 2948c2ecf20Sopenharmony_ci struct kvec iov = {.iov_base = buf, .iov_len = bytes}; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci if (!bdev) 2978c2ecf20Sopenharmony_ci return -ENODEV; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci memset(&file, 0, sizeof(struct file)); 3008c2ecf20Sopenharmony_ci file.f_mapping = bdev->bd_inode->i_mapping; 3018c2ecf20Sopenharmony_ci file.f_flags = O_DSYNC | __O_SYNC | O_NOATIME; 3028c2ecf20Sopenharmony_ci file.f_inode = bdev->bd_inode; 3038c2ecf20Sopenharmony_ci file_ra_state_init(&file.f_ra, file.f_mapping); 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci init_sync_kiocb(&kiocb, &file); 3068c2ecf20Sopenharmony_ci kiocb.ki_pos = pos; 3078c2ecf20Sopenharmony_ci iov_iter_kvec(&iter, READ, &iov, 1, bytes); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci return generic_file_read_iter(&kiocb, &iter); 3108c2ecf20Sopenharmony_ci} 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_cistatic ssize_t psblk_generic_blk_write(const char *buf, size_t bytes, 3138c2ecf20Sopenharmony_ci loff_t pos) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci struct block_device *bdev = psblk_bdev; 3168c2ecf20Sopenharmony_ci struct iov_iter iter; 3178c2ecf20Sopenharmony_ci struct kiocb kiocb; 3188c2ecf20Sopenharmony_ci struct file file; 3198c2ecf20Sopenharmony_ci ssize_t ret; 3208c2ecf20Sopenharmony_ci struct kvec iov = {.iov_base = (void *)buf, .iov_len = bytes}; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci if (!bdev) 3238c2ecf20Sopenharmony_ci return -ENODEV; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci /* Console/Ftrace backend may handle buffer until flush dirty zones */ 3268c2ecf20Sopenharmony_ci if (in_interrupt() || irqs_disabled()) 3278c2ecf20Sopenharmony_ci return -EBUSY; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci memset(&file, 0, sizeof(struct file)); 3308c2ecf20Sopenharmony_ci file.f_mapping = bdev->bd_inode->i_mapping; 3318c2ecf20Sopenharmony_ci file.f_flags = O_DSYNC | __O_SYNC | O_NOATIME; 3328c2ecf20Sopenharmony_ci file.f_inode = bdev->bd_inode; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci init_sync_kiocb(&kiocb, &file); 3358c2ecf20Sopenharmony_ci kiocb.ki_pos = pos; 3368c2ecf20Sopenharmony_ci iov_iter_kvec(&iter, WRITE, &iov, 1, bytes); 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci inode_lock(bdev->bd_inode); 3398c2ecf20Sopenharmony_ci ret = generic_write_checks(&kiocb, &iter); 3408c2ecf20Sopenharmony_ci if (ret > 0) 3418c2ecf20Sopenharmony_ci ret = generic_perform_write(&file, &iter, pos); 3428c2ecf20Sopenharmony_ci inode_unlock(bdev->bd_inode); 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci if (likely(ret > 0)) { 3458c2ecf20Sopenharmony_ci const struct file_operations f_op = {.fsync = blkdev_fsync}; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci file.f_op = &f_op; 3488c2ecf20Sopenharmony_ci kiocb.ki_pos += ret; 3498c2ecf20Sopenharmony_ci ret = generic_write_sync(&kiocb, ret); 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci return ret; 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_cistatic ssize_t psblk_blk_panic_write(const char *buf, size_t size, 3558c2ecf20Sopenharmony_ci loff_t off) 3568c2ecf20Sopenharmony_ci{ 3578c2ecf20Sopenharmony_ci int ret; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci if (!blkdev_panic_write) 3608c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci /* size and off must align to SECTOR_SIZE for block device */ 3638c2ecf20Sopenharmony_ci ret = blkdev_panic_write(buf, off >> SECTOR_SHIFT, 3648c2ecf20Sopenharmony_ci size >> SECTOR_SHIFT); 3658c2ecf20Sopenharmony_ci /* try next zone */ 3668c2ecf20Sopenharmony_ci if (ret == -ENOMSG) 3678c2ecf20Sopenharmony_ci return ret; 3688c2ecf20Sopenharmony_ci return ret ? -EIO : size; 3698c2ecf20Sopenharmony_ci} 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_cistatic int __register_pstore_blk(struct pstore_blk_info *info) 3728c2ecf20Sopenharmony_ci{ 3738c2ecf20Sopenharmony_ci char bdev_name[BDEVNAME_SIZE]; 3748c2ecf20Sopenharmony_ci struct block_device *bdev; 3758c2ecf20Sopenharmony_ci struct pstore_device_info dev; 3768c2ecf20Sopenharmony_ci struct bdev_info binfo; 3778c2ecf20Sopenharmony_ci void *holder = blkdev; 3788c2ecf20Sopenharmony_ci int ret = -ENODEV; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci lockdep_assert_held(&pstore_blk_lock); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci /* hold bdev exclusively */ 3838c2ecf20Sopenharmony_ci memset(&binfo, 0, sizeof(binfo)); 3848c2ecf20Sopenharmony_ci bdev = psblk_get_bdev(holder, &binfo); 3858c2ecf20Sopenharmony_ci if (IS_ERR(bdev)) { 3868c2ecf20Sopenharmony_ci pr_err("failed to open '%s'!\n", blkdev); 3878c2ecf20Sopenharmony_ci return PTR_ERR(bdev); 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci /* only allow driver matching the @blkdev */ 3918c2ecf20Sopenharmony_ci if (!binfo.devt || (!best_effort && 3928c2ecf20Sopenharmony_ci MAJOR(binfo.devt) != info->major)) { 3938c2ecf20Sopenharmony_ci pr_debug("invalid major %u (expect %u)\n", 3948c2ecf20Sopenharmony_ci info->major, MAJOR(binfo.devt)); 3958c2ecf20Sopenharmony_ci ret = -ENODEV; 3968c2ecf20Sopenharmony_ci goto err_put_bdev; 3978c2ecf20Sopenharmony_ci } 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci /* psblk_bdev must be assigned before register to pstore/blk */ 4008c2ecf20Sopenharmony_ci psblk_bdev = bdev; 4018c2ecf20Sopenharmony_ci blkdev_panic_write = info->panic_write; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci /* Copy back block device details. */ 4048c2ecf20Sopenharmony_ci info->devt = binfo.devt; 4058c2ecf20Sopenharmony_ci info->nr_sects = binfo.nr_sects; 4068c2ecf20Sopenharmony_ci info->start_sect = binfo.start_sect; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci memset(&dev, 0, sizeof(dev)); 4098c2ecf20Sopenharmony_ci dev.total_size = info->nr_sects << SECTOR_SHIFT; 4108c2ecf20Sopenharmony_ci dev.flags = info->flags; 4118c2ecf20Sopenharmony_ci dev.read = psblk_generic_blk_read; 4128c2ecf20Sopenharmony_ci dev.write = psblk_generic_blk_write; 4138c2ecf20Sopenharmony_ci dev.erase = NULL; 4148c2ecf20Sopenharmony_ci dev.panic_write = info->panic_write ? psblk_blk_panic_write : NULL; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci ret = __register_pstore_device(&dev); 4178c2ecf20Sopenharmony_ci if (ret) 4188c2ecf20Sopenharmony_ci goto err_put_bdev; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci bdevname(bdev, bdev_name); 4218c2ecf20Sopenharmony_ci pr_info("attached %s%s\n", bdev_name, 4228c2ecf20Sopenharmony_ci info->panic_write ? "" : " (no dedicated panic_write!)"); 4238c2ecf20Sopenharmony_ci return 0; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_cierr_put_bdev: 4268c2ecf20Sopenharmony_ci psblk_bdev = NULL; 4278c2ecf20Sopenharmony_ci blkdev_panic_write = NULL; 4288c2ecf20Sopenharmony_ci psblk_put_bdev(bdev, holder); 4298c2ecf20Sopenharmony_ci return ret; 4308c2ecf20Sopenharmony_ci} 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci/** 4338c2ecf20Sopenharmony_ci * register_pstore_blk() - register block device to pstore/blk 4348c2ecf20Sopenharmony_ci * 4358c2ecf20Sopenharmony_ci * @info: details on the desired block device interface 4368c2ecf20Sopenharmony_ci * 4378c2ecf20Sopenharmony_ci * Return: 4388c2ecf20Sopenharmony_ci * * 0 - OK 4398c2ecf20Sopenharmony_ci * * Others - something error. 4408c2ecf20Sopenharmony_ci */ 4418c2ecf20Sopenharmony_ciint register_pstore_blk(struct pstore_blk_info *info) 4428c2ecf20Sopenharmony_ci{ 4438c2ecf20Sopenharmony_ci int ret; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci mutex_lock(&pstore_blk_lock); 4468c2ecf20Sopenharmony_ci ret = __register_pstore_blk(info); 4478c2ecf20Sopenharmony_ci mutex_unlock(&pstore_blk_lock); 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci return ret; 4508c2ecf20Sopenharmony_ci} 4518c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(register_pstore_blk); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_cistatic void __unregister_pstore_blk(unsigned int major) 4548c2ecf20Sopenharmony_ci{ 4558c2ecf20Sopenharmony_ci struct pstore_device_info dev = { .read = psblk_generic_blk_read }; 4568c2ecf20Sopenharmony_ci void *holder = blkdev; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci lockdep_assert_held(&pstore_blk_lock); 4598c2ecf20Sopenharmony_ci if (psblk_bdev && MAJOR(psblk_bdev->bd_dev) == major) { 4608c2ecf20Sopenharmony_ci __unregister_pstore_device(&dev); 4618c2ecf20Sopenharmony_ci psblk_put_bdev(psblk_bdev, holder); 4628c2ecf20Sopenharmony_ci blkdev_panic_write = NULL; 4638c2ecf20Sopenharmony_ci psblk_bdev = NULL; 4648c2ecf20Sopenharmony_ci } 4658c2ecf20Sopenharmony_ci} 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci/** 4688c2ecf20Sopenharmony_ci * unregister_pstore_blk() - unregister block device from pstore/blk 4698c2ecf20Sopenharmony_ci * 4708c2ecf20Sopenharmony_ci * @major: the major device number of device 4718c2ecf20Sopenharmony_ci */ 4728c2ecf20Sopenharmony_civoid unregister_pstore_blk(unsigned int major) 4738c2ecf20Sopenharmony_ci{ 4748c2ecf20Sopenharmony_ci mutex_lock(&pstore_blk_lock); 4758c2ecf20Sopenharmony_ci __unregister_pstore_blk(major); 4768c2ecf20Sopenharmony_ci mutex_unlock(&pstore_blk_lock); 4778c2ecf20Sopenharmony_ci} 4788c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(unregister_pstore_blk); 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci/* get information of pstore/blk */ 4818c2ecf20Sopenharmony_ciint pstore_blk_get_config(struct pstore_blk_config *info) 4828c2ecf20Sopenharmony_ci{ 4838c2ecf20Sopenharmony_ci strncpy(info->device, blkdev, 80); 4848c2ecf20Sopenharmony_ci info->max_reason = max_reason; 4858c2ecf20Sopenharmony_ci info->kmsg_size = check_size(kmsg_size, 4096); 4868c2ecf20Sopenharmony_ci info->pmsg_size = check_size(pmsg_size, 4096); 4878c2ecf20Sopenharmony_ci info->ftrace_size = check_size(ftrace_size, 4096); 4888c2ecf20Sopenharmony_ci info->console_size = check_size(console_size, 4096); 4898c2ecf20Sopenharmony_ci info->blackbox_size = check_size(blackbox_size, 4096); 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci return 0; 4928c2ecf20Sopenharmony_ci} 4938c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pstore_blk_get_config); 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_cistatic int __init pstore_blk_init(void) 4968c2ecf20Sopenharmony_ci{ 4978c2ecf20Sopenharmony_ci struct pstore_blk_info info = { }; 4988c2ecf20Sopenharmony_ci int ret = 0; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci mutex_lock(&pstore_blk_lock); 5018c2ecf20Sopenharmony_ci if (!pstore_zone_info && best_effort && blkdev[0]) 5028c2ecf20Sopenharmony_ci ret = __register_pstore_blk(&info); 5038c2ecf20Sopenharmony_ci mutex_unlock(&pstore_blk_lock); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_PSTORE_BLACKBOX) 5068c2ecf20Sopenharmony_ci if (best_effort && blkdev[0]) 5078c2ecf20Sopenharmony_ci pstore_ready = true; 5088c2ecf20Sopenharmony_ci#endif 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci return ret; 5118c2ecf20Sopenharmony_ci} 5128c2ecf20Sopenharmony_cilate_initcall(pstore_blk_init); 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_cistatic void __exit pstore_blk_exit(void) 5158c2ecf20Sopenharmony_ci{ 5168c2ecf20Sopenharmony_ci mutex_lock(&pstore_blk_lock); 5178c2ecf20Sopenharmony_ci if (psblk_bdev) 5188c2ecf20Sopenharmony_ci __unregister_pstore_blk(MAJOR(psblk_bdev->bd_dev)); 5198c2ecf20Sopenharmony_ci else { 5208c2ecf20Sopenharmony_ci struct pstore_device_info dev = { }; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci if (pstore_zone_info) 5238c2ecf20Sopenharmony_ci dev.read = pstore_zone_info->read; 5248c2ecf20Sopenharmony_ci __unregister_pstore_device(&dev); 5258c2ecf20Sopenharmony_ci } 5268c2ecf20Sopenharmony_ci mutex_unlock(&pstore_blk_lock); 5278c2ecf20Sopenharmony_ci} 5288c2ecf20Sopenharmony_cimodule_exit(pstore_blk_exit); 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 5318c2ecf20Sopenharmony_ciMODULE_AUTHOR("WeiXiong Liao <liaoweixiong@allwinnertech.com>"); 5328c2ecf20Sopenharmony_ciMODULE_AUTHOR("Kees Cook <keescook@chromium.org>"); 5338c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("pstore backend for block devices"); 534