1 jelson 1.1 /*
2 * EMLOG: the EMbedded-device LOGger
3 *
|
4 jelson 1.6 * Jeremy Elson <jelson@circlemud.org>
5 * USC/Information Sciences Institute
|
6 jelson 1.1 *
|
7 jelson 1.6 * This code is released under the GPL
8 *
|
9 jelson 1.10 * This is emlog version 0.50, released XXXX
|
10 jelson 1.6 * For more information see http://www.circlemud.org/~jelson/software/emlog
11 *
|
12 jelson 1.11 * $Id: emlog.c,v 1.10 2001/12/03 17:04:07 jelson Exp $
|
13 jelson 1.1 */
14
15 #include <linux/config.h>
16 #include <linux/stddef.h>
17 #include <linux/tqueue.h>
18 #include <linux/sched.h>
19 #include <linux/kernel.h>
20 #ifdef MODULE
21 #include <linux/module.h>
|
22 jelson 1.11 #endif
23 #ifdef MODVERSIONS
24 #include <linux/modversions.h>
|
25 jelson 1.1 #endif
26 #include <linux/timer.h>
27 #include <linux/delay.h>
28 #include <linux/errno.h>
29 #include <linux/malloc.h>
|
30 jelson 1.4 #include <linux/vmalloc.h>
|
31 jelson 1.1 #include <linux/types.h>
32 #include <linux/fs.h>
33 #include <linux/poll.h>
34
35 #include <asm/uaccess.h>
36
37
38 #include "emlog.h"
39
40 MODULE_PARM(emlog_debug, "i");
41
42 struct emlog_info *emlog_info_list = NULL;
43 static int emlog_debug;
44
45 #define MIN(x, y) ((x) < (y) ? (x) : y)
46
47
48 /* find the emlog-info structure associated with an inode. returns a
49 * pointer to the structure if found, NULL if not found */
50 static struct emlog_info *get_einfo(struct inode *inode)
51 {
52 jelson 1.1 struct emlog_info *einfo;
53
54 if (inode == NULL)
55 return NULL;
56
57 for (einfo = emlog_info_list; einfo != NULL; einfo = einfo->next)
|
58 jelson 1.7 if (einfo->i_ino == inode->i_ino && einfo->i_dev == inode->i_dev)
|
59 jelson 1.1 return einfo;
60
61 return NULL;
62 }
63
64
65 /* create a new emlog buffer and its associated info structure.
66 * returns an errno on failure, or 0 on success. on success, the
67 * pointer to the new struct is passed back using peinfo */
68 static int create_einfo(struct inode *inode, int minor,
69 struct emlog_info **peinfo)
70 {
71 struct emlog_info *einfo;
72
73 /* make sure the memory requirement is legal */
74 if (minor < 1 || minor > EMLOG_MAX_SIZE)
75 return -EINVAL;
76
|
77 jelson 1.5 /* allocate space for our metadata and initialize it */
|
78 jelson 1.1 if ((einfo = kmalloc(sizeof(struct emlog_info), GFP_KERNEL)) == NULL)
79 goto struct_malloc_failed;
80
|
81 jelson 1.5 memset(einfo, 0, sizeof(struct emlog_info));
|
82 jelson 1.1 einfo->i_ino = inode->i_ino;
|
83 jelson 1.7 einfo->i_dev = inode->i_dev;
|
84 jelson 1.2
85 #if defined(DECLARE_WAIT_QUEUE_HEAD)
|
86 jelson 1.6 init_waitqueue_head(EMLOG_READQ(einfo));
|
87 jelson 1.2 #else
|
88 jelson 1.6 init_waitqueue(EMLOG_READQ(einfo));
|
89 jelson 1.2 #endif
90
|
91 jelson 1.5 /* figure out how much of a buffer this should be and allocate the buffer */
92 einfo->size = 1024 * minor;
93 if ((einfo->data = (char *) vmalloc(sizeof(char) * einfo->size)) == NULL)
94 goto data_malloc_failed;
95
|
96 jelson 1.1 /* add it to our linked list */
97 einfo->next = emlog_info_list;
98 emlog_info_list = einfo;
99
100 if (emlog_debug)
|
101 jelson 1.6 printk("allocating resources associated with inode %ld\n", einfo->i_ino);
|
102 jelson 1.1
103 /* pass the struct back */
104 *peinfo = einfo;
105 return 0;
106
|
107 jelson 1.6 #if 0
|
108 jelson 1.1 other_failure: /* if we check for other errors later, jump here */
|
109 jelson 1.6 #endif
|
110 jelson 1.4 vfree(einfo->data);
|
111 jelson 1.1 data_malloc_failed:
112 kfree(einfo);
113 struct_malloc_failed:
114 return -ENOMEM;
115 }
116
117
118 /* this frees all data associated with an emlog_info buffer, including
119 * the struct that you pass to the function. don't dereference this
120 * structure after calling free_einfo! */
121 void free_einfo(struct emlog_info *einfo)
122 {
123 struct emlog_info **ptr;
124
125 if (einfo == NULL) {
126 printk("null passed to free_einfo... which is bad\n");
127 return;
128 }
129
130 if (emlog_debug)
|
131 jelson 1.6 printk("freeing resources associated with inode %ld\n", einfo->i_ino);
|
132 jelson 1.1
|
133 jelson 1.4 vfree(einfo->data);
|
134 jelson 1.1
135 /* now delete the 'einfo' structure from the linked list. 'ptr' is
|
136 jelson 1.10 * a pointer to the pointer that needs to be changed... which is
137 * either the list head or one of the 'next' pointers on the
138 * list. */
|
139 jelson 1.1 ptr = &emlog_info_list;
140 while (*ptr != einfo) {
141 if (!*ptr) {
142 printk("corrupt einfo list!\n");
143 break;
144 } else
|
145 jelson 1.10 ptr = &((**ptr).next);
|
146 jelson 1.1 }
147 *ptr = einfo->next;
148
|
149 jelson 1.10 /* now free einfo itself */
150 kfree(einfo);
|
151 jelson 1.1 }
152
153
154
155 /************************ File Interface Functions ************************/
156
157
158
159 static int emlog_open(struct inode *inode, struct file *file)
160 {
161 int minor = MINOR(inode->i_rdev);
|
162 jelson 1.8 struct emlog_info *einfo = NULL;
163 int retval = 0;
|
164 jelson 1.1
|
165 jelson 1.8 MOD_INC_USE_COUNT;
|
166 jelson 1.1
167 if ((einfo = get_einfo(inode)) == NULL) {
168 /* never heard of this inode before... create a new record */
169 if ((retval = create_einfo(inode, minor, &einfo)) < 0)
|
170 jelson 1.8 goto out;
|
171 jelson 1.1 }
172
173 if (einfo == NULL) {
174 printk("BUG IN EMLOG!\n");
|
175 jelson 1.8 retval = -EIO;
176 goto out;
|
177 jelson 1.1 }
178
|
179 jelson 1.8 /* success! */
|
180 jelson 1.5 einfo->refcount++;
|
181 jelson 1.8
182 out:
183 if (retval < 0)
184 MOD_DEC_USE_COUNT;
185 return retval;
|
186 jelson 1.1 }
187
188
189 /* this is called when a file is closed */
190 static int emlog_release(struct inode *inode, struct file *file)
191 {
192 struct emlog_info *einfo;
|
193 jelson 1.6 int retval = 0;
|
194 jelson 1.1
195 /* get the buffer info */
196 if ((einfo = get_einfo(inode)) == NULL) {
197 printk("emlog: releasing unknown file! zoinks!\n");
198 return -EINVAL;
|
199 jelson 1.6 goto out;
|
200 jelson 1.1 }
201
202 /* decrement the reference count. if no one has this file open and
203 * it's not holding any data, delete the record. */
204 einfo->refcount--;
205
|
206 jelson 1.6 if (einfo->refcount == 0 && EMLOG_QLEN(einfo) == 0)
|
207 jelson 1.1 free_einfo(einfo);
208
|
209 jelson 1.6 out:
210 MOD_DEC_USE_COUNT;
211 return retval;
|
212 jelson 1.1 }
213
214
215 /* read_from_emlog reads bytes out of a circular buffer with
216 * wraparound. returns caddr_t, pointer to data read, which the
|
217 jelson 1.5 * caller must free. length is (a pointer to) the number of bytes to
218 * be read, which will be set by this function to be the number of
219 * bytes actually returned */
220 caddr_t read_from_emlog(struct emlog_info *einfo, int *length, loff_t *offset)
|
221 jelson 1.1 {
222 caddr_t retval;
|
223 jelson 1.5 int bytes_copied = 0, n, start_point, remaining;
224
225 /* is the user trying to read data that has already scrolled off? */
226 if (*offset < einfo->offset)
227 *offset = einfo->offset;
228
229 /* is the user trying to read past EOF? */
|
230 jelson 1.6 if (*offset >= EMLOG_FIRST_EMPTY_BYTE(einfo))
|
231 jelson 1.1 return NULL;
232
|
233 jelson 1.5 /* find the smaller of the total bytes we have available and what
234 * the user is asking for */
|
235 jelson 1.6 *length = MIN(*length, EMLOG_FIRST_EMPTY_BYTE(einfo) - *offset);
|
236 jelson 1.5 remaining = *length;
237
238 /* figure out where to start based on user's offset */
239 start_point = einfo->read_point + (*offset - einfo->offset);
240 start_point = start_point % einfo->size;
241
242 /* allocate memory to return */
243 if ((retval = kmalloc(sizeof(char) * remaining, GFP_KERNEL)) == NULL)
|
244 jelson 1.1 return NULL;
245
|
246 jelson 1.5 /* copy the (possibly noncontiguous) data to our buffer */
247 while (remaining) {
248 n = MIN(remaining, einfo->size - start_point);
249 memcpy(retval + bytes_copied, einfo->data + start_point, n);
|
250 jelson 1.1 bytes_copied += n;
|
251 jelson 1.5 remaining -= n;
252 start_point = (start_point + n) % einfo->size;
|
253 jelson 1.1 }
254
|
255 jelson 1.5 /* advance user's file pointer */
256 *offset += *length;
|
257 jelson 1.1 return retval;
258 }
259
260 static ssize_t emlog_read(struct file *file,
261 char *buffer, /* The buffer to fill with data */
262 size_t length, /* The length of the buffer */
263 loff_t *offset) /* Our offset in the file */
264 {
|
265 jelson 1.5 int retval;
|
266 jelson 1.1 caddr_t data_to_return;
267 struct emlog_info *einfo;
268
|
269 jelson 1.5 /* get the metadata about this emlog */
|
270 jelson 1.1 if ((einfo = get_einfo(file->f_dentry->d_inode)) == NULL) {
271 printk("emlog_read: record not found\n");
272 return -EIO;
273 }
274
275 /* wait until there's data available (unless we do nonblocking reads) */
|
276 jelson 1.6 while (*offset >= EMLOG_FIRST_EMPTY_BYTE(einfo)) {
|
277 jelson 1.1 if (file->f_flags & O_NONBLOCK)
278 return -EAGAIN;
279
|
280 jelson 1.9 /* TODO - unroll sleep_on to make it atomic, like fs/pipe.c */
|
281 jelson 1.2 interruptible_sleep_on(EMLOG_READQ(einfo));
|
282 jelson 1.1
283 /* see if a signal woke us up */
284 if (signal_pending(current))
285 return -ERESTARTSYS;
286 }
287
|
288 jelson 1.5 if ((data_to_return = read_from_emlog(einfo, &length, offset)) == NULL)
289 return 0;
|
290 jelson 1.1
|
291 jelson 1.5 if (copy_to_user(buffer, data_to_return, length) > 0)
|
292 jelson 1.1 retval = -EFAULT;
293 else
|
294 jelson 1.5 retval = length;
|
295 jelson 1.1 kfree(data_to_return);
296 return retval;
297 }
298
299
300
301 /* write_to_emlog writes to a circular buffer with wraparound. in the
302 * case of an overflow, it overwrites the oldest unread data. */
303 void write_to_emlog(struct emlog_info *einfo, caddr_t buf, int length)
304 {
|
305 jelson 1.5 int bytes_copied = 0;
306 int overflow = 0;
307 int n;
308
309 if (length + EMLOG_QLEN(einfo) >= (einfo->size-1)) {
310 overflow = 1;
|
311 jelson 1.1
|
312 jelson 1.5 /* in case of overflow, figure out where the new buffer will
313 * begin. we start by figuring out where the current buffer ENDS:
314 * einfo->offset + EMLOG_QLEN. we then advance the end-offset
315 * by the length of the current write, and work backwards to
316 * figure out what the oldest unoverwritten data will be (i.e.,
317 * size of the buffer). was that all quite clear? :-) */
318 einfo->offset = einfo->offset + EMLOG_QLEN(einfo) + length
319 - einfo->size + 1;
320 }
|
321 jelson 1.1
322 while (length) {
|
323 jelson 1.5 /* how many contiguous bytes are available from the write point to
324 * the end of the circular buffer? */
325 n = MIN(length, einfo->size - einfo->write_point);
|
326 jelson 1.1 memcpy(einfo->data + einfo->write_point, buf + bytes_copied, n);
327 bytes_copied += n;
328 length -= n;
329 einfo->write_point = (einfo->write_point + n) % einfo->size;
330 }
331
332 /* if there is an overflow, reset the read point to read whatever is
333 * the oldest data that we have, that has not yet been
334 * overwritten. */
335 if (overflow)
336 einfo->read_point = (einfo->write_point + 1) % einfo->size;
337 }
338
339
340 static ssize_t emlog_write(struct file *file,
341 const char *buffer,
342 size_t length,
343 loff_t *offset)
344 {
345 caddr_t message = NULL;
346 int n;
347 jelson 1.1 struct emlog_info *einfo;
348
|
349 jelson 1.5 /* get the metadata about this emlog */
|
350 jelson 1.1 if ((einfo = get_einfo(file->f_dentry->d_inode)) == NULL)
351 return -EIO;
352
353 /* if the message is longer than the buffer, just take the beginning
354 * of it, in hopes that the reader (if any) will have time to read
355 * before we wrap around and obliterate it */
356 n = MIN(length, einfo->size - 1);
357
358 /* make sure we have the memory for it */
359 if ((message = kmalloc(n, GFP_KERNEL)) == NULL)
360 return -ENOMEM;
361
|
362 jelson 1.5 /* copy into our temp buffer */
|
363 jelson 1.1 if (copy_from_user(message, buffer, n) > 0) {
364 kfree(message);
365 return -EFAULT;
366 }
367
|
368 jelson 1.5 /* now copy it into the circular buffer and free our temp copy */
|
369 jelson 1.1 write_to_emlog(einfo, message, n);
370 kfree(message);
|
371 jelson 1.5
|
372 jelson 1.9 /* wake up any readers that might be waiting for the data */
|
373 jelson 1.2 wake_up_interruptible(EMLOG_READQ(einfo));
|
374 jelson 1.5
|
375 jelson 1.1 return n;
376 }
377
|
378 jelson 1.2
379 static unsigned int emlog_poll(struct file *file, poll_table *wait)
380 {
381 struct emlog_info *einfo;
382
|
383 jelson 1.5 /* get the metadata about this emlog */
|
384 jelson 1.2 if ((einfo = get_einfo(file->f_dentry->d_inode)) == NULL)
385 return -EIO;
386
387 poll_wait(file, EMLOG_READQ(einfo), wait);
388
|
389 jelson 1.6 if (file->f_pos < EMLOG_FIRST_EMPTY_BYTE(einfo))
|
390 jelson 1.2 return POLLIN | POLLRDNORM;
391 else
392 return 0;
393 }
394
395
|
396 jelson 1.1 static struct file_operations emlog_fops = {
|
397 jelson 1.2 read : emlog_read,
398 write : emlog_write,
399 open : emlog_open,
400 release: emlog_release,
401 poll : emlog_poll,
|
402 jelson 1.1 };
403
404
405 int init_module(void)
406 {
407 if (register_chrdev(EMLOG_MAJOR_NUMBER, "emlog", &emlog_fops) < 0) {
408 printk("emlog: unable to register character device %d\n",
409 EMLOG_MAJOR_NUMBER);
410 return -EIO;
|
411 jelson 1.6 } else {
412 printk("emlog: version %s running, using major number %d\n", EMLOG_VERSION,
413 EMLOG_MAJOR_NUMBER);
|
414 jelson 1.1 }
415
416 return 0;
417 }
418
419
420 void cleanup_module(void)
421 {
422 unregister_chrdev(EMLOG_MAJOR_NUMBER, "emlog");
423
424 /* clean up any still-allocated memory */
425 while (emlog_info_list != NULL)
426 free_einfo(emlog_info_list);
|
427 jelson 1.6
428 printk("emlog: unloading\n");
|
429 jelson 1.1 }
430
|