Blame view

kernel/linux-rt-4.4.41/fs/pstore/pmsg.c 2.72 KB
5113f6f70   김현기   kernel add
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
  /*
   * Copyright 2014  Google, Inc.
   *
   * This software is licensed under the terms of the GNU General Public
   * License version 2, as published by the Free Software Foundation, and
   * may be copied, distributed, and modified under those terms.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   */
  
  #include <linux/cdev.h>
  #include <linux/device.h>
  #include <linux/fs.h>
  #include <linux/uaccess.h>
  #include <linux/vmalloc.h>
  #include "internal.h"
  
  static DEFINE_MUTEX(pmsg_lock);
  #define PMSG_MAX_BOUNCE_BUFFER_SIZE (2*PAGE_SIZE)
  
  static ssize_t write_pmsg(struct file *file, const char __user *buf,
  			  size_t count, loff_t *ppos)
  {
  	size_t i, buffer_size;
  	char *buffer;
  
  	if (!count)
  		return 0;
  
  	if (!access_ok(VERIFY_READ, buf, count))
  		return -EFAULT;
  
  	buffer_size = count;
  	if (buffer_size > PMSG_MAX_BOUNCE_BUFFER_SIZE)
  		buffer_size = PMSG_MAX_BOUNCE_BUFFER_SIZE;
  	buffer = vmalloc(buffer_size);
  	if (!buffer)
  		return -ENOMEM;
  
  	mutex_lock(&pmsg_lock);
  	for (i = 0; i < count; ) {
  		size_t c = min(count - i, buffer_size);
  		u64 id;
  		long ret;
  
  		ret = __copy_from_user(buffer, buf + i, c);
  		if (unlikely(ret != 0)) {
  			mutex_unlock(&pmsg_lock);
  			vfree(buffer);
  			return -EFAULT;
  		}
  		psinfo->write_buf(PSTORE_TYPE_PMSG, 0, &id, 0, buffer, 0, c,
  				  psinfo);
  
  		i += c;
  	}
  
  	mutex_unlock(&pmsg_lock);
  	vfree(buffer);
  	return count;
  }
  
  static const struct file_operations pmsg_fops = {
  	.owner		= THIS_MODULE,
  	.llseek		= noop_llseek,
  	.write		= write_pmsg,
  };
  
  static struct class *pmsg_class;
  static int pmsg_major;
  #define PMSG_NAME "pmsg"
  #undef pr_fmt
  #define pr_fmt(fmt) PMSG_NAME ": " fmt
  
  static char *pmsg_devnode(struct device *dev, umode_t *mode)
  {
  	if (mode)
  		*mode = 0220;
  	return NULL;
  }
  
  void pstore_register_pmsg(void)
  {
  	struct device *pmsg_device;
  
  	pmsg_major = register_chrdev(0, PMSG_NAME, &pmsg_fops);
  	if (pmsg_major < 0) {
  		pr_err("register_chrdev failed
  ");
  		goto err;
  	}
  
  	pmsg_class = class_create(THIS_MODULE, PMSG_NAME);
  	if (IS_ERR(pmsg_class)) {
  		pr_err("device class file already in use
  ");
  		goto err_class;
  	}
  	pmsg_class->devnode = pmsg_devnode;
  
  	pmsg_device = device_create(pmsg_class, NULL, MKDEV(pmsg_major, 0),
  					NULL, "%s%d", PMSG_NAME, 0);
  	if (IS_ERR(pmsg_device)) {
  		pr_err("failed to create device
  ");
  		goto err_device;
  	}
  	return;
  
  err_device:
  	class_destroy(pmsg_class);
  err_class:
  	unregister_chrdev(pmsg_major, PMSG_NAME);
  err:
  	return;
  }
  
  void pstore_unregister_pmsg(void)
  {
  	device_destroy(pmsg_class, MKDEV(pmsg_major, 0));
  	class_destroy(pmsg_class);
  	unregister_chrdev(pmsg_major, PMSG_NAME);
  }