1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Implements pstore backend driver that write to block (or non-block) storage |
4 | * devices, using the pstore/zone API. |
5 | */ |
6 | |
7 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
8 | |
9 | #include <linux/kernel.h> |
10 | #include <linux/module.h> |
11 | #include <linux/blkdev.h> |
12 | #include <linux/string.h> |
13 | #include <linux/of.h> |
14 | #include <linux/of_address.h> |
15 | #include <linux/platform_device.h> |
16 | #include <linux/pstore_blk.h> |
17 | #include <linux/fs.h> |
18 | #include <linux/file.h> |
19 | #include <linux/init_syscalls.h> |
20 | #include <linux/mount.h> |
21 | |
22 | static long kmsg_size = CONFIG_PSTORE_BLK_KMSG_SIZE; |
23 | module_param(kmsg_size, long, 0400); |
24 | MODULE_PARM_DESC(kmsg_size, "kmsg dump record size in kbytes" ); |
25 | |
26 | static int max_reason = CONFIG_PSTORE_BLK_MAX_REASON; |
27 | module_param(max_reason, int, 0400); |
28 | MODULE_PARM_DESC(max_reason, |
29 | "maximum reason for kmsg dump (default 2: Oops and Panic)" ); |
30 | |
31 | #if IS_ENABLED(CONFIG_PSTORE_PMSG) |
32 | static long pmsg_size = CONFIG_PSTORE_BLK_PMSG_SIZE; |
33 | #else |
34 | static long pmsg_size = -1; |
35 | #endif |
36 | module_param(pmsg_size, long, 0400); |
37 | MODULE_PARM_DESC(pmsg_size, "pmsg size in kbytes" ); |
38 | |
39 | #if IS_ENABLED(CONFIG_PSTORE_CONSOLE) |
40 | static long console_size = CONFIG_PSTORE_BLK_CONSOLE_SIZE; |
41 | #else |
42 | static long console_size = -1; |
43 | #endif |
44 | module_param(console_size, long, 0400); |
45 | MODULE_PARM_DESC(console_size, "console size in kbytes" ); |
46 | |
47 | #if IS_ENABLED(CONFIG_PSTORE_FTRACE) |
48 | static long ftrace_size = CONFIG_PSTORE_BLK_FTRACE_SIZE; |
49 | #else |
50 | static long ftrace_size = -1; |
51 | #endif |
52 | module_param(ftrace_size, long, 0400); |
53 | MODULE_PARM_DESC(ftrace_size, "ftrace size in kbytes" ); |
54 | |
55 | static bool best_effort; |
56 | module_param(best_effort, bool, 0400); |
57 | MODULE_PARM_DESC(best_effort, "use best effort to write (i.e. do not require storage driver pstore support, default: off)" ); |
58 | |
59 | /* |
60 | * blkdev - the block device to use for pstore storage |
61 | * See Documentation/admin-guide/pstore-blk.rst for details. |
62 | */ |
63 | static char blkdev[80] = CONFIG_PSTORE_BLK_BLKDEV; |
64 | module_param_string(blkdev, blkdev, 80, 0400); |
65 | MODULE_PARM_DESC(blkdev, "block device for pstore storage" ); |
66 | |
67 | /* |
68 | * All globals must only be accessed under the pstore_blk_lock |
69 | * during the register/unregister functions. |
70 | */ |
71 | static DEFINE_MUTEX(pstore_blk_lock); |
72 | static struct file *psblk_file; |
73 | static struct pstore_device_info *pstore_device_info; |
74 | |
75 | #define check_size(name, alignsize) ({ \ |
76 | long _##name_ = (name); \ |
77 | _##name_ = _##name_ <= 0 ? 0 : (_##name_ * 1024); \ |
78 | if (_##name_ & ((alignsize) - 1)) { \ |
79 | pr_info(#name " must align to %d\n", \ |
80 | (alignsize)); \ |
81 | _##name_ = ALIGN(name, (alignsize)); \ |
82 | } \ |
83 | _##name_; \ |
84 | }) |
85 | |
86 | #define verify_size(name, alignsize, enabled) { \ |
87 | long _##name_; \ |
88 | if (enabled) \ |
89 | _##name_ = check_size(name, alignsize); \ |
90 | else \ |
91 | _##name_ = 0; \ |
92 | /* Synchronize module parameters with resuls. */ \ |
93 | name = _##name_ / 1024; \ |
94 | dev->zone.name = _##name_; \ |
95 | } |
96 | |
97 | static int __register_pstore_device(struct pstore_device_info *dev) |
98 | { |
99 | int ret; |
100 | |
101 | lockdep_assert_held(&pstore_blk_lock); |
102 | |
103 | if (!dev) { |
104 | pr_err("NULL device info\n" ); |
105 | return -EINVAL; |
106 | } |
107 | if (!dev->zone.total_size) { |
108 | pr_err("zero sized device\n" ); |
109 | return -EINVAL; |
110 | } |
111 | if (!dev->zone.read) { |
112 | pr_err("no read handler for device\n" ); |
113 | return -EINVAL; |
114 | } |
115 | if (!dev->zone.write) { |
116 | pr_err("no write handler for device\n" ); |
117 | return -EINVAL; |
118 | } |
119 | |
120 | /* someone already registered before */ |
121 | if (pstore_device_info) |
122 | return -EBUSY; |
123 | |
124 | /* zero means not limit on which backends to attempt to store. */ |
125 | if (!dev->flags) |
126 | dev->flags = UINT_MAX; |
127 | |
128 | /* Copy in module parameters. */ |
129 | verify_size(kmsg_size, 4096, dev->flags & PSTORE_FLAGS_DMESG); |
130 | verify_size(pmsg_size, 4096, dev->flags & PSTORE_FLAGS_PMSG); |
131 | verify_size(console_size, 4096, dev->flags & PSTORE_FLAGS_CONSOLE); |
132 | verify_size(ftrace_size, 4096, dev->flags & PSTORE_FLAGS_FTRACE); |
133 | dev->zone.max_reason = max_reason; |
134 | |
135 | /* Initialize required zone ownership details. */ |
136 | dev->zone.name = KBUILD_MODNAME; |
137 | dev->zone.owner = THIS_MODULE; |
138 | |
139 | ret = register_pstore_zone(info: &dev->zone); |
140 | if (ret == 0) |
141 | pstore_device_info = dev; |
142 | |
143 | return ret; |
144 | } |
145 | /** |
146 | * register_pstore_device() - register non-block device to pstore/blk |
147 | * |
148 | * @dev: non-block device information |
149 | * |
150 | * Return: |
151 | * * 0 - OK |
152 | * * Others - something error. |
153 | */ |
154 | int register_pstore_device(struct pstore_device_info *dev) |
155 | { |
156 | int ret; |
157 | |
158 | mutex_lock(&pstore_blk_lock); |
159 | ret = __register_pstore_device(dev); |
160 | mutex_unlock(lock: &pstore_blk_lock); |
161 | |
162 | return ret; |
163 | } |
164 | EXPORT_SYMBOL_GPL(register_pstore_device); |
165 | |
166 | static void __unregister_pstore_device(struct pstore_device_info *dev) |
167 | { |
168 | lockdep_assert_held(&pstore_blk_lock); |
169 | if (pstore_device_info && pstore_device_info == dev) { |
170 | unregister_pstore_zone(info: &dev->zone); |
171 | pstore_device_info = NULL; |
172 | } |
173 | } |
174 | |
175 | /** |
176 | * unregister_pstore_device() - unregister non-block device from pstore/blk |
177 | * |
178 | * @dev: non-block device information |
179 | */ |
180 | void unregister_pstore_device(struct pstore_device_info *dev) |
181 | { |
182 | mutex_lock(&pstore_blk_lock); |
183 | __unregister_pstore_device(dev); |
184 | mutex_unlock(lock: &pstore_blk_lock); |
185 | } |
186 | EXPORT_SYMBOL_GPL(unregister_pstore_device); |
187 | |
188 | static ssize_t psblk_generic_blk_read(char *buf, size_t bytes, loff_t pos) |
189 | { |
190 | return kernel_read(psblk_file, buf, bytes, &pos); |
191 | } |
192 | |
193 | static ssize_t psblk_generic_blk_write(const char *buf, size_t bytes, |
194 | loff_t pos) |
195 | { |
196 | /* Console/Ftrace backend may handle buffer until flush dirty zones */ |
197 | if (in_interrupt() || irqs_disabled()) |
198 | return -EBUSY; |
199 | return kernel_write(psblk_file, buf, bytes, &pos); |
200 | } |
201 | |
202 | /* |
203 | * This takes its configuration only from the module parameters now. |
204 | */ |
205 | static int __register_pstore_blk(struct pstore_device_info *dev, |
206 | const char *devpath) |
207 | { |
208 | int ret = -ENODEV; |
209 | |
210 | lockdep_assert_held(&pstore_blk_lock); |
211 | |
212 | psblk_file = filp_open(devpath, O_RDWR | O_DSYNC | O_NOATIME | O_EXCL, 0); |
213 | if (IS_ERR(ptr: psblk_file)) { |
214 | ret = PTR_ERR(ptr: psblk_file); |
215 | pr_err("failed to open '%s': %d!\n" , devpath, ret); |
216 | goto err; |
217 | } |
218 | |
219 | if (!S_ISBLK(file_inode(psblk_file)->i_mode)) { |
220 | pr_err("'%s' is not block device!\n" , devpath); |
221 | goto err_fput; |
222 | } |
223 | |
224 | dev->zone.total_size = |
225 | bdev_nr_bytes(bdev: I_BDEV(inode: psblk_file->f_mapping->host)); |
226 | |
227 | ret = __register_pstore_device(dev); |
228 | if (ret) |
229 | goto err_fput; |
230 | |
231 | return 0; |
232 | |
233 | err_fput: |
234 | fput(psblk_file); |
235 | err: |
236 | psblk_file = NULL; |
237 | |
238 | return ret; |
239 | } |
240 | |
241 | /* get information of pstore/blk */ |
242 | int pstore_blk_get_config(struct pstore_blk_config *info) |
243 | { |
244 | strncpy(p: info->device, q: blkdev, size: 80); |
245 | info->max_reason = max_reason; |
246 | info->kmsg_size = check_size(kmsg_size, 4096); |
247 | info->pmsg_size = check_size(pmsg_size, 4096); |
248 | info->ftrace_size = check_size(ftrace_size, 4096); |
249 | info->console_size = check_size(console_size, 4096); |
250 | |
251 | return 0; |
252 | } |
253 | EXPORT_SYMBOL_GPL(pstore_blk_get_config); |
254 | |
255 | |
256 | #ifndef MODULE |
257 | static const char devname[] = "/dev/pstore-blk" ; |
258 | static __init const char *early_boot_devpath(const char *initial_devname) |
259 | { |
260 | /* |
261 | * During early boot the real root file system hasn't been |
262 | * mounted yet, and no device nodes are present yet. Use the |
263 | * same scheme to find the device that we use for mounting |
264 | * the root file system. |
265 | */ |
266 | dev_t dev; |
267 | |
268 | if (early_lookup_bdev(pathname: initial_devname, dev: &dev)) { |
269 | pr_err("failed to resolve '%s'!\n" , initial_devname); |
270 | return initial_devname; |
271 | } |
272 | |
273 | init_unlink(pathname: devname); |
274 | init_mknod(filename: devname, S_IFBLK | 0600, dev: new_encode_dev(dev)); |
275 | |
276 | return devname; |
277 | } |
278 | #else |
279 | static inline const char *early_boot_devpath(const char *initial_devname) |
280 | { |
281 | return initial_devname; |
282 | } |
283 | #endif |
284 | |
285 | static int __init __best_effort_init(void) |
286 | { |
287 | struct pstore_device_info *best_effort_dev; |
288 | int ret; |
289 | |
290 | /* No best-effort mode requested. */ |
291 | if (!best_effort) |
292 | return 0; |
293 | |
294 | /* Reject an empty blkdev. */ |
295 | if (!blkdev[0]) { |
296 | pr_err("blkdev empty with best_effort=Y\n" ); |
297 | return -EINVAL; |
298 | } |
299 | |
300 | best_effort_dev = kzalloc(size: sizeof(*best_effort_dev), GFP_KERNEL); |
301 | if (!best_effort_dev) |
302 | return -ENOMEM; |
303 | |
304 | best_effort_dev->zone.read = psblk_generic_blk_read; |
305 | best_effort_dev->zone.write = psblk_generic_blk_write; |
306 | |
307 | ret = __register_pstore_blk(dev: best_effort_dev, |
308 | devpath: early_boot_devpath(initial_devname: blkdev)); |
309 | if (ret) |
310 | kfree(objp: best_effort_dev); |
311 | else |
312 | pr_info("attached %s (%lu) (no dedicated panic_write!)\n" , |
313 | blkdev, best_effort_dev->zone.total_size); |
314 | |
315 | return ret; |
316 | } |
317 | |
318 | static void __exit __best_effort_exit(void) |
319 | { |
320 | /* |
321 | * Currently, the only user of psblk_file is best_effort, so |
322 | * we can assume that pstore_device_info is associated with it. |
323 | * Once there are "real" blk devices, there will need to be a |
324 | * dedicated pstore_blk_info, etc. |
325 | */ |
326 | if (psblk_file) { |
327 | struct pstore_device_info *dev = pstore_device_info; |
328 | |
329 | __unregister_pstore_device(dev); |
330 | kfree(objp: dev); |
331 | fput(psblk_file); |
332 | psblk_file = NULL; |
333 | } |
334 | } |
335 | |
336 | static int __init pstore_blk_init(void) |
337 | { |
338 | int ret; |
339 | |
340 | mutex_lock(&pstore_blk_lock); |
341 | ret = __best_effort_init(); |
342 | mutex_unlock(lock: &pstore_blk_lock); |
343 | |
344 | return ret; |
345 | } |
346 | late_initcall(pstore_blk_init); |
347 | |
348 | static void __exit pstore_blk_exit(void) |
349 | { |
350 | mutex_lock(&pstore_blk_lock); |
351 | __best_effort_exit(); |
352 | /* If we've been asked to unload, unregister any remaining device. */ |
353 | __unregister_pstore_device(dev: pstore_device_info); |
354 | mutex_unlock(lock: &pstore_blk_lock); |
355 | } |
356 | module_exit(pstore_blk_exit); |
357 | |
358 | MODULE_LICENSE("GPL" ); |
359 | MODULE_AUTHOR("WeiXiong Liao <liaoweixiong@allwinnertech.com>" ); |
360 | MODULE_AUTHOR("Kees Cook <keescook@chromium.org>" ); |
361 | MODULE_DESCRIPTION("pstore backend for block devices" ); |
362 | |