145051539SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2ca01d6ddSTony Luck /*
3ca01d6ddSTony Luck * Persistent Storage - ramfs parts.
4ca01d6ddSTony Luck *
5ca01d6ddSTony Luck * Copyright (C) 2010 Intel Corporation <tony.luck@intel.com>
6ca01d6ddSTony Luck */
7ca01d6ddSTony Luck
8ca01d6ddSTony Luck #include <linux/module.h>
9ca01d6ddSTony Luck #include <linux/fs.h>
10ca01d6ddSTony Luck #include <linux/fsnotify.h>
11ca01d6ddSTony Luck #include <linux/pagemap.h>
12ca01d6ddSTony Luck #include <linux/highmem.h>
13ca01d6ddSTony Luck #include <linux/time.h>
14ca01d6ddSTony Luck #include <linux/init.h>
156dda9266SLuck, Tony #include <linux/list.h>
16ca01d6ddSTony Luck #include <linux/string.h>
17ca01d6ddSTony Luck #include <linux/mount.h>
18060287b8SAnton Vorontsov #include <linux/seq_file.h>
19ca01d6ddSTony Luck #include <linux/ramfs.h>
20366f7e7aSLuck, Tony #include <linux/parser.h>
21ca01d6ddSTony Luck #include <linux/sched.h>
22ca01d6ddSTony Luck #include <linux/magic.h>
23ca01d6ddSTony Luck #include <linux/pstore.h>
24ca01d6ddSTony Luck #include <linux/slab.h>
25ca01d6ddSTony Luck #include <linux/uaccess.h>
26ca01d6ddSTony Luck
27ca01d6ddSTony Luck #include "internal.h"
28ca01d6ddSTony Luck
29ca01d6ddSTony Luck #define PSTORE_NAMELEN 64
30ca01d6ddSTony Luck
31db23491cSKees Cook static DEFINE_MUTEX(records_list_lock);
3247af61ffSKees Cook static LIST_HEAD(records_list);
336dda9266SLuck, Tony
3427e5041aSKees Cook static DEFINE_MUTEX(pstore_sb_lock);
3527e5041aSKees Cook static struct super_block *pstore_sb;
3627e5041aSKees Cook
37ca01d6ddSTony Luck struct pstore_private {
386dda9266SLuck, Tony struct list_head list;
39609e28bbSKees Cook struct dentry *dentry;
4083f70f07SKees Cook struct pstore_record *record;
4183f70f07SKees Cook size_t total_size;
42ca01d6ddSTony Luck };
43ca01d6ddSTony Luck
44060287b8SAnton Vorontsov struct pstore_ftrace_seq_data {
45060287b8SAnton Vorontsov const void *ptr;
46060287b8SAnton Vorontsov size_t off;
47060287b8SAnton Vorontsov size_t size;
48060287b8SAnton Vorontsov };
49060287b8SAnton Vorontsov
50060287b8SAnton Vorontsov #define REC_SIZE sizeof(struct pstore_ftrace_record)
51060287b8SAnton Vorontsov
free_pstore_private(struct pstore_private * private)521dfff7ddSKees Cook static void free_pstore_private(struct pstore_private *private)
531dfff7ddSKees Cook {
541dfff7ddSKees Cook if (!private)
551dfff7ddSKees Cook return;
5683f70f07SKees Cook if (private->record) {
57104fd0b5SYuxiao Zhang kvfree(private->record->buf);
588ca869b2SArd Biesheuvel kfree(private->record->priv);
5983f70f07SKees Cook kfree(private->record);
6083f70f07SKees Cook }
611dfff7ddSKees Cook kfree(private);
621dfff7ddSKees Cook }
631dfff7ddSKees Cook
pstore_ftrace_seq_start(struct seq_file * s,loff_t * pos)64060287b8SAnton Vorontsov static void *pstore_ftrace_seq_start(struct seq_file *s, loff_t *pos)
65060287b8SAnton Vorontsov {
66060287b8SAnton Vorontsov struct pstore_private *ps = s->private;
67060287b8SAnton Vorontsov struct pstore_ftrace_seq_data *data;
68060287b8SAnton Vorontsov
69060287b8SAnton Vorontsov data = kzalloc(sizeof(*data), GFP_KERNEL);
70060287b8SAnton Vorontsov if (!data)
71060287b8SAnton Vorontsov return NULL;
72060287b8SAnton Vorontsov
7383f70f07SKees Cook data->off = ps->total_size % REC_SIZE;
74060287b8SAnton Vorontsov data->off += *pos * REC_SIZE;
7583f70f07SKees Cook if (data->off + REC_SIZE > ps->total_size) {
76060287b8SAnton Vorontsov kfree(data);
77060287b8SAnton Vorontsov return NULL;
78060287b8SAnton Vorontsov }
79060287b8SAnton Vorontsov
80060287b8SAnton Vorontsov return data;
81060287b8SAnton Vorontsov
82060287b8SAnton Vorontsov }
83060287b8SAnton Vorontsov
pstore_ftrace_seq_stop(struct seq_file * s,void * v)84060287b8SAnton Vorontsov static void pstore_ftrace_seq_stop(struct seq_file *s, void *v)
85060287b8SAnton Vorontsov {
86060287b8SAnton Vorontsov kfree(v);
87060287b8SAnton Vorontsov }
88060287b8SAnton Vorontsov
pstore_ftrace_seq_next(struct seq_file * s,void * v,loff_t * pos)89060287b8SAnton Vorontsov static void *pstore_ftrace_seq_next(struct seq_file *s, void *v, loff_t *pos)
90060287b8SAnton Vorontsov {
91060287b8SAnton Vorontsov struct pstore_private *ps = s->private;
92060287b8SAnton Vorontsov struct pstore_ftrace_seq_data *data = v;
93060287b8SAnton Vorontsov
946c871b73SVasily Averin (*pos)++;
95060287b8SAnton Vorontsov data->off += REC_SIZE;
9683f70f07SKees Cook if (data->off + REC_SIZE > ps->total_size)
97060287b8SAnton Vorontsov return NULL;
98060287b8SAnton Vorontsov
99060287b8SAnton Vorontsov return data;
100060287b8SAnton Vorontsov }
101060287b8SAnton Vorontsov
pstore_ftrace_seq_show(struct seq_file * s,void * v)102060287b8SAnton Vorontsov static int pstore_ftrace_seq_show(struct seq_file *s, void *v)
103060287b8SAnton Vorontsov {
104060287b8SAnton Vorontsov struct pstore_private *ps = s->private;
105060287b8SAnton Vorontsov struct pstore_ftrace_seq_data *data = v;
10683f70f07SKees Cook struct pstore_ftrace_record *rec;
10783f70f07SKees Cook
1086c871b73SVasily Averin if (!data)
1096c871b73SVasily Averin return 0;
1106c871b73SVasily Averin
11183f70f07SKees Cook rec = (struct pstore_ftrace_record *)(ps->record->buf + data->off);
112060287b8SAnton Vorontsov
113d75f773cSSakari Ailus seq_printf(s, "CPU:%d ts:%llu %08lx %08lx %ps <- %pS\n",
114fbccdeb8SJoel Fernandes pstore_ftrace_decode_cpu(rec),
115fbccdeb8SJoel Fernandes pstore_ftrace_read_timestamp(rec),
116fbccdeb8SJoel Fernandes rec->ip, rec->parent_ip, (void *)rec->ip,
117fbccdeb8SJoel Fernandes (void *)rec->parent_ip);
118060287b8SAnton Vorontsov
119060287b8SAnton Vorontsov return 0;
120060287b8SAnton Vorontsov }
121060287b8SAnton Vorontsov
122060287b8SAnton Vorontsov static const struct seq_operations pstore_ftrace_seq_ops = {
123060287b8SAnton Vorontsov .start = pstore_ftrace_seq_start,
124060287b8SAnton Vorontsov .next = pstore_ftrace_seq_next,
125060287b8SAnton Vorontsov .stop = pstore_ftrace_seq_stop,
126060287b8SAnton Vorontsov .show = pstore_ftrace_seq_show,
127060287b8SAnton Vorontsov };
128060287b8SAnton Vorontsov
pstore_file_read(struct file * file,char __user * userbuf,size_t count,loff_t * ppos)129fbe0aa1fSTony Luck static ssize_t pstore_file_read(struct file *file, char __user *userbuf,
130fbe0aa1fSTony Luck size_t count, loff_t *ppos)
131fbe0aa1fSTony Luck {
132060287b8SAnton Vorontsov struct seq_file *sf = file->private_data;
133060287b8SAnton Vorontsov struct pstore_private *ps = sf->private;
134fbe0aa1fSTony Luck
13583f70f07SKees Cook if (ps->record->type == PSTORE_TYPE_FTRACE)
136060287b8SAnton Vorontsov return seq_read(file, userbuf, count, ppos);
13783f70f07SKees Cook return simple_read_from_buffer(userbuf, count, ppos,
13883f70f07SKees Cook ps->record->buf, ps->total_size);
139fbe0aa1fSTony Luck }
140fbe0aa1fSTony Luck
pstore_file_open(struct inode * inode,struct file * file)141060287b8SAnton Vorontsov static int pstore_file_open(struct inode *inode, struct file *file)
142060287b8SAnton Vorontsov {
143060287b8SAnton Vorontsov struct pstore_private *ps = inode->i_private;
144060287b8SAnton Vorontsov struct seq_file *sf;
145060287b8SAnton Vorontsov int err;
146060287b8SAnton Vorontsov const struct seq_operations *sops = NULL;
147060287b8SAnton Vorontsov
14883f70f07SKees Cook if (ps->record->type == PSTORE_TYPE_FTRACE)
149060287b8SAnton Vorontsov sops = &pstore_ftrace_seq_ops;
150060287b8SAnton Vorontsov
151060287b8SAnton Vorontsov err = seq_open(file, sops);
152060287b8SAnton Vorontsov if (err < 0)
153060287b8SAnton Vorontsov return err;
154060287b8SAnton Vorontsov
155060287b8SAnton Vorontsov sf = file->private_data;
156060287b8SAnton Vorontsov sf->private = ps;
157060287b8SAnton Vorontsov
158060287b8SAnton Vorontsov return 0;
159060287b8SAnton Vorontsov }
160060287b8SAnton Vorontsov
pstore_file_llseek(struct file * file,loff_t off,int whence)161965c8e59SAndrew Morton static loff_t pstore_file_llseek(struct file *file, loff_t off, int whence)
162060287b8SAnton Vorontsov {
163060287b8SAnton Vorontsov struct seq_file *sf = file->private_data;
164060287b8SAnton Vorontsov
165060287b8SAnton Vorontsov if (sf->op)
166965c8e59SAndrew Morton return seq_lseek(file, off, whence);
167965c8e59SAndrew Morton return default_llseek(file, off, whence);
168060287b8SAnton Vorontsov }
169060287b8SAnton Vorontsov
170fbe0aa1fSTony Luck static const struct file_operations pstore_file_operations = {
171060287b8SAnton Vorontsov .open = pstore_file_open,
172fbe0aa1fSTony Luck .read = pstore_file_read,
173060287b8SAnton Vorontsov .llseek = pstore_file_llseek,
174060287b8SAnton Vorontsov .release = seq_release,
175fbe0aa1fSTony Luck };
176ca01d6ddSTony Luck
177ca01d6ddSTony Luck /*
178ca01d6ddSTony Luck * When a file is unlinked from our file system we call the
179ca01d6ddSTony Luck * platform driver to erase the record from persistent store.
180ca01d6ddSTony Luck */
pstore_unlink(struct inode * dir,struct dentry * dentry)181ca01d6ddSTony Luck static int pstore_unlink(struct inode *dir, struct dentry *dentry)
182ca01d6ddSTony Luck {
1832b0143b5SDavid Howells struct pstore_private *p = d_inode(dentry)->i_private;
18483f70f07SKees Cook struct pstore_record *record = p->record;
185ca01d6ddSTony Luck
186a61072aaSKees Cook if (!record->psi->erase)
187bf288333SAruna Balakrishnaiah return -EPERM;
188a61072aaSKees Cook
1897a0ad546SKees Cook /* Make sure we can't race while removing this file. */
190952d8a80SKees Cook scoped_guard(mutex, &records_list_lock) {
1917a0ad546SKees Cook if (!list_empty(&p->list))
1927a0ad546SKees Cook list_del_init(&p->list);
1937a0ad546SKees Cook else
194952d8a80SKees Cook return -ENOENT;
195609e28bbSKees Cook p->dentry = NULL;
196952d8a80SKees Cook }
1977a0ad546SKees Cook
198952d8a80SKees Cook scoped_guard(mutex, &record->psi->read_mutex)
199a61072aaSKees Cook record->psi->erase(record);
200ca01d6ddSTony Luck
201ca01d6ddSTony Luck return simple_unlink(dir, dentry);
202ca01d6ddSTony Luck }
203ca01d6ddSTony Luck
pstore_evict_inode(struct inode * inode)204a872d510STony Luck static void pstore_evict_inode(struct inode *inode)
205a872d510STony Luck {
2066dda9266SLuck, Tony struct pstore_private *p = inode->i_private;
2076dda9266SLuck, Tony
208dbd5768fSJan Kara clear_inode(inode);
2091dfff7ddSKees Cook free_pstore_private(p);
2106dda9266SLuck, Tony }
211a872d510STony Luck
212ca01d6ddSTony Luck static const struct inode_operations pstore_dir_inode_operations = {
213ca01d6ddSTony Luck .lookup = simple_lookup,
214ca01d6ddSTony Luck .unlink = pstore_unlink,
215ca01d6ddSTony Luck };
216ca01d6ddSTony Luck
pstore_get_inode(struct super_block * sb)21722a71c30SAl Viro static struct inode *pstore_get_inode(struct super_block *sb)
218fbe0aa1fSTony Luck {
219fbe0aa1fSTony Luck struct inode *inode = new_inode(sb);
220fbe0aa1fSTony Luck if (inode) {
221fbe0aa1fSTony Luck inode->i_ino = get_next_ino();
222a411ea5aSJeff Layton inode->i_atime = inode->i_mtime = inode_set_ctime_current(inode);
223fbe0aa1fSTony Luck }
224fbe0aa1fSTony Luck return inode;
225fbe0aa1fSTony Luck }
226fbe0aa1fSTony Luck
227366f7e7aSLuck, Tony enum {
228366f7e7aSLuck, Tony Opt_kmsg_bytes, Opt_err
229366f7e7aSLuck, Tony };
230366f7e7aSLuck, Tony
231366f7e7aSLuck, Tony static const match_table_t tokens = {
232366f7e7aSLuck, Tony {Opt_kmsg_bytes, "kmsg_bytes=%u"},
233366f7e7aSLuck, Tony {Opt_err, NULL}
234366f7e7aSLuck, Tony };
235366f7e7aSLuck, Tony
parse_options(char * options)236366f7e7aSLuck, Tony static void parse_options(char *options)
237366f7e7aSLuck, Tony {
238366f7e7aSLuck, Tony char *p;
239366f7e7aSLuck, Tony substring_t args[MAX_OPT_ARGS];
240366f7e7aSLuck, Tony int option;
241366f7e7aSLuck, Tony
242366f7e7aSLuck, Tony if (!options)
243366f7e7aSLuck, Tony return;
244366f7e7aSLuck, Tony
245366f7e7aSLuck, Tony while ((p = strsep(&options, ",")) != NULL) {
246366f7e7aSLuck, Tony int token;
247366f7e7aSLuck, Tony
248366f7e7aSLuck, Tony if (!*p)
249366f7e7aSLuck, Tony continue;
250366f7e7aSLuck, Tony
251366f7e7aSLuck, Tony token = match_token(p, tokens, args);
252366f7e7aSLuck, Tony switch (token) {
253366f7e7aSLuck, Tony case Opt_kmsg_bytes:
254366f7e7aSLuck, Tony if (!match_int(&args[0], &option))
255366f7e7aSLuck, Tony pstore_set_kmsg_bytes(option);
256366f7e7aSLuck, Tony break;
257366f7e7aSLuck, Tony }
258366f7e7aSLuck, Tony }
259366f7e7aSLuck, Tony }
260366f7e7aSLuck, Tony
261349d7438SDavid Howells /*
262349d7438SDavid Howells * Display the mount options in /proc/mounts.
263349d7438SDavid Howells */
pstore_show_options(struct seq_file * m,struct dentry * root)264349d7438SDavid Howells static int pstore_show_options(struct seq_file *m, struct dentry *root)
265349d7438SDavid Howells {
26626fecbf7SVasile-Laurentiu Stanimir if (kmsg_bytes != CONFIG_PSTORE_DEFAULT_KMSG_BYTES)
267349d7438SDavid Howells seq_printf(m, ",kmsg_bytes=%lu", kmsg_bytes);
268349d7438SDavid Howells return 0;
269349d7438SDavid Howells }
270349d7438SDavid Howells
pstore_remount(struct super_block * sb,int * flags,char * data)271366f7e7aSLuck, Tony static int pstore_remount(struct super_block *sb, int *flags, char *data)
272366f7e7aSLuck, Tony {
27302b9984dSTheodore Ts'o sync_filesystem(sb);
274366f7e7aSLuck, Tony parse_options(data);
275366f7e7aSLuck, Tony
276366f7e7aSLuck, Tony return 0;
277366f7e7aSLuck, Tony }
278366f7e7aSLuck, Tony
279ca01d6ddSTony Luck static const struct super_operations pstore_ops = {
280ca01d6ddSTony Luck .statfs = simple_statfs,
281ca01d6ddSTony Luck .drop_inode = generic_delete_inode,
282a872d510STony Luck .evict_inode = pstore_evict_inode,
283366f7e7aSLuck, Tony .remount_fs = pstore_remount,
284349d7438SDavid Howells .show_options = pstore_show_options,
285ca01d6ddSTony Luck };
286ca01d6ddSTony Luck
psinfo_lock_root(void)28727e5041aSKees Cook static struct dentry *psinfo_lock_root(void)
288ca01d6ddSTony Luck {
28927e5041aSKees Cook struct dentry *root;
29027e5041aSKees Cook
291952d8a80SKees Cook guard(mutex)(&pstore_sb_lock);
29227e5041aSKees Cook /*
29327e5041aSKees Cook * Having no backend is fine -- no records appear.
29427e5041aSKees Cook * Not being mounted is fine -- nothing to do.
29527e5041aSKees Cook */
296952d8a80SKees Cook if (!psinfo || !pstore_sb)
29727e5041aSKees Cook return NULL;
29827e5041aSKees Cook
29927e5041aSKees Cook root = pstore_sb->s_root;
30027e5041aSKees Cook inode_lock(d_inode(root));
30127e5041aSKees Cook
30227e5041aSKees Cook return root;
303ca01d6ddSTony Luck }
304ca01d6ddSTony Luck
pstore_put_backend_records(struct pstore_info * psi)305609e28bbSKees Cook int pstore_put_backend_records(struct pstore_info *psi)
306609e28bbSKees Cook {
307609e28bbSKees Cook struct pstore_private *pos, *tmp;
308609e28bbSKees Cook struct dentry *root;
309609e28bbSKees Cook
310609e28bbSKees Cook root = psinfo_lock_root();
311609e28bbSKees Cook if (!root)
312609e28bbSKees Cook return 0;
313609e28bbSKees Cook
314952d8a80SKees Cook scoped_guard(mutex, &records_list_lock) {
315609e28bbSKees Cook list_for_each_entry_safe(pos, tmp, &records_list, list) {
316609e28bbSKees Cook if (pos->record->psi == psi) {
317609e28bbSKees Cook list_del_init(&pos->list);
318*4cdf9006SKees Cook d_invalidate(pos->dentry);
319*4cdf9006SKees Cook simple_unlink(d_inode(root), pos->dentry);
320609e28bbSKees Cook pos->dentry = NULL;
321609e28bbSKees Cook }
322609e28bbSKees Cook }
323952d8a80SKees Cook }
324609e28bbSKees Cook
325609e28bbSKees Cook inode_unlock(d_inode(root));
326609e28bbSKees Cook
327*4cdf9006SKees Cook return 0;
328609e28bbSKees Cook }
329609e28bbSKees Cook
330ca01d6ddSTony Luck /*
331ca01d6ddSTony Luck * Make a regular file in the root directory of our file system.
332ca01d6ddSTony Luck * Load it up with "size" bytes of data from "buf".
333ca01d6ddSTony Luck * Set the mtime & ctime to the date that this record was originally stored.
334ca01d6ddSTony Luck */
pstore_mkfile(struct dentry * root,struct pstore_record * record)3353a7d2fd1SKees Cook int pstore_mkfile(struct dentry *root, struct pstore_record *record)
336ca01d6ddSTony Luck {
337ca01d6ddSTony Luck struct dentry *dentry;
338ca01d6ddSTony Luck struct inode *inode;
3396dda9266SLuck, Tony int rc = 0;
340ca01d6ddSTony Luck char name[PSTORE_NAMELEN];
3416dda9266SLuck, Tony struct pstore_private *private, *pos;
3421edd1aa3SKees Cook size_t size = record->size + record->ecc_notice_size;
3436dda9266SLuck, Tony
34427e5041aSKees Cook if (WARN_ON(!inode_is_locked(d_inode(root))))
34527e5041aSKees Cook return -EINVAL;
3463a7d2fd1SKees Cook
347952d8a80SKees Cook guard(mutex)(&records_list_lock);
348952d8a80SKees Cook
34927e5041aSKees Cook /* Skip records that are already present in the filesystem. */
35047af61ffSKees Cook list_for_each_entry(pos, &records_list, list) {
35183f70f07SKees Cook if (pos->record->type == record->type &&
35283f70f07SKees Cook pos->record->id == record->id &&
35327e5041aSKees Cook pos->record->psi == record->psi)
354952d8a80SKees Cook return -EEXIST;
3556dda9266SLuck, Tony }
356ca01d6ddSTony Luck
357ca01d6ddSTony Luck rc = -ENOMEM;
3583a7d2fd1SKees Cook inode = pstore_get_inode(root->d_sb);
359ca01d6ddSTony Luck if (!inode)
360952d8a80SKees Cook return -ENOMEM;
36122a71c30SAl Viro inode->i_mode = S_IFREG | 0444;
36222a71c30SAl Viro inode->i_fop = &pstore_file_operations;
363f0f23e54SJoel Fernandes (Google) scnprintf(name, sizeof(name), "%s-%s-%llu%s",
364f0f23e54SJoel Fernandes (Google) pstore_type_to_name(record->type),
3651edd1aa3SKees Cook record->psi->name, record->id,
3661edd1aa3SKees Cook record->compressed ? ".enc.z" : "");
367ca01d6ddSTony Luck
3684c6d80e1SNorbert Manthey private = kzalloc(sizeof(*private), GFP_KERNEL);
3694c6d80e1SNorbert Manthey if (!private)
3704c6d80e1SNorbert Manthey goto fail_inode;
3714c6d80e1SNorbert Manthey
372ca01d6ddSTony Luck dentry = d_alloc_name(root, name);
373c39524e6SDan Carpenter if (!dentry)
3743a7d2fd1SKees Cook goto fail_private;
375ca01d6ddSTony Luck
376609e28bbSKees Cook private->dentry = dentry;
3774c6d80e1SNorbert Manthey private->record = record;
37883f70f07SKees Cook inode->i_size = private->total_size = size;
379ca01d6ddSTony Luck inode->i_private = private;
380ca01d6ddSTony Luck
3811edd1aa3SKees Cook if (record->time.tv_sec)
382a411ea5aSJeff Layton inode->i_mtime = inode_set_ctime_to_ts(inode, record->time);
383ca01d6ddSTony Luck
384fbe0aa1fSTony Luck d_add(dentry, inode);
385ca01d6ddSTony Luck
38647af61ffSKees Cook list_add(&private->list, &records_list);
3876dda9266SLuck, Tony
388fbe0aa1fSTony Luck return 0;
389ca01d6ddSTony Luck
3903a7d2fd1SKees Cook fail_private:
3911dfff7ddSKees Cook free_pstore_private(private);
3924c6d80e1SNorbert Manthey fail_inode:
393ca01d6ddSTony Luck iput(inode);
394ca01d6ddSTony Luck return rc;
395ca01d6ddSTony Luck }
396ca01d6ddSTony Luck
3973a7d2fd1SKees Cook /*
3983a7d2fd1SKees Cook * Read all the records from the persistent store. Create
3993a7d2fd1SKees Cook * files in our filesystem. Don't warn about -EEXIST errors
4003a7d2fd1SKees Cook * when we are re-scanning the backing store looking to add new
4013a7d2fd1SKees Cook * error records.
4023a7d2fd1SKees Cook */
pstore_get_records(int quiet)4033a7d2fd1SKees Cook void pstore_get_records(int quiet)
4043a7d2fd1SKees Cook {
4053a7d2fd1SKees Cook struct dentry *root;
4063a7d2fd1SKees Cook
40727e5041aSKees Cook root = psinfo_lock_root();
40827e5041aSKees Cook if (!root)
4093a7d2fd1SKees Cook return;
4103a7d2fd1SKees Cook
41127e5041aSKees Cook pstore_get_backend_records(psinfo, root, quiet);
4123a7d2fd1SKees Cook inode_unlock(d_inode(root));
4133a7d2fd1SKees Cook }
4143a7d2fd1SKees Cook
pstore_fill_super(struct super_block * sb,void * data,int silent)415364ed2f4SAnton Vorontsov static int pstore_fill_super(struct super_block *sb, void *data, int silent)
416ca01d6ddSTony Luck {
417318ceed0SAl Viro struct inode *inode;
418ca01d6ddSTony Luck
419ca01d6ddSTony Luck sb->s_maxbytes = MAX_LFS_FILESIZE;
42009cbfeafSKirill A. Shutemov sb->s_blocksize = PAGE_SIZE;
42109cbfeafSKirill A. Shutemov sb->s_blocksize_bits = PAGE_SHIFT;
422ca01d6ddSTony Luck sb->s_magic = PSTOREFS_MAGIC;
423ca01d6ddSTony Luck sb->s_op = &pstore_ops;
424ca01d6ddSTony Luck sb->s_time_gran = 1;
425ca01d6ddSTony Luck
426366f7e7aSLuck, Tony parse_options(data);
427366f7e7aSLuck, Tony
42822a71c30SAl Viro inode = pstore_get_inode(sb);
429318ceed0SAl Viro if (inode) {
430d7caa336SKees Cook inode->i_mode = S_IFDIR | 0750;
431ca01d6ddSTony Luck inode->i_op = &pstore_dir_inode_operations;
43222a71c30SAl Viro inode->i_fop = &simple_dir_operations;
43322a71c30SAl Viro inc_nlink(inode);
434ca01d6ddSTony Luck }
435318ceed0SAl Viro sb->s_root = d_make_root(inode);
436318ceed0SAl Viro if (!sb->s_root)
437318ceed0SAl Viro return -ENOMEM;
438ca01d6ddSTony Luck
439952d8a80SKees Cook scoped_guard(mutex, &pstore_sb_lock)
44027e5041aSKees Cook pstore_sb = sb;
44127e5041aSKees Cook
4426dda9266SLuck, Tony pstore_get_records(0);
443ca01d6ddSTony Luck
444ca01d6ddSTony Luck return 0;
445ca01d6ddSTony Luck }
446ca01d6ddSTony Luck
pstore_mount(struct file_system_type * fs_type,int flags,const char * dev_name,void * data)447fbe0aa1fSTony Luck static struct dentry *pstore_mount(struct file_system_type *fs_type,
448fbe0aa1fSTony Luck int flags, const char *dev_name, void *data)
449ca01d6ddSTony Luck {
450fbe0aa1fSTony Luck return mount_single(fs_type, flags, data, pstore_fill_super);
451ca01d6ddSTony Luck }
452ca01d6ddSTony Luck
pstore_kill_sb(struct super_block * sb)453ca01d6ddSTony Luck static void pstore_kill_sb(struct super_block *sb)
454ca01d6ddSTony Luck {
455952d8a80SKees Cook guard(mutex)(&pstore_sb_lock);
4569c7d83aeSTetsuo Handa WARN_ON(pstore_sb && pstore_sb != sb);
45727e5041aSKees Cook
458ca01d6ddSTony Luck kill_litter_super(sb);
459ca01d6ddSTony Luck pstore_sb = NULL;
46027e5041aSKees Cook
461952d8a80SKees Cook guard(mutex)(&records_list_lock);
4627a0ad546SKees Cook INIT_LIST_HEAD(&records_list);
463ca01d6ddSTony Luck }
464ca01d6ddSTony Luck
465ca01d6ddSTony Luck static struct file_system_type pstore_fs_type = {
466ee1d2674SGeliang Tang .owner = THIS_MODULE,
467ca01d6ddSTony Luck .name = "pstore",
468fbe0aa1fSTony Luck .mount = pstore_mount,
469ca01d6ddSTony Luck .kill_sb = pstore_kill_sb,
470ca01d6ddSTony Luck };
471ca01d6ddSTony Luck
pstore_init_fs(void)472cb095afdSKees Cook int __init pstore_init_fs(void)
473ca01d6ddSTony Luck {
474f9bb4882SEric W. Biederman int err;
475fb0af3f2SJosh Boyer
476fb0af3f2SJosh Boyer /* Create a convenient mount point for people to access pstore */
477f9bb4882SEric W. Biederman err = sysfs_create_mount_point(fs_kobj, "pstore");
478f9bb4882SEric W. Biederman if (err)
479fb0af3f2SJosh Boyer goto out;
480fb0af3f2SJosh Boyer
481fb0af3f2SJosh Boyer err = register_filesystem(&pstore_fs_type);
482fb0af3f2SJosh Boyer if (err < 0)
483f9bb4882SEric W. Biederman sysfs_remove_mount_point(fs_kobj, "pstore");
484fb0af3f2SJosh Boyer
485fb0af3f2SJosh Boyer out:
486fb0af3f2SJosh Boyer return err;
487ca01d6ddSTony Luck }
488ca01d6ddSTony Luck
pstore_exit_fs(void)489cb095afdSKees Cook void __exit pstore_exit_fs(void)
490ee1d2674SGeliang Tang {
491ee1d2674SGeliang Tang unregister_filesystem(&pstore_fs_type);
492ee1d2674SGeliang Tang sysfs_remove_mount_point(fs_kobj, "pstore");
493ee1d2674SGeliang Tang }
494