xref: /kernel/linux/linux-5.10/fs/pstore/inode.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Persistent Storage - ramfs parts.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2010 Intel Corporation <tony.luck@intel.com>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/module.h>
98c2ecf20Sopenharmony_ci#include <linux/fs.h>
108c2ecf20Sopenharmony_ci#include <linux/fsnotify.h>
118c2ecf20Sopenharmony_ci#include <linux/pagemap.h>
128c2ecf20Sopenharmony_ci#include <linux/highmem.h>
138c2ecf20Sopenharmony_ci#include <linux/time.h>
148c2ecf20Sopenharmony_ci#include <linux/init.h>
158c2ecf20Sopenharmony_ci#include <linux/list.h>
168c2ecf20Sopenharmony_ci#include <linux/string.h>
178c2ecf20Sopenharmony_ci#include <linux/mount.h>
188c2ecf20Sopenharmony_ci#include <linux/seq_file.h>
198c2ecf20Sopenharmony_ci#include <linux/ramfs.h>
208c2ecf20Sopenharmony_ci#include <linux/parser.h>
218c2ecf20Sopenharmony_ci#include <linux/sched.h>
228c2ecf20Sopenharmony_ci#include <linux/magic.h>
238c2ecf20Sopenharmony_ci#include <linux/pstore.h>
248c2ecf20Sopenharmony_ci#include <linux/slab.h>
258c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#include "internal.h"
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#define	PSTORE_NAMELEN	64
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(records_list_lock);
328c2ecf20Sopenharmony_cistatic LIST_HEAD(records_list);
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(pstore_sb_lock);
358c2ecf20Sopenharmony_cistatic struct super_block *pstore_sb;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistruct pstore_private {
388c2ecf20Sopenharmony_ci	struct list_head list;
398c2ecf20Sopenharmony_ci	struct dentry *dentry;
408c2ecf20Sopenharmony_ci	struct pstore_record *record;
418c2ecf20Sopenharmony_ci	size_t total_size;
428c2ecf20Sopenharmony_ci};
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistruct pstore_ftrace_seq_data {
458c2ecf20Sopenharmony_ci	const void *ptr;
468c2ecf20Sopenharmony_ci	size_t off;
478c2ecf20Sopenharmony_ci	size_t size;
488c2ecf20Sopenharmony_ci};
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci#define REC_SIZE sizeof(struct pstore_ftrace_record)
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cistatic void free_pstore_private(struct pstore_private *private)
538c2ecf20Sopenharmony_ci{
548c2ecf20Sopenharmony_ci	if (!private)
558c2ecf20Sopenharmony_ci		return;
568c2ecf20Sopenharmony_ci	if (private->record) {
578c2ecf20Sopenharmony_ci		kfree(private->record->buf);
588c2ecf20Sopenharmony_ci		kfree(private->record);
598c2ecf20Sopenharmony_ci	}
608c2ecf20Sopenharmony_ci	kfree(private);
618c2ecf20Sopenharmony_ci}
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_cistatic void *pstore_ftrace_seq_start(struct seq_file *s, loff_t *pos)
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	struct pstore_private *ps = s->private;
668c2ecf20Sopenharmony_ci	struct pstore_ftrace_seq_data *data;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	data = kzalloc(sizeof(*data), GFP_KERNEL);
698c2ecf20Sopenharmony_ci	if (!data)
708c2ecf20Sopenharmony_ci		return NULL;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	data->off = ps->total_size % REC_SIZE;
738c2ecf20Sopenharmony_ci	data->off += *pos * REC_SIZE;
748c2ecf20Sopenharmony_ci	if (data->off + REC_SIZE > ps->total_size) {
758c2ecf20Sopenharmony_ci		kfree(data);
768c2ecf20Sopenharmony_ci		return NULL;
778c2ecf20Sopenharmony_ci	}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	return data;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistatic void pstore_ftrace_seq_stop(struct seq_file *s, void *v)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci	kfree(v);
868c2ecf20Sopenharmony_ci}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_cistatic void *pstore_ftrace_seq_next(struct seq_file *s, void *v, loff_t *pos)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	struct pstore_private *ps = s->private;
918c2ecf20Sopenharmony_ci	struct pstore_ftrace_seq_data *data = v;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	(*pos)++;
948c2ecf20Sopenharmony_ci	data->off += REC_SIZE;
958c2ecf20Sopenharmony_ci	if (data->off + REC_SIZE > ps->total_size)
968c2ecf20Sopenharmony_ci		return NULL;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	return data;
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cistatic int pstore_ftrace_seq_show(struct seq_file *s, void *v)
1028c2ecf20Sopenharmony_ci{
1038c2ecf20Sopenharmony_ci	struct pstore_private *ps = s->private;
1048c2ecf20Sopenharmony_ci	struct pstore_ftrace_seq_data *data = v;
1058c2ecf20Sopenharmony_ci	struct pstore_ftrace_record *rec;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	if (!data)
1088c2ecf20Sopenharmony_ci		return 0;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	rec = (struct pstore_ftrace_record *)(ps->record->buf + data->off);
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	seq_printf(s, "CPU:%d ts:%llu %08lx  %08lx  %ps <- %pS\n",
1138c2ecf20Sopenharmony_ci		   pstore_ftrace_decode_cpu(rec),
1148c2ecf20Sopenharmony_ci		   pstore_ftrace_read_timestamp(rec),
1158c2ecf20Sopenharmony_ci		   rec->ip, rec->parent_ip, (void *)rec->ip,
1168c2ecf20Sopenharmony_ci		   (void *)rec->parent_ip);
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	return 0;
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_cistatic const struct seq_operations pstore_ftrace_seq_ops = {
1228c2ecf20Sopenharmony_ci	.start	= pstore_ftrace_seq_start,
1238c2ecf20Sopenharmony_ci	.next	= pstore_ftrace_seq_next,
1248c2ecf20Sopenharmony_ci	.stop	= pstore_ftrace_seq_stop,
1258c2ecf20Sopenharmony_ci	.show	= pstore_ftrace_seq_show,
1268c2ecf20Sopenharmony_ci};
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_cistatic ssize_t pstore_file_read(struct file *file, char __user *userbuf,
1298c2ecf20Sopenharmony_ci						size_t count, loff_t *ppos)
1308c2ecf20Sopenharmony_ci{
1318c2ecf20Sopenharmony_ci	struct seq_file *sf = file->private_data;
1328c2ecf20Sopenharmony_ci	struct pstore_private *ps = sf->private;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	if (ps->record->type == PSTORE_TYPE_FTRACE)
1358c2ecf20Sopenharmony_ci		return seq_read(file, userbuf, count, ppos);
1368c2ecf20Sopenharmony_ci	return simple_read_from_buffer(userbuf, count, ppos,
1378c2ecf20Sopenharmony_ci				       ps->record->buf, ps->total_size);
1388c2ecf20Sopenharmony_ci}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_cistatic int pstore_file_open(struct inode *inode, struct file *file)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	struct pstore_private *ps = inode->i_private;
1438c2ecf20Sopenharmony_ci	struct seq_file *sf;
1448c2ecf20Sopenharmony_ci	int err;
1458c2ecf20Sopenharmony_ci	const struct seq_operations *sops = NULL;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	if (ps->record->type == PSTORE_TYPE_FTRACE)
1488c2ecf20Sopenharmony_ci		sops = &pstore_ftrace_seq_ops;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	err = seq_open(file, sops);
1518c2ecf20Sopenharmony_ci	if (err < 0)
1528c2ecf20Sopenharmony_ci		return err;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	sf = file->private_data;
1558c2ecf20Sopenharmony_ci	sf->private = ps;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	return 0;
1588c2ecf20Sopenharmony_ci}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_cistatic loff_t pstore_file_llseek(struct file *file, loff_t off, int whence)
1618c2ecf20Sopenharmony_ci{
1628c2ecf20Sopenharmony_ci	struct seq_file *sf = file->private_data;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	if (sf->op)
1658c2ecf20Sopenharmony_ci		return seq_lseek(file, off, whence);
1668c2ecf20Sopenharmony_ci	return default_llseek(file, off, whence);
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistatic const struct file_operations pstore_file_operations = {
1708c2ecf20Sopenharmony_ci	.open		= pstore_file_open,
1718c2ecf20Sopenharmony_ci	.read		= pstore_file_read,
1728c2ecf20Sopenharmony_ci	.llseek		= pstore_file_llseek,
1738c2ecf20Sopenharmony_ci	.release	= seq_release,
1748c2ecf20Sopenharmony_ci};
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci/*
1778c2ecf20Sopenharmony_ci * When a file is unlinked from our file system we call the
1788c2ecf20Sopenharmony_ci * platform driver to erase the record from persistent store.
1798c2ecf20Sopenharmony_ci */
1808c2ecf20Sopenharmony_cistatic int pstore_unlink(struct inode *dir, struct dentry *dentry)
1818c2ecf20Sopenharmony_ci{
1828c2ecf20Sopenharmony_ci	struct pstore_private *p = d_inode(dentry)->i_private;
1838c2ecf20Sopenharmony_ci	struct pstore_record *record = p->record;
1848c2ecf20Sopenharmony_ci	int rc = 0;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	if (!record->psi->erase)
1878c2ecf20Sopenharmony_ci		return -EPERM;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	/* Make sure we can't race while removing this file. */
1908c2ecf20Sopenharmony_ci	mutex_lock(&records_list_lock);
1918c2ecf20Sopenharmony_ci	if (!list_empty(&p->list))
1928c2ecf20Sopenharmony_ci		list_del_init(&p->list);
1938c2ecf20Sopenharmony_ci	else
1948c2ecf20Sopenharmony_ci		rc = -ENOENT;
1958c2ecf20Sopenharmony_ci	p->dentry = NULL;
1968c2ecf20Sopenharmony_ci	mutex_unlock(&records_list_lock);
1978c2ecf20Sopenharmony_ci	if (rc)
1988c2ecf20Sopenharmony_ci		return rc;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	mutex_lock(&record->psi->read_mutex);
2018c2ecf20Sopenharmony_ci	record->psi->erase(record);
2028c2ecf20Sopenharmony_ci	mutex_unlock(&record->psi->read_mutex);
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	return simple_unlink(dir, dentry);
2058c2ecf20Sopenharmony_ci}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_cistatic void pstore_evict_inode(struct inode *inode)
2088c2ecf20Sopenharmony_ci{
2098c2ecf20Sopenharmony_ci	struct pstore_private	*p = inode->i_private;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	clear_inode(inode);
2128c2ecf20Sopenharmony_ci	free_pstore_private(p);
2138c2ecf20Sopenharmony_ci}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_cistatic const struct inode_operations pstore_dir_inode_operations = {
2168c2ecf20Sopenharmony_ci	.lookup		= simple_lookup,
2178c2ecf20Sopenharmony_ci	.unlink		= pstore_unlink,
2188c2ecf20Sopenharmony_ci};
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_cistatic struct inode *pstore_get_inode(struct super_block *sb)
2218c2ecf20Sopenharmony_ci{
2228c2ecf20Sopenharmony_ci	struct inode *inode = new_inode(sb);
2238c2ecf20Sopenharmony_ci	if (inode) {
2248c2ecf20Sopenharmony_ci		inode->i_ino = get_next_ino();
2258c2ecf20Sopenharmony_ci		inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode);
2268c2ecf20Sopenharmony_ci	}
2278c2ecf20Sopenharmony_ci	return inode;
2288c2ecf20Sopenharmony_ci}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_cienum {
2318c2ecf20Sopenharmony_ci	Opt_kmsg_bytes, Opt_err
2328c2ecf20Sopenharmony_ci};
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_cistatic const match_table_t tokens = {
2358c2ecf20Sopenharmony_ci	{Opt_kmsg_bytes, "kmsg_bytes=%u"},
2368c2ecf20Sopenharmony_ci	{Opt_err, NULL}
2378c2ecf20Sopenharmony_ci};
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_cistatic void parse_options(char *options)
2408c2ecf20Sopenharmony_ci{
2418c2ecf20Sopenharmony_ci	char		*p;
2428c2ecf20Sopenharmony_ci	substring_t	args[MAX_OPT_ARGS];
2438c2ecf20Sopenharmony_ci	int		option;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	if (!options)
2468c2ecf20Sopenharmony_ci		return;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	while ((p = strsep(&options, ",")) != NULL) {
2498c2ecf20Sopenharmony_ci		int token;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci		if (!*p)
2528c2ecf20Sopenharmony_ci			continue;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci		token = match_token(p, tokens, args);
2558c2ecf20Sopenharmony_ci		switch (token) {
2568c2ecf20Sopenharmony_ci		case Opt_kmsg_bytes:
2578c2ecf20Sopenharmony_ci			if (!match_int(&args[0], &option))
2588c2ecf20Sopenharmony_ci				pstore_set_kmsg_bytes(option);
2598c2ecf20Sopenharmony_ci			break;
2608c2ecf20Sopenharmony_ci		}
2618c2ecf20Sopenharmony_ci	}
2628c2ecf20Sopenharmony_ci}
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci/*
2658c2ecf20Sopenharmony_ci * Display the mount options in /proc/mounts.
2668c2ecf20Sopenharmony_ci */
2678c2ecf20Sopenharmony_cistatic int pstore_show_options(struct seq_file *m, struct dentry *root)
2688c2ecf20Sopenharmony_ci{
2698c2ecf20Sopenharmony_ci	if (kmsg_bytes != PSTORE_DEFAULT_KMSG_BYTES)
2708c2ecf20Sopenharmony_ci		seq_printf(m, ",kmsg_bytes=%lu", kmsg_bytes);
2718c2ecf20Sopenharmony_ci	return 0;
2728c2ecf20Sopenharmony_ci}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_cistatic int pstore_remount(struct super_block *sb, int *flags, char *data)
2758c2ecf20Sopenharmony_ci{
2768c2ecf20Sopenharmony_ci	sync_filesystem(sb);
2778c2ecf20Sopenharmony_ci	parse_options(data);
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	return 0;
2808c2ecf20Sopenharmony_ci}
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_cistatic const struct super_operations pstore_ops = {
2838c2ecf20Sopenharmony_ci	.statfs		= simple_statfs,
2848c2ecf20Sopenharmony_ci	.drop_inode	= generic_delete_inode,
2858c2ecf20Sopenharmony_ci	.evict_inode	= pstore_evict_inode,
2868c2ecf20Sopenharmony_ci	.remount_fs	= pstore_remount,
2878c2ecf20Sopenharmony_ci	.show_options	= pstore_show_options,
2888c2ecf20Sopenharmony_ci};
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_cistatic struct dentry *psinfo_lock_root(void)
2918c2ecf20Sopenharmony_ci{
2928c2ecf20Sopenharmony_ci	struct dentry *root;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	mutex_lock(&pstore_sb_lock);
2958c2ecf20Sopenharmony_ci	/*
2968c2ecf20Sopenharmony_ci	 * Having no backend is fine -- no records appear.
2978c2ecf20Sopenharmony_ci	 * Not being mounted is fine -- nothing to do.
2988c2ecf20Sopenharmony_ci	 */
2998c2ecf20Sopenharmony_ci	if (!psinfo || !pstore_sb) {
3008c2ecf20Sopenharmony_ci		mutex_unlock(&pstore_sb_lock);
3018c2ecf20Sopenharmony_ci		return NULL;
3028c2ecf20Sopenharmony_ci	}
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	root = pstore_sb->s_root;
3058c2ecf20Sopenharmony_ci	inode_lock(d_inode(root));
3068c2ecf20Sopenharmony_ci	mutex_unlock(&pstore_sb_lock);
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	return root;
3098c2ecf20Sopenharmony_ci}
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ciint pstore_put_backend_records(struct pstore_info *psi)
3128c2ecf20Sopenharmony_ci{
3138c2ecf20Sopenharmony_ci	struct pstore_private *pos, *tmp;
3148c2ecf20Sopenharmony_ci	struct dentry *root;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	root = psinfo_lock_root();
3178c2ecf20Sopenharmony_ci	if (!root)
3188c2ecf20Sopenharmony_ci		return 0;
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	mutex_lock(&records_list_lock);
3218c2ecf20Sopenharmony_ci	list_for_each_entry_safe(pos, tmp, &records_list, list) {
3228c2ecf20Sopenharmony_ci		if (pos->record->psi == psi) {
3238c2ecf20Sopenharmony_ci			list_del_init(&pos->list);
3248c2ecf20Sopenharmony_ci			d_invalidate(pos->dentry);
3258c2ecf20Sopenharmony_ci			simple_unlink(d_inode(root), pos->dentry);
3268c2ecf20Sopenharmony_ci			pos->dentry = NULL;
3278c2ecf20Sopenharmony_ci		}
3288c2ecf20Sopenharmony_ci	}
3298c2ecf20Sopenharmony_ci	mutex_unlock(&records_list_lock);
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	inode_unlock(d_inode(root));
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	return 0;
3348c2ecf20Sopenharmony_ci}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci/*
3378c2ecf20Sopenharmony_ci * Make a regular file in the root directory of our file system.
3388c2ecf20Sopenharmony_ci * Load it up with "size" bytes of data from "buf".
3398c2ecf20Sopenharmony_ci * Set the mtime & ctime to the date that this record was originally stored.
3408c2ecf20Sopenharmony_ci */
3418c2ecf20Sopenharmony_ciint pstore_mkfile(struct dentry *root, struct pstore_record *record)
3428c2ecf20Sopenharmony_ci{
3438c2ecf20Sopenharmony_ci	struct dentry		*dentry;
3448c2ecf20Sopenharmony_ci	struct inode		*inode;
3458c2ecf20Sopenharmony_ci	int			rc = 0;
3468c2ecf20Sopenharmony_ci	char			name[PSTORE_NAMELEN];
3478c2ecf20Sopenharmony_ci	struct pstore_private	*private, *pos;
3488c2ecf20Sopenharmony_ci	size_t			size = record->size + record->ecc_notice_size;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	if (WARN_ON(!inode_is_locked(d_inode(root))))
3518c2ecf20Sopenharmony_ci		return -EINVAL;
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	rc = -EEXIST;
3548c2ecf20Sopenharmony_ci	/* Skip records that are already present in the filesystem. */
3558c2ecf20Sopenharmony_ci	mutex_lock(&records_list_lock);
3568c2ecf20Sopenharmony_ci	list_for_each_entry(pos, &records_list, list) {
3578c2ecf20Sopenharmony_ci		if (pos->record->type == record->type &&
3588c2ecf20Sopenharmony_ci		    pos->record->id == record->id &&
3598c2ecf20Sopenharmony_ci		    pos->record->psi == record->psi)
3608c2ecf20Sopenharmony_ci			goto fail;
3618c2ecf20Sopenharmony_ci	}
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	rc = -ENOMEM;
3648c2ecf20Sopenharmony_ci	inode = pstore_get_inode(root->d_sb);
3658c2ecf20Sopenharmony_ci	if (!inode)
3668c2ecf20Sopenharmony_ci		goto fail;
3678c2ecf20Sopenharmony_ci	inode->i_mode = S_IFREG | 0444;
3688c2ecf20Sopenharmony_ci	inode->i_fop = &pstore_file_operations;
3698c2ecf20Sopenharmony_ci	scnprintf(name, sizeof(name), "%s-%s-%llu%s",
3708c2ecf20Sopenharmony_ci			pstore_type_to_name(record->type),
3718c2ecf20Sopenharmony_ci			record->psi->name, record->id,
3728c2ecf20Sopenharmony_ci			record->compressed ? ".enc.z" : "");
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	private = kzalloc(sizeof(*private), GFP_KERNEL);
3758c2ecf20Sopenharmony_ci	if (!private)
3768c2ecf20Sopenharmony_ci		goto fail_inode;
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	dentry = d_alloc_name(root, name);
3798c2ecf20Sopenharmony_ci	if (!dentry)
3808c2ecf20Sopenharmony_ci		goto fail_private;
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	private->dentry = dentry;
3838c2ecf20Sopenharmony_ci	private->record = record;
3848c2ecf20Sopenharmony_ci	inode->i_size = private->total_size = size;
3858c2ecf20Sopenharmony_ci	inode->i_private = private;
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	if (record->time.tv_sec)
3888c2ecf20Sopenharmony_ci		inode->i_mtime = inode->i_ctime = record->time;
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	d_add(dentry, inode);
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	list_add(&private->list, &records_list);
3938c2ecf20Sopenharmony_ci	mutex_unlock(&records_list_lock);
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	return 0;
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_cifail_private:
3988c2ecf20Sopenharmony_ci	free_pstore_private(private);
3998c2ecf20Sopenharmony_cifail_inode:
4008c2ecf20Sopenharmony_ci	iput(inode);
4018c2ecf20Sopenharmony_cifail:
4028c2ecf20Sopenharmony_ci	mutex_unlock(&records_list_lock);
4038c2ecf20Sopenharmony_ci	return rc;
4048c2ecf20Sopenharmony_ci}
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci/*
4078c2ecf20Sopenharmony_ci * Read all the records from the persistent store. Create
4088c2ecf20Sopenharmony_ci * files in our filesystem.  Don't warn about -EEXIST errors
4098c2ecf20Sopenharmony_ci * when we are re-scanning the backing store looking to add new
4108c2ecf20Sopenharmony_ci * error records.
4118c2ecf20Sopenharmony_ci */
4128c2ecf20Sopenharmony_civoid pstore_get_records(int quiet)
4138c2ecf20Sopenharmony_ci{
4148c2ecf20Sopenharmony_ci	struct dentry *root;
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	root = psinfo_lock_root();
4178c2ecf20Sopenharmony_ci	if (!root)
4188c2ecf20Sopenharmony_ci		return;
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	pstore_get_backend_records(psinfo, root, quiet);
4218c2ecf20Sopenharmony_ci	inode_unlock(d_inode(root));
4228c2ecf20Sopenharmony_ci}
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_cistatic int pstore_fill_super(struct super_block *sb, void *data, int silent)
4258c2ecf20Sopenharmony_ci{
4268c2ecf20Sopenharmony_ci	struct inode *inode;
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	sb->s_maxbytes		= MAX_LFS_FILESIZE;
4298c2ecf20Sopenharmony_ci	sb->s_blocksize		= PAGE_SIZE;
4308c2ecf20Sopenharmony_ci	sb->s_blocksize_bits	= PAGE_SHIFT;
4318c2ecf20Sopenharmony_ci	sb->s_magic		= PSTOREFS_MAGIC;
4328c2ecf20Sopenharmony_ci	sb->s_op		= &pstore_ops;
4338c2ecf20Sopenharmony_ci	sb->s_time_gran		= 1;
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	parse_options(data);
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	inode = pstore_get_inode(sb);
4388c2ecf20Sopenharmony_ci	if (inode) {
4398c2ecf20Sopenharmony_ci		inode->i_mode = S_IFDIR | 0750;
4408c2ecf20Sopenharmony_ci		inode->i_op = &pstore_dir_inode_operations;
4418c2ecf20Sopenharmony_ci		inode->i_fop = &simple_dir_operations;
4428c2ecf20Sopenharmony_ci		inc_nlink(inode);
4438c2ecf20Sopenharmony_ci	}
4448c2ecf20Sopenharmony_ci	sb->s_root = d_make_root(inode);
4458c2ecf20Sopenharmony_ci	if (!sb->s_root)
4468c2ecf20Sopenharmony_ci		return -ENOMEM;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	mutex_lock(&pstore_sb_lock);
4498c2ecf20Sopenharmony_ci	pstore_sb = sb;
4508c2ecf20Sopenharmony_ci	mutex_unlock(&pstore_sb_lock);
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	pstore_get_records(0);
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	return 0;
4558c2ecf20Sopenharmony_ci}
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_cistatic struct dentry *pstore_mount(struct file_system_type *fs_type,
4588c2ecf20Sopenharmony_ci	int flags, const char *dev_name, void *data)
4598c2ecf20Sopenharmony_ci{
4608c2ecf20Sopenharmony_ci	return mount_single(fs_type, flags, data, pstore_fill_super);
4618c2ecf20Sopenharmony_ci}
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_cistatic void pstore_kill_sb(struct super_block *sb)
4648c2ecf20Sopenharmony_ci{
4658c2ecf20Sopenharmony_ci	mutex_lock(&pstore_sb_lock);
4668c2ecf20Sopenharmony_ci	WARN_ON(pstore_sb && pstore_sb != sb);
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	kill_litter_super(sb);
4698c2ecf20Sopenharmony_ci	pstore_sb = NULL;
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	mutex_lock(&records_list_lock);
4728c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&records_list);
4738c2ecf20Sopenharmony_ci	mutex_unlock(&records_list_lock);
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	mutex_unlock(&pstore_sb_lock);
4768c2ecf20Sopenharmony_ci}
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_cistatic struct file_system_type pstore_fs_type = {
4798c2ecf20Sopenharmony_ci	.owner          = THIS_MODULE,
4808c2ecf20Sopenharmony_ci	.name		= "pstore",
4818c2ecf20Sopenharmony_ci	.mount		= pstore_mount,
4828c2ecf20Sopenharmony_ci	.kill_sb	= pstore_kill_sb,
4838c2ecf20Sopenharmony_ci};
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ciint __init pstore_init_fs(void)
4868c2ecf20Sopenharmony_ci{
4878c2ecf20Sopenharmony_ci	int err;
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	/* Create a convenient mount point for people to access pstore */
4908c2ecf20Sopenharmony_ci	err = sysfs_create_mount_point(fs_kobj, "pstore");
4918c2ecf20Sopenharmony_ci	if (err)
4928c2ecf20Sopenharmony_ci		goto out;
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	err = register_filesystem(&pstore_fs_type);
4958c2ecf20Sopenharmony_ci	if (err < 0)
4968c2ecf20Sopenharmony_ci		sysfs_remove_mount_point(fs_kobj, "pstore");
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ciout:
4998c2ecf20Sopenharmony_ci	return err;
5008c2ecf20Sopenharmony_ci}
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_civoid __exit pstore_exit_fs(void)
5038c2ecf20Sopenharmony_ci{
5048c2ecf20Sopenharmony_ci	unregister_filesystem(&pstore_fs_type);
5058c2ecf20Sopenharmony_ci	sysfs_remove_mount_point(fs_kobj, "pstore");
5068c2ecf20Sopenharmony_ci}
507