1 jelson 1.1 /*
2 * EMLOG: the EMbedded-device LOGger
3 *
4 * Jeremy Elson
5 * USC/ISI
6 *
7 * $Id$
8 */
9
10
11 #include <linux/config.h>
12 #include <linux/stddef.h>
13 #include <linux/tqueue.h>
14 #include <linux/sched.h>
15 #include <linux/kernel.h>
16 #ifdef MODULE
17 #include <linux/module.h>
18 #endif
19 #include <linux/timer.h>
20 #include <linux/delay.h>
21 #include <linux/errno.h>
22 jelson 1.1 #include <linux/malloc.h>
23 #include <linux/types.h>
24 #include <linux/fs.h>
25 #include <linux/poll.h>
26
27 /* network interface headers */
28 #include <linux/netdevice.h>
29 #include <linux/etherdevice.h>
30 #include <linux/skbuff.h>
31 #include <linux/random.h>
32
33 #include <asm/uaccess.h>
34
35
36 #include "emlog.h"
37
38 MODULE_PARM(emlog_debug, "i");
39
40 struct emlog_info *emlog_info_list = NULL;
41 static struct wait_queue *emlog_read_wait = NULL;
42 static int emlog_debug;
43 jelson 1.1
44 #define MIN(x, y) ((x) < (y) ? (x) : y)
45
46
47 /* find the emlog-info structure associated with an inode. returns a
48 * pointer to the structure if found, NULL if not found */
49 static struct emlog_info *get_einfo(struct inode *inode)
50 {
51 struct emlog_info *einfo;
52
53 if (inode == NULL)
54 return NULL;
55
56 for (einfo = emlog_info_list; einfo != NULL; einfo = einfo->next)
57 if (einfo->i_ino == inode->i_ino)
58 return einfo;
59
60 return NULL;
61 }
62
63
64 jelson 1.1 /* create a new emlog buffer and its associated info structure.
65 * returns an errno on failure, or 0 on success. on success, the
66 * pointer to the new struct is passed back using peinfo */
67 static int create_einfo(struct inode *inode, int minor,
68 struct emlog_info **peinfo)
69 {
70 struct emlog_info *einfo;
71
72 /* make sure the memory requirement is legal */
73 if (minor < 1 || minor > EMLOG_MAX_SIZE)
74 return -EINVAL;
75
76 /* allocate space for our metadata */
77 if ((einfo = kmalloc(sizeof(struct emlog_info), GFP_KERNEL)) == NULL)
78 goto struct_malloc_failed;
79
80 /* figure out how much of a buffer this should be and allocate the buffer */
81 einfo->size = 1024 * minor;
82 if ((einfo->data = kmalloc(sizeof(char)*einfo->size, GFP_KERNEL))==NULL)
83 goto data_malloc_failed;
84
85 jelson 1.1 /* init the rest of the structure */
86 einfo->i_ino = inode->i_ino;
87 einfo->refcount = 0;
88 einfo->read_point = 0;
89 einfo->write_point = 0;
90
91 /* add it to our linked list */
92 einfo->next = emlog_info_list;
93 emlog_info_list = einfo;
94
95 if (emlog_debug)
96 printk("allocating resources associated with inode %d\n", einfo->i_ino);
97
98 /* pass the struct back */
99 *peinfo = einfo;
100 return 0;
101
102 other_failure: /* if we check for other errors later, jump here */
103 kfree(einfo->data);
104 data_malloc_failed:
105 kfree(einfo);
106 jelson 1.1 struct_malloc_failed:
107 return -ENOMEM;
108 }
109
110
111 /* this frees all data associated with an emlog_info buffer, including
112 * the struct that you pass to the function. don't dereference this
113 * structure after calling free_einfo! */
114 void free_einfo(struct emlog_info *einfo)
115 {
116 struct emlog_info **ptr;
117
118 if (einfo == NULL) {
119 printk("null passed to free_einfo... which is bad\n");
120 return;
121 }
122
123 if (emlog_debug)
124 printk("freeing resources associated with inode %d\n", einfo->i_ino);
125
126 kfree(einfo->data);
127 jelson 1.1
128 /* now delete the 'einfo' structure from the linked list. 'ptr' is
129 * the pointer that needs to be changed... which is either the list
130 * head or one of the 'next' pointers on the list. */
131 ptr = &emlog_info_list;
132 while (*ptr != einfo) {
133 if (!*ptr) {
134 printk("corrupt einfo list!\n");
135 break;
136 } else
137 ptr = &((**ptr).next);
138
139 }
140 *ptr = einfo->next;
141
142
143 }
144
145
146
147 /************************ File Interface Functions ************************/
148 jelson 1.1
149
150
151 static int emlog_open(struct inode *inode, struct file *file)
152 {
153 int minor = MINOR(inode->i_rdev);
154
155 struct emlog_info *einfo = NULL;
156 int retval;
157
158 if ((einfo = get_einfo(inode)) == NULL) {
159 /* never heard of this inode before... create a new record */
160 if ((retval = create_einfo(inode, minor, &einfo)) < 0)
161 return retval;
162 }
163
164 if (einfo == NULL) {
165 printk("BUG IN EMLOG!\n");
166 return -EIO;
167 }
168
169 jelson 1.1 EMLOG_REFCOUNT(einfo)++;
170 MOD_INC_USE_COUNT;
171 return 0;
172 }
173
174
175 /* this is called when a file is closed */
176 static int emlog_release(struct inode *inode, struct file *file)
177 {
178 struct emlog_info *einfo;
179
180 MOD_DEC_USE_COUNT;
181
182 /* get the buffer info */
183 if ((einfo = get_einfo(inode)) == NULL) {
184 printk("emlog: releasing unknown file! zoinks!\n");
185 return -EINVAL;
186 }
187
188 /* decrement the reference count. if no one has this file open and
189 * it's not holding any data, delete the record. */
190 jelson 1.1 einfo->refcount--;
191
192 if (einfo->refcount == 0 && EMLOG_EMPTY(einfo))
193 free_einfo(einfo);
194
195 return 0;
196 }
197
198
199 /* read_from_emlog reads bytes out of a circular buffer with
200 * wraparound. returns caddr_t, pointer to data read, which the
201 * caller must free. length is the number of bytes to be read, which
202 * we assume is <= the number of bytes available. */
203 caddr_t read_from_emlog(struct emlog_info *einfo, int length)
204 {
205 caddr_t retval;
206 int bytes_copied = 0, n;
207
208 if (length > EMLOG_SIZE(einfo)) {
209 printk("emlog: trying to read more (%d) than we have (%d)\n",
210 length, EMLOG_SIZE(einfo));
211 jelson 1.1 return NULL;
212 }
213
214 if ((retval = kmalloc(sizeof(char) * length, GFP_KERNEL)) == NULL)
215 return NULL;
216
217 while (length) {
218 n = MIN(length, einfo->size - einfo->read_point /* contiguous bytes */);
219 memcpy(retval + bytes_copied, einfo->data + einfo->read_point, n);
220 bytes_copied += n;
221 length -= n;
222 einfo->read_point = (einfo->read_point + n) % einfo->size;
223 }
224
225 return retval;
226 }
227
228 static ssize_t emlog_read(struct file *file,
229 char *buffer, /* The buffer to fill with data */
230 size_t length, /* The length of the buffer */
231 loff_t *offset) /* Our offset in the file */
232 jelson 1.1 {
233 int n, retval;
234 caddr_t data_to_return;
235 struct emlog_info *einfo;
236
237 if ((einfo = get_einfo(file->f_dentry->d_inode)) == NULL) {
238 printk("emlog_read: record not found\n");
239 return -EIO;
240 }
241
242 /* wait until there's data available (unless we do nonblocking reads) */
243 while (EMLOG_EMPTY(einfo)) {
244 if (file->f_flags & O_NONBLOCK)
245 return -EAGAIN;
246
247 interruptible_sleep_on(&emlog_read_wait);
248
249 /* see if a signal woke us up */
250 if (signal_pending(current))
251 return -ERESTARTSYS;
252 }
253 jelson 1.1
254 /* read the data out of the internal buffer. the following two
255 * statements must be must be atomic with respect to
256 * emlog_info... okay since syscalls are not interrupted */
257 n = MIN(length, EMLOG_SIZE(einfo));
258 if ((data_to_return = read_from_emlog(einfo, n)) == NULL)
259 return -EIO;
260
261 /* another thread might run here if we block accessing userspace (in
262 * copy_to_user). this is why i remove the message from the read
263 * queue before copying it back to the user -- to preserve the
264 * atomicity of read_from_emlog. otherwise, a block here might
265 * cause the same data to be returned to two different threads
266 * trying to read from the same device.
267 *
268 * note that this function is reentrant by virtue of the fact that
269 * each thread has its own stack, and we keep the message
270 * temporarily buffered in local variables (i.e. on the stack) */
271 if (copy_to_user(buffer, data_to_return, n) > 0)
272 retval = -EFAULT;
273 else
274 jelson 1.1 retval = n;
275 kfree(data_to_return);
276 return retval;
277 }
278
279
280
281 /* write_to_emlog writes to a circular buffer with wraparound. in the
282 * case of an overflow, it overwrites the oldest unread data. */
283 void write_to_emlog(struct emlog_info *einfo, caddr_t buf, int length)
284 {
285 caddr_t retval;
286 int bytes_copied = 0, n;
287 int overflow;
288
289 overflow = length + EMLOG_SIZE(einfo) > einfo->size;
290
291 while (length) {
292 n = MIN(length, einfo->size - einfo->write_point /* contiguous bytes */);
293 memcpy(einfo->data + einfo->write_point, buf + bytes_copied, n);
294 bytes_copied += n;
295 jelson 1.1 length -= n;
296 einfo->write_point = (einfo->write_point + n) % einfo->size;
297 }
298
299 /* if there is an overflow, reset the read point to read whatever is
300 * the oldest data that we have, that has not yet been
301 * overwritten. */
302 if (overflow)
303 einfo->read_point = (einfo->write_point + 1) % einfo->size;
304 }
305
306
307 static ssize_t emlog_write(struct file *file,
308 const char *buffer,
309 size_t length,
310 loff_t *offset)
311 {
312 caddr_t message = NULL;
313 int n;
314 struct emlog_info *einfo;
315
316 jelson 1.1 if ((einfo = get_einfo(file->f_dentry->d_inode)) == NULL)
317 return -EIO;
318
319 /* if the message is longer than the buffer, just take the beginning
320 * of it, in hopes that the reader (if any) will have time to read
321 * before we wrap around and obliterate it */
322 n = MIN(length, einfo->size - 1);
323
324 /* make sure we have the memory for it */
325 if ((message = kmalloc(n, GFP_KERNEL)) == NULL)
326 return -ENOMEM;
327
328 if (copy_from_user(message, buffer, n) > 0) {
329 kfree(message);
330 return -EFAULT;
331 }
332
333 /* another thread might run here if we end up blocking on the
334 * user-space access, so it's important for reentrancy that the
335 * "message" variable is local. see note about atomicity and
336 * reentrancy in emlog_read. */
337 jelson 1.1 write_to_emlog(einfo, message, n);
338 kfree(message);
339 wake_up_interruptible(&emlog_read_wait);
340 schedule(); /* hope that a reader wakes up! */
341 return n;
342 }
343
344 static struct file_operations emlog_fops = {
345 NULL, /* lseek */
346 emlog_read, /* read */
347 emlog_write, /* write */
348 NULL, /* readdir */
349 NULL, /* poll */
350 NULL, /* ioctl */
351 NULL, /* mmap */
352 emlog_open, /* open */
353 NULL, /* flush */
354 emlog_release, /* release */
355 NULL, /* fsync */
356 NULL /* fasync */
357 };
358 jelson 1.1
359
360 int init_module(void)
361 {
362 if (register_chrdev(EMLOG_MAJOR_NUMBER, "emlog", &emlog_fops) < 0) {
363 printk("emlog: unable to register character device %d\n",
364 EMLOG_MAJOR_NUMBER);
365 return -EIO;
366 }
367
368 return 0;
369 }
370
371
372 void cleanup_module(void)
373 {
374 unregister_chrdev(EMLOG_MAJOR_NUMBER, "emlog");
375
376 /* clean up any still-allocated memory */
377 while (emlog_info_list != NULL)
378 free_einfo(emlog_info_list);
379 jelson 1.1 }
380
|