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