1 jelson 1.1 /*
2 * EMLOG: the EMbedded-device LOGger
3 *
4 * Jeremy Elson
5 * USC/ISI
6 *
|
7 jelson 1.5 * $Id: emlog.c,v 1.4 2001/08/13 06:01:31 jelson Exp $
|
8 jelson 1.1 */
9
|
10 jelson 1.3 #ifdef MODVERSIONS
11 #include <linux/modversions.h>
12 #endif
|
13 jelson 1.1
14 #include <linux/config.h>
15 #include <linux/stddef.h>
16 #include <linux/tqueue.h>
17 #include <linux/sched.h>
18 #include <linux/kernel.h>
19 #ifdef MODULE
20 #include <linux/module.h>
21 #endif
22 #include <linux/timer.h>
23 #include <linux/delay.h>
24 #include <linux/errno.h>
25 #include <linux/malloc.h>
|
26 jelson 1.4 #include <linux/vmalloc.h>
|
27 jelson 1.1 #include <linux/types.h>
28 #include <linux/fs.h>
29 #include <linux/poll.h>
30
31 #include <asm/uaccess.h>
32
33
34 #include "emlog.h"
35
36 MODULE_PARM(emlog_debug, "i");
37
38 struct emlog_info *emlog_info_list = NULL;
39 static int emlog_debug;
40
41 #define MIN(x, y) ((x) < (y) ? (x) : y)
42
43
44 /* find the emlog-info structure associated with an inode. returns a
45 * pointer to the structure if found, NULL if not found */
46 static struct emlog_info *get_einfo(struct inode *inode)
47 {
48 jelson 1.1 struct emlog_info *einfo;
49
50 if (inode == NULL)
51 return NULL;
52
53 for (einfo = emlog_info_list; einfo != NULL; einfo = einfo->next)
54 if (einfo->i_ino == inode->i_ino)
55 return einfo;
56
57 return NULL;
58 }
59
60
61 /* create a new emlog buffer and its associated info structure.
62 * returns an errno on failure, or 0 on success. on success, the
63 * pointer to the new struct is passed back using peinfo */
64 static int create_einfo(struct inode *inode, int minor,
65 struct emlog_info **peinfo)
66 {
67 struct emlog_info *einfo;
68
69 jelson 1.1 /* make sure the memory requirement is legal */
70 if (minor < 1 || minor > EMLOG_MAX_SIZE)
71 return -EINVAL;
72
|
73 jelson 1.5 /* allocate space for our metadata and initialize it */
|
74 jelson 1.1 if ((einfo = kmalloc(sizeof(struct emlog_info), GFP_KERNEL)) == NULL)
75 goto struct_malloc_failed;
76
|
77 jelson 1.5 memset(einfo, 0, sizeof(struct emlog_info));
|
78 jelson 1.1 einfo->i_ino = inode->i_ino;
|
79 jelson 1.2
80 #if defined(DECLARE_WAIT_QUEUE_HEAD)
81 init_waitqueue_head(&einfo->read_q);
82 #else
83 init_waitqueue(&einfo->read_q);
84 #endif
85
|
86 jelson 1.5 /* figure out how much of a buffer this should be and allocate the buffer */
87 einfo->size = 1024 * minor;
88 if ((einfo->data = (char *) vmalloc(sizeof(char) * einfo->size)) == NULL)
89 goto data_malloc_failed;
90
|
91 jelson 1.1 /* 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 jelson 1.4 vfree(einfo->data);
|
104 jelson 1.1 data_malloc_failed:
105 kfree(einfo);
106 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 jelson 1.1
|
126 jelson 1.4 vfree(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.5 einfo->refcount++;
|
170 jelson 1.1 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 einfo->refcount--;
191 jelson 1.1
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 jelson 1.5 * caller must free. length is (a pointer to) the number of bytes to
202 * be read, which will be set by this function to be the number of
203 * bytes actually returned */
204 caddr_t read_from_emlog(struct emlog_info *einfo, int *length, loff_t *offset)
|
205 jelson 1.1 {
206 caddr_t retval;
|
207 jelson 1.5 int bytes_copied = 0, n, start_point, remaining;
208
209 /* is the user trying to read data that has already scrolled off? */
210 if (*offset < einfo->offset)
211 *offset = einfo->offset;
212
213 /* is the user trying to read past EOF? */
214 if (*offset >= einfo->offset + EMLOG_QLEN(einfo))
|
215 jelson 1.1 return NULL;
216
|
217 jelson 1.5 /* find the smaller of the total bytes we have available and what
218 * the user is asking for */
219 *length = MIN(*length, EMLOG_QLEN(einfo) - (*offset - einfo->offset));
220 remaining = *length;
221
222 /* figure out where to start based on user's offset */
223 start_point = einfo->read_point + (*offset - einfo->offset);
224 start_point = start_point % einfo->size;
225
226 /* allocate memory to return */
227 if ((retval = kmalloc(sizeof(char) * remaining, GFP_KERNEL)) == NULL)
|
228 jelson 1.1 return NULL;
229
|
230 jelson 1.5 /* copy the (possibly noncontiguous) data to our buffer */
231 while (remaining) {
232 n = MIN(remaining, einfo->size - start_point);
233 memcpy(retval + bytes_copied, einfo->data + start_point, n);
|
234 jelson 1.1 bytes_copied += n;
|
235 jelson 1.5 remaining -= n;
236 start_point = (start_point + n) % einfo->size;
|
237 jelson 1.1 }
238
|
239 jelson 1.5 /* advance user's file pointer */
240 *offset += *length;
|
241 jelson 1.1 return retval;
242 }
243
244 static ssize_t emlog_read(struct file *file,
245 char *buffer, /* The buffer to fill with data */
246 size_t length, /* The length of the buffer */
247 loff_t *offset) /* Our offset in the file */
248 {
|
249 jelson 1.5 int retval;
|
250 jelson 1.1 caddr_t data_to_return;
251 struct emlog_info *einfo;
252
|
253 jelson 1.5 printk("got a read at offset %d\n", *offset);
254
255 /* get the metadata about this emlog */
|
256 jelson 1.1 if ((einfo = get_einfo(file->f_dentry->d_inode)) == NULL) {
257 printk("emlog_read: record not found\n");
258 return -EIO;
259 }
260
|
261 jelson 1.5 #if 0
|
262 jelson 1.1 /* wait until there's data available (unless we do nonblocking reads) */
263 while (EMLOG_EMPTY(einfo)) {
264 if (file->f_flags & O_NONBLOCK)
265 return -EAGAIN;
266
|
267 jelson 1.2 interruptible_sleep_on(EMLOG_READQ(einfo));
|
268 jelson 1.1
269 /* see if a signal woke us up */
270 if (signal_pending(current))
271 return -ERESTARTSYS;
272 }
|
273 jelson 1.5 #endif
|
274 jelson 1.1
|
275 jelson 1.5 if ((data_to_return = read_from_emlog(einfo, &length, offset)) == NULL)
276 return 0;
|
277 jelson 1.1
|
278 jelson 1.5 if (copy_to_user(buffer, data_to_return, length) > 0)
|
279 jelson 1.1 retval = -EFAULT;
280 else
|
281 jelson 1.5 retval = length;
|
282 jelson 1.1 kfree(data_to_return);
283 return retval;
284 }
285
286
287
288 /* write_to_emlog writes to a circular buffer with wraparound. in the
289 * case of an overflow, it overwrites the oldest unread data. */
290 void write_to_emlog(struct emlog_info *einfo, caddr_t buf, int length)
291 {
292 caddr_t retval;
|
293 jelson 1.5 int bytes_copied = 0;
294 int overflow = 0;
295 int n;
296
297 if (length + EMLOG_QLEN(einfo) >= (einfo->size-1)) {
298 overflow = 1;
|
299 jelson 1.1
|
300 jelson 1.5 /* in case of overflow, figure out where the new buffer will
301 * begin. we start by figuring out where the current buffer ENDS:
302 * einfo->offset + EMLOG_QLEN. we then advance the end-offset
303 * by the length of the current write, and work backwards to
304 * figure out what the oldest unoverwritten data will be (i.e.,
305 * size of the buffer). was that all quite clear? :-) */
306 einfo->offset = einfo->offset + EMLOG_QLEN(einfo) + length
307 - einfo->size + 1;
308 }
|
309 jelson 1.1
310 while (length) {
|
311 jelson 1.5 /* how many contiguous bytes are available from the write point to
312 * the end of the circular buffer? */
313 n = MIN(length, einfo->size - einfo->write_point);
|
314 jelson 1.1 memcpy(einfo->data + einfo->write_point, buf + bytes_copied, n);
315 bytes_copied += n;
316 length -= n;
317 einfo->write_point = (einfo->write_point + n) % einfo->size;
318 }
319
320 /* if there is an overflow, reset the read point to read whatever is
321 * the oldest data that we have, that has not yet been
322 * overwritten. */
323 if (overflow)
324 einfo->read_point = (einfo->write_point + 1) % einfo->size;
325 }
326
327
328 static ssize_t emlog_write(struct file *file,
329 const char *buffer,
330 size_t length,
331 loff_t *offset)
332 {
333 caddr_t message = NULL;
334 int n;
335 jelson 1.1 struct emlog_info *einfo;
336
|
337 jelson 1.5 /* get the metadata about this emlog */
|
338 jelson 1.1 if ((einfo = get_einfo(file->f_dentry->d_inode)) == NULL)
339 return -EIO;
340
341 /* if the message is longer than the buffer, just take the beginning
342 * of it, in hopes that the reader (if any) will have time to read
343 * before we wrap around and obliterate it */
344 n = MIN(length, einfo->size - 1);
345
346 /* make sure we have the memory for it */
347 if ((message = kmalloc(n, GFP_KERNEL)) == NULL)
348 return -ENOMEM;
349
|
350 jelson 1.5 /* copy into our temp buffer */
|
351 jelson 1.1 if (copy_from_user(message, buffer, n) > 0) {
352 kfree(message);
353 return -EFAULT;
354 }
355
|
356 jelson 1.5 /* now copy it into the circular buffer and free our temp copy */
|
357 jelson 1.1 write_to_emlog(einfo, message, n);
358 kfree(message);
|
359 jelson 1.5
360 /* update the size in the inode, so tail -f works */
361 file->f_dentry->d_inode->i_size = EMLOG_QLEN(einfo) + einfo->offset - 1;
362 printk("size now %d\n", file->f_dentry->d_inode->i_size );
363
364 /* wake up any readers that might be waiting for the data. we call
365 * schedule in the vague hope that a reader will run before the
366 * writer's next write, to avoid losing data. */
|
367 jelson 1.2 wake_up_interruptible(EMLOG_READQ(einfo));
|
368 jelson 1.5 schedule();
369
|
370 jelson 1.1 return n;
371 }
372
|
373 jelson 1.2
374 static unsigned int emlog_poll(struct file *file, poll_table *wait)
375 {
376 struct emlog_info *einfo;
377
|
378 jelson 1.5 /* get the metadata about this emlog */
|
379 jelson 1.2 if ((einfo = get_einfo(file->f_dentry->d_inode)) == NULL)
380 return -EIO;
381
382 poll_wait(file, EMLOG_READQ(einfo), wait);
383
384 if (!EMLOG_EMPTY(einfo))
385 return POLLIN | POLLRDNORM;
386 else
387 return 0;
388 }
389
390
|
391 jelson 1.1 static struct file_operations emlog_fops = {
|
392 jelson 1.2 read : emlog_read,
393 write : emlog_write,
394 open : emlog_open,
395 release: emlog_release,
396 poll : emlog_poll,
|
397 jelson 1.1 };
398
399
400 int init_module(void)
401 {
402 if (register_chrdev(EMLOG_MAJOR_NUMBER, "emlog", &emlog_fops) < 0) {
403 printk("emlog: unable to register character device %d\n",
404 EMLOG_MAJOR_NUMBER);
405 return -EIO;
406 }
407
408 return 0;
409 }
410
411
412 void cleanup_module(void)
413 {
414 unregister_chrdev(EMLOG_MAJOR_NUMBER, "emlog");
415
416 /* clean up any still-allocated memory */
417 while (emlog_info_list != NULL)
418 jelson 1.1 free_einfo(emlog_info_list);
419 }
|
420 jelson 1.2
421
|
422 jelson 1.1
|