.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * Persistent Storage - ramfs parts. |
---|
3 | 4 | * |
---|
4 | 5 | * Copyright (C) 2010 Intel Corporation <tony.luck@intel.com> |
---|
5 | | - * |
---|
6 | | - * This program is free software; you can redistribute it and/or modify |
---|
7 | | - * it under the terms of the GNU General Public License version 2 as |
---|
8 | | - * published by the Free Software Foundation. |
---|
9 | | - * |
---|
10 | | - * This program is distributed in the hope that it will be useful, |
---|
11 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
12 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
13 | | - * GNU General Public License for more details. |
---|
14 | | - * |
---|
15 | | - * You should have received a copy of the GNU General Public License |
---|
16 | | - * along with this program; if not, write to the Free Software |
---|
17 | | - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
---|
18 | 6 | */ |
---|
19 | 7 | |
---|
20 | 8 | #include <linux/module.h> |
---|
.. | .. |
---|
34 | 22 | #include <linux/magic.h> |
---|
35 | 23 | #include <linux/pstore.h> |
---|
36 | 24 | #include <linux/slab.h> |
---|
37 | | -#include <linux/spinlock.h> |
---|
38 | 25 | #include <linux/uaccess.h> |
---|
39 | 26 | |
---|
40 | 27 | #ifdef CONFIG_PSTORE_BOOT_LOG |
---|
.. | .. |
---|
46 | 33 | |
---|
47 | 34 | #define PSTORE_NAMELEN 64 |
---|
48 | 35 | |
---|
49 | | -static DEFINE_SPINLOCK(allpstore_lock); |
---|
50 | | -static LIST_HEAD(allpstore); |
---|
| 36 | +static DEFINE_MUTEX(records_list_lock); |
---|
| 37 | +static LIST_HEAD(records_list); |
---|
| 38 | + |
---|
| 39 | +static DEFINE_MUTEX(pstore_sb_lock); |
---|
| 40 | +static struct super_block *pstore_sb; |
---|
51 | 41 | |
---|
52 | 42 | struct pstore_private { |
---|
53 | 43 | struct list_head list; |
---|
| 44 | + struct dentry *dentry; |
---|
54 | 45 | struct pstore_record *record; |
---|
55 | 46 | size_t total_size; |
---|
56 | 47 | }; |
---|
.. | .. |
---|
123 | 114 | |
---|
124 | 115 | rec = (struct pstore_ftrace_record *)(ps->record->buf + data->off); |
---|
125 | 116 | |
---|
126 | | - seq_printf(s, "CPU:%d ts:%llu %08lx %08lx %pf <- %pF\n", |
---|
| 117 | + seq_printf(s, "CPU:%d ts:%llu %08lx %08lx %ps <- %pS\n", |
---|
127 | 118 | pstore_ftrace_decode_cpu(rec), |
---|
128 | 119 | pstore_ftrace_read_timestamp(rec), |
---|
129 | 120 | rec->ip, rec->parent_ip, (void *)rec->ip, |
---|
.. | .. |
---|
145 | 136 | struct seq_file *sf = file->private_data; |
---|
146 | 137 | struct pstore_private *ps = sf->private; |
---|
147 | 138 | #ifdef CONFIG_PSTORE_BOOT_LOG |
---|
| 139 | + size_t size = 0; |
---|
148 | 140 | struct pstore_record *record = ps->record; |
---|
149 | | - struct ramoops_context *cxt = record->psi->data; |
---|
150 | | - struct persistent_ram_zone *prz; |
---|
151 | | - struct persistent_ram_buffer *buffer; |
---|
152 | | - char *log_tmp; |
---|
153 | | - size_t size, start, n; |
---|
154 | 141 | |
---|
155 | | - if (ps->record->type == PSTORE_TYPE_BOOT_LOG) { |
---|
156 | | - |
---|
157 | | - if (!cxt) |
---|
158 | | - return count; |
---|
159 | | - |
---|
160 | | - prz = cxt->boot_przs[record->id]; |
---|
161 | | - |
---|
162 | | - if (!prz) |
---|
163 | | - return count; |
---|
164 | | - |
---|
165 | | - buffer = prz->buffer; |
---|
166 | | - if (!buffer) |
---|
167 | | - return count; |
---|
168 | | - |
---|
169 | | - size = atomic_read(&buffer->size); |
---|
170 | | - start = atomic_read(&buffer->start); |
---|
171 | | - |
---|
172 | | - log_tmp = kmalloc(size, GFP_KERNEL); |
---|
173 | | - if (!log_tmp) |
---|
174 | | - return count; |
---|
175 | | - memcpy_fromio(log_tmp, &buffer->data[start], size - start); |
---|
176 | | - memcpy_fromio(log_tmp + size - start, &buffer->data[0], start); |
---|
177 | | - |
---|
178 | | - n = simple_read_from_buffer(userbuf, count, ppos, log_tmp, size); |
---|
179 | | - kfree(log_tmp); |
---|
180 | | - return n; |
---|
| 142 | + if (record->type == PSTORE_TYPE_BOOT_LOG) { |
---|
| 143 | + size = ramoops_pstore_read_for_boot_log(ps->record); |
---|
| 144 | + size = simple_read_from_buffer(userbuf, count, ppos, record->buf, size); |
---|
| 145 | + return size; |
---|
181 | 146 | } |
---|
182 | 147 | #endif |
---|
183 | 148 | if (ps->record->type == PSTORE_TYPE_FTRACE) |
---|
.. | .. |
---|
230 | 195 | { |
---|
231 | 196 | struct pstore_private *p = d_inode(dentry)->i_private; |
---|
232 | 197 | struct pstore_record *record = p->record; |
---|
| 198 | + int rc = 0; |
---|
233 | 199 | |
---|
234 | 200 | if (!record->psi->erase) |
---|
235 | 201 | return -EPERM; |
---|
| 202 | + |
---|
| 203 | + /* Make sure we can't race while removing this file. */ |
---|
| 204 | + mutex_lock(&records_list_lock); |
---|
| 205 | + if (!list_empty(&p->list)) |
---|
| 206 | + list_del_init(&p->list); |
---|
| 207 | + else |
---|
| 208 | + rc = -ENOENT; |
---|
| 209 | + p->dentry = NULL; |
---|
| 210 | + mutex_unlock(&records_list_lock); |
---|
| 211 | + if (rc) |
---|
| 212 | + return rc; |
---|
236 | 213 | |
---|
237 | 214 | mutex_lock(&record->psi->read_mutex); |
---|
238 | 215 | record->psi->erase(record); |
---|
.. | .. |
---|
244 | 221 | static void pstore_evict_inode(struct inode *inode) |
---|
245 | 222 | { |
---|
246 | 223 | struct pstore_private *p = inode->i_private; |
---|
247 | | - unsigned long flags; |
---|
248 | 224 | |
---|
249 | 225 | clear_inode(inode); |
---|
250 | | - if (p) { |
---|
251 | | - spin_lock_irqsave(&allpstore_lock, flags); |
---|
252 | | - list_del(&p->list); |
---|
253 | | - spin_unlock_irqrestore(&allpstore_lock, flags); |
---|
254 | | - free_pstore_private(p); |
---|
255 | | - } |
---|
| 226 | + free_pstore_private(p); |
---|
256 | 227 | } |
---|
257 | 228 | |
---|
258 | 229 | static const struct inode_operations pstore_dir_inode_operations = { |
---|
.. | .. |
---|
330 | 301 | .show_options = pstore_show_options, |
---|
331 | 302 | }; |
---|
332 | 303 | |
---|
333 | | -static struct super_block *pstore_sb; |
---|
334 | | - |
---|
335 | | -bool pstore_is_mounted(void) |
---|
| 304 | +static struct dentry *psinfo_lock_root(void) |
---|
336 | 305 | { |
---|
337 | | - return pstore_sb != NULL; |
---|
| 306 | + struct dentry *root; |
---|
| 307 | + |
---|
| 308 | + mutex_lock(&pstore_sb_lock); |
---|
| 309 | + /* |
---|
| 310 | + * Having no backend is fine -- no records appear. |
---|
| 311 | + * Not being mounted is fine -- nothing to do. |
---|
| 312 | + */ |
---|
| 313 | + if (!psinfo || !pstore_sb) { |
---|
| 314 | + mutex_unlock(&pstore_sb_lock); |
---|
| 315 | + return NULL; |
---|
| 316 | + } |
---|
| 317 | + |
---|
| 318 | + root = pstore_sb->s_root; |
---|
| 319 | + inode_lock(d_inode(root)); |
---|
| 320 | + mutex_unlock(&pstore_sb_lock); |
---|
| 321 | + |
---|
| 322 | + return root; |
---|
| 323 | +} |
---|
| 324 | + |
---|
| 325 | +int pstore_put_backend_records(struct pstore_info *psi) |
---|
| 326 | +{ |
---|
| 327 | + struct pstore_private *pos, *tmp; |
---|
| 328 | + struct dentry *root; |
---|
| 329 | + int rc = 0; |
---|
| 330 | + |
---|
| 331 | + root = psinfo_lock_root(); |
---|
| 332 | + if (!root) |
---|
| 333 | + return 0; |
---|
| 334 | + |
---|
| 335 | + mutex_lock(&records_list_lock); |
---|
| 336 | + list_for_each_entry_safe(pos, tmp, &records_list, list) { |
---|
| 337 | + if (pos->record->psi == psi) { |
---|
| 338 | + list_del_init(&pos->list); |
---|
| 339 | + rc = simple_unlink(d_inode(root), pos->dentry); |
---|
| 340 | + if (WARN_ON(rc)) |
---|
| 341 | + break; |
---|
| 342 | + d_drop(pos->dentry); |
---|
| 343 | + dput(pos->dentry); |
---|
| 344 | + pos->dentry = NULL; |
---|
| 345 | + } |
---|
| 346 | + } |
---|
| 347 | + mutex_unlock(&records_list_lock); |
---|
| 348 | + |
---|
| 349 | + inode_unlock(d_inode(root)); |
---|
| 350 | + |
---|
| 351 | + return rc; |
---|
338 | 352 | } |
---|
339 | 353 | |
---|
340 | 354 | /* |
---|
.. | .. |
---|
349 | 363 | int rc = 0; |
---|
350 | 364 | char name[PSTORE_NAMELEN]; |
---|
351 | 365 | struct pstore_private *private, *pos; |
---|
352 | | - unsigned long flags; |
---|
353 | 366 | size_t size = record->size + record->ecc_notice_size; |
---|
354 | 367 | |
---|
355 | | - WARN_ON(!inode_is_locked(d_inode(root))); |
---|
| 368 | + if (WARN_ON(!inode_is_locked(d_inode(root)))) |
---|
| 369 | + return -EINVAL; |
---|
356 | 370 | |
---|
357 | | - spin_lock_irqsave(&allpstore_lock, flags); |
---|
358 | | - list_for_each_entry(pos, &allpstore, list) { |
---|
| 371 | + rc = -EEXIST; |
---|
| 372 | + /* Skip records that are already present in the filesystem. */ |
---|
| 373 | + mutex_lock(&records_list_lock); |
---|
| 374 | + list_for_each_entry(pos, &records_list, list) { |
---|
359 | 375 | if (pos->record->type == record->type && |
---|
360 | 376 | pos->record->id == record->id && |
---|
361 | | - pos->record->psi == record->psi) { |
---|
362 | | - rc = -EEXIST; |
---|
363 | | - break; |
---|
364 | | - } |
---|
| 377 | + pos->record->psi == record->psi) |
---|
| 378 | + goto fail; |
---|
365 | 379 | } |
---|
366 | | - spin_unlock_irqrestore(&allpstore_lock, flags); |
---|
367 | | - if (rc) |
---|
368 | | - return rc; |
---|
369 | 380 | |
---|
370 | 381 | rc = -ENOMEM; |
---|
371 | 382 | inode = pstore_get_inode(root->d_sb); |
---|
.. | .. |
---|
373 | 384 | goto fail; |
---|
374 | 385 | inode->i_mode = S_IFREG | 0444; |
---|
375 | 386 | inode->i_fop = &pstore_file_operations; |
---|
376 | | - |
---|
377 | | - switch (record->type) { |
---|
378 | | - case PSTORE_TYPE_DMESG: |
---|
379 | | - scnprintf(name, sizeof(name), "dmesg-%s-%llu%s", |
---|
380 | | - record->psi->name, record->id, |
---|
381 | | - record->compressed ? ".enc.z" : ""); |
---|
382 | | - break; |
---|
383 | | - case PSTORE_TYPE_CONSOLE: |
---|
384 | | - scnprintf(name, sizeof(name), "console-%s-%llu", |
---|
385 | | - record->psi->name, record->id); |
---|
386 | | - break; |
---|
387 | | - case PSTORE_TYPE_FTRACE: |
---|
388 | | - scnprintf(name, sizeof(name), "ftrace-%s-%llu", |
---|
389 | | - record->psi->name, record->id); |
---|
390 | | - break; |
---|
391 | | - case PSTORE_TYPE_MCE: |
---|
392 | | - scnprintf(name, sizeof(name), "mce-%s-%llu", |
---|
393 | | - record->psi->name, record->id); |
---|
394 | | - break; |
---|
395 | | - case PSTORE_TYPE_PPC_RTAS: |
---|
396 | | - scnprintf(name, sizeof(name), "rtas-%s-%llu", |
---|
397 | | - record->psi->name, record->id); |
---|
398 | | - break; |
---|
399 | | - case PSTORE_TYPE_PPC_OF: |
---|
400 | | - scnprintf(name, sizeof(name), "powerpc-ofw-%s-%llu", |
---|
401 | | - record->psi->name, record->id); |
---|
402 | | - break; |
---|
403 | | - case PSTORE_TYPE_PPC_COMMON: |
---|
404 | | - scnprintf(name, sizeof(name), "powerpc-common-%s-%llu", |
---|
405 | | - record->psi->name, record->id); |
---|
406 | | - break; |
---|
407 | | - case PSTORE_TYPE_PMSG: |
---|
408 | | - scnprintf(name, sizeof(name), "pmsg-%s-%llu", |
---|
409 | | - record->psi->name, record->id); |
---|
410 | | - break; |
---|
411 | | - case PSTORE_TYPE_PPC_OPAL: |
---|
412 | | - scnprintf(name, sizeof(name), "powerpc-opal-%s-%llu", |
---|
413 | | - record->psi->name, record->id); |
---|
414 | | - break; |
---|
415 | | -#ifdef CONFIG_PSTORE_BOOT_LOG |
---|
416 | | - case PSTORE_TYPE_BOOT_LOG: |
---|
417 | | - scnprintf(name, sizeof(name), "boot-log-%llu", record->id); |
---|
418 | | - break; |
---|
419 | | -#endif |
---|
420 | | - case PSTORE_TYPE_UNKNOWN: |
---|
421 | | - scnprintf(name, sizeof(name), "unknown-%s-%llu", |
---|
422 | | - record->psi->name, record->id); |
---|
423 | | - break; |
---|
424 | | - default: |
---|
425 | | - scnprintf(name, sizeof(name), "type%d-%s-%llu", |
---|
426 | | - record->type, record->psi->name, record->id); |
---|
427 | | - break; |
---|
428 | | - } |
---|
| 387 | + scnprintf(name, sizeof(name), "%s-%s-%llu%s", |
---|
| 388 | + pstore_type_to_name(record->type), |
---|
| 389 | + record->psi->name, record->id, |
---|
| 390 | + record->compressed ? ".enc.z" : ""); |
---|
429 | 391 | |
---|
430 | 392 | private = kzalloc(sizeof(*private), GFP_KERNEL); |
---|
431 | 393 | if (!private) |
---|
.. | .. |
---|
435 | 397 | if (!dentry) |
---|
436 | 398 | goto fail_private; |
---|
437 | 399 | |
---|
| 400 | + private->dentry = dentry; |
---|
438 | 401 | private->record = record; |
---|
439 | 402 | inode->i_size = private->total_size = size; |
---|
440 | 403 | inode->i_private = private; |
---|
.. | .. |
---|
444 | 407 | |
---|
445 | 408 | d_add(dentry, inode); |
---|
446 | 409 | |
---|
447 | | - spin_lock_irqsave(&allpstore_lock, flags); |
---|
448 | | - list_add(&private->list, &allpstore); |
---|
449 | | - spin_unlock_irqrestore(&allpstore_lock, flags); |
---|
| 410 | + list_add(&private->list, &records_list); |
---|
| 411 | + mutex_unlock(&records_list_lock); |
---|
450 | 412 | |
---|
451 | 413 | return 0; |
---|
452 | 414 | |
---|
.. | .. |
---|
454 | 416 | free_pstore_private(private); |
---|
455 | 417 | fail_inode: |
---|
456 | 418 | iput(inode); |
---|
457 | | - |
---|
458 | 419 | fail: |
---|
| 420 | + mutex_unlock(&records_list_lock); |
---|
459 | 421 | return rc; |
---|
460 | 422 | } |
---|
461 | 423 | |
---|
.. | .. |
---|
467 | 429 | */ |
---|
468 | 430 | void pstore_get_records(int quiet) |
---|
469 | 431 | { |
---|
470 | | - struct pstore_info *psi = psinfo; |
---|
471 | 432 | struct dentry *root; |
---|
472 | 433 | |
---|
473 | | - if (!psi || !pstore_sb) |
---|
| 434 | + root = psinfo_lock_root(); |
---|
| 435 | + if (!root) |
---|
474 | 436 | return; |
---|
475 | 437 | |
---|
476 | | - root = pstore_sb->s_root; |
---|
477 | | - |
---|
478 | | - inode_lock(d_inode(root)); |
---|
479 | | - pstore_get_backend_records(psi, root, quiet); |
---|
| 438 | + pstore_get_backend_records(psinfo, root, quiet); |
---|
480 | 439 | inode_unlock(d_inode(root)); |
---|
481 | 440 | } |
---|
482 | 441 | |
---|
483 | 442 | static int pstore_fill_super(struct super_block *sb, void *data, int silent) |
---|
484 | 443 | { |
---|
485 | 444 | struct inode *inode; |
---|
486 | | - |
---|
487 | | - pstore_sb = sb; |
---|
488 | 445 | |
---|
489 | 446 | sb->s_maxbytes = MAX_LFS_FILESIZE; |
---|
490 | 447 | sb->s_blocksize = PAGE_SIZE; |
---|
.. | .. |
---|
506 | 463 | if (!sb->s_root) |
---|
507 | 464 | return -ENOMEM; |
---|
508 | 465 | |
---|
| 466 | + mutex_lock(&pstore_sb_lock); |
---|
| 467 | + pstore_sb = sb; |
---|
| 468 | + mutex_unlock(&pstore_sb_lock); |
---|
| 469 | + |
---|
509 | 470 | pstore_get_records(0); |
---|
510 | 471 | |
---|
511 | 472 | return 0; |
---|
.. | .. |
---|
519 | 480 | |
---|
520 | 481 | static void pstore_kill_sb(struct super_block *sb) |
---|
521 | 482 | { |
---|
| 483 | + mutex_lock(&pstore_sb_lock); |
---|
| 484 | + WARN_ON(pstore_sb && pstore_sb != sb); |
---|
| 485 | + |
---|
522 | 486 | kill_litter_super(sb); |
---|
523 | 487 | pstore_sb = NULL; |
---|
| 488 | + |
---|
| 489 | + mutex_lock(&records_list_lock); |
---|
| 490 | + INIT_LIST_HEAD(&records_list); |
---|
| 491 | + mutex_unlock(&records_list_lock); |
---|
| 492 | + |
---|
| 493 | + mutex_unlock(&pstore_sb_lock); |
---|
524 | 494 | } |
---|
525 | 495 | |
---|
526 | 496 | static struct file_system_type pstore_fs_type = { |
---|