(file) Return to emlog.c CVS log (file) Jump to this file's LXR Page (dir) Up to [CENS] / misc / emlog

  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             * This is emlog version 0.40, released 13 August 2001
 10             * For more information see http://www.circlemud.org/~jelson/software/emlog
 11             *
 12             * $Id: emlog.c,v 1.5 2001/08/13 08:10:18 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                if (einfo->i_ino == inode->i_ino)
 60                  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 jelson 1.1   /* 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.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               * the pointer that needs to be changed... which is either the list
137               * head or one of the 'next' pointers on the list. */
138              ptr = &emlog_info_list;
139              while (*ptr != einfo) {
140                if (!*ptr) {
141                  printk("corrupt einfo list!\n");
142                  break;
143                } else
144                ptr = &((**ptr).next);
145            
146              }
147              *ptr = einfo->next;
148            
149            
150            }
151            
152            
153            
154            /************************ File Interface Functions ************************/
155 jelson 1.1 
156            
157            
158            static int emlog_open(struct inode *inode, struct file *file)
159            {
160              int minor = MINOR(inode->i_rdev);
161            
162              struct emlog_info *einfo = NULL;
163              int retval;
164            
165              if ((einfo = get_einfo(inode)) == NULL) {
166                /* never heard of this inode before... create a new record */
167                if ((retval = create_einfo(inode, minor, &einfo)) < 0)
168                  return retval;
169              }
170            
171              if (einfo == NULL) {
172                printk("BUG IN EMLOG!\n");
173                return -EIO;
174              }
175            
176 jelson 1.5   einfo->refcount++;
177 jelson 1.1   MOD_INC_USE_COUNT;
178              return 0;
179            }
180            
181            
182            /* this is called when a file is closed */
183            static int emlog_release(struct inode *inode, struct file *file)
184            {
185              struct emlog_info *einfo;
186 jelson 1.6   int retval = 0;
187 jelson 1.1 
188              /* get the buffer info */
189              if ((einfo = get_einfo(inode)) == NULL) {
190                printk("emlog: releasing unknown file!  zoinks!\n");
191                return -EINVAL;
192 jelson 1.6     goto out;
193 jelson 1.1   }
194            
195              /* decrement the reference count.  if no one has this file open and
196               * it's not holding any data, delete the record. */
197              einfo->refcount--;
198            
199 jelson 1.6   if (einfo->refcount == 0 && EMLOG_QLEN(einfo) == 0)
200 jelson 1.1     free_einfo(einfo);
201            
202 jelson 1.6  out:
203              MOD_DEC_USE_COUNT;
204              return retval;
205 jelson 1.1 }
206            
207            
208            /* read_from_emlog reads bytes out of a circular buffer with
209             * wraparound.  returns caddr_t, pointer to data read, which the
210 jelson 1.5  * caller must free.  length is (a pointer to) the number of bytes to
211             * be read, which will be set by this function to be the number of
212             * bytes actually returned */
213            caddr_t read_from_emlog(struct emlog_info *einfo, int *length, loff_t *offset)
214 jelson 1.1 {
215              caddr_t retval;
216 jelson 1.5   int bytes_copied = 0, n, start_point, remaining;
217            
218              /* is the user trying to read data that has already scrolled off? */
219              if (*offset < einfo->offset)
220                *offset = einfo->offset;
221            
222              /* is the user trying to read past EOF? */
223 jelson 1.6   if (*offset >= EMLOG_FIRST_EMPTY_BYTE(einfo))
224 jelson 1.1     return NULL;
225            
226 jelson 1.5   /* find the smaller of the total bytes we have available and what
227               * the user is asking for */
228 jelson 1.6   *length = MIN(*length, EMLOG_FIRST_EMPTY_BYTE(einfo) - *offset);
229 jelson 1.5   remaining = *length;
230            
231              /* figure out where to start based on user's offset */
232              start_point = einfo->read_point + (*offset - einfo->offset);
233              start_point = start_point % einfo->size;
234            
235              /* allocate memory to return */
236              if ((retval = kmalloc(sizeof(char) * remaining, GFP_KERNEL)) == NULL)
237 jelson 1.1     return NULL;
238            
239 jelson 1.5   /* copy the (possibly noncontiguous) data to our buffer */
240              while (remaining) {
241                n = MIN(remaining, einfo->size - start_point);
242                memcpy(retval + bytes_copied, einfo->data + start_point, n);
243 jelson 1.1     bytes_copied += n;
244 jelson 1.5     remaining -= n;
245                start_point = (start_point + n) % einfo->size;
246 jelson 1.1   }
247            
248 jelson 1.5   /* advance user's file pointer */
249              *offset += *length;
250 jelson 1.1   return retval;
251            }
252            
253            static ssize_t emlog_read(struct file *file,
254                char *buffer,    /* The buffer to fill with data */
255                size_t length,   /* The length of the buffer */
256                loff_t *offset)  /* Our offset in the file */
257            {
258 jelson 1.5   int retval;
259 jelson 1.1   caddr_t data_to_return;
260              struct emlog_info *einfo;
261            
262 jelson 1.5   /* get the metadata about this emlog */
263 jelson 1.1   if ((einfo = get_einfo(file->f_dentry->d_inode)) == NULL) {
264                printk("emlog_read: record not found\n");
265                return -EIO;
266              }
267            
268              /* wait until there's data available (unless we do nonblocking reads) */
269 jelson 1.6   while (*offset >= EMLOG_FIRST_EMPTY_BYTE(einfo)) {
270 jelson 1.1     if (file->f_flags & O_NONBLOCK)
271                  return -EAGAIN;
272            
273 jelson 1.2     interruptible_sleep_on(EMLOG_READQ(einfo));
274 jelson 1.1 
275                /* see if a signal woke us up */
276                if (signal_pending(current))
277                  return -ERESTARTSYS;
278              }
279            
280 jelson 1.5   if ((data_to_return = read_from_emlog(einfo, &length, offset)) == NULL)
281                return 0;
282 jelson 1.1 
283 jelson 1.5   if (copy_to_user(buffer, data_to_return, length) > 0)
284 jelson 1.1     retval = -EFAULT;
285              else
286 jelson 1.5     retval = length;
287 jelson 1.1   kfree(data_to_return);
288              return retval;
289            }
290            
291            
292            
293            /* write_to_emlog writes to a circular buffer with wraparound.  in the
294             * case of an overflow, it overwrites the oldest unread data. */
295            void write_to_emlog(struct emlog_info *einfo, caddr_t buf, int length)
296            {
297 jelson 1.5   int bytes_copied = 0;
298              int overflow = 0;
299              int n;
300            
301              if (length + EMLOG_QLEN(einfo) >= (einfo->size-1)) {
302                overflow = 1;
303 jelson 1.1 
304 jelson 1.5     /* in case of overflow, figure out where the new buffer will
305                 * begin.  we start by figuring out where the current buffer ENDS:
306                 * einfo->offset + EMLOG_QLEN.  we then advance the end-offset
307                 * by the length of the current write, and work backwards to
308                 * figure out what the oldest unoverwritten data will be (i.e.,
309                 * size of the buffer).  was that all quite clear? :-) */
310                einfo->offset = einfo->offset + EMLOG_QLEN(einfo) + length
311                  - einfo->size + 1;
312              }
313 jelson 1.1 
314              while (length) {
315 jelson 1.5     /* how many contiguous bytes are available from the write point to
316                 * the end of the circular buffer? */
317                n = MIN(length, einfo->size - einfo->write_point);
318 jelson 1.1     memcpy(einfo->data + einfo->write_point, buf + bytes_copied, n);
319                bytes_copied += n;
320                length -= n;
321                einfo->write_point = (einfo->write_point + n) % einfo->size;
322              }
323            
324              /* if there is an overflow, reset the read point to read whatever is
325               * the oldest data that we have, that has not yet been
326               * overwritten. */
327              if (overflow)
328                einfo->read_point = (einfo->write_point + 1) % einfo->size;
329            }
330            
331            
332            static ssize_t emlog_write(struct file *file,
333                const char *buffer,
334                size_t length,
335                loff_t *offset)
336            {
337              caddr_t message = NULL;
338              int n;
339 jelson 1.1   struct emlog_info *einfo;
340            
341 jelson 1.5   /* get the metadata about this emlog */
342 jelson 1.1   if ((einfo = get_einfo(file->f_dentry->d_inode)) == NULL)
343                return -EIO;
344            
345              /* if the message is longer than the buffer, just take the beginning
346               * of it, in hopes that the reader (if any) will have time to read
347               * before we wrap around and obliterate it */
348              n = MIN(length, einfo->size - 1);
349            
350              /* make sure we have the memory for it */
351              if ((message = kmalloc(n, GFP_KERNEL)) == NULL)
352                return -ENOMEM;
353            
354 jelson 1.5   /* copy into our temp buffer */
355 jelson 1.1   if (copy_from_user(message, buffer, n) > 0) {
356                kfree(message);
357                return -EFAULT;
358              }
359            
360 jelson 1.5   /* now copy it into the circular buffer and free our temp copy */
361 jelson 1.1   write_to_emlog(einfo, message, n);
362              kfree(message);
363 jelson 1.5 
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 
369 jelson 1.1   return n;
370            }
371            
372 jelson 1.2 
373            static unsigned int emlog_poll(struct file *file, poll_table *wait)
374            {
375              struct emlog_info *einfo;
376            
377 jelson 1.5   /* get the metadata about this emlog */
378 jelson 1.2   if ((einfo = get_einfo(file->f_dentry->d_inode)) == NULL)
379                return -EIO;
380            
381              poll_wait(file, EMLOG_READQ(einfo), wait);
382            
383 jelson 1.6   if (file->f_pos < EMLOG_FIRST_EMPTY_BYTE(einfo))
384 jelson 1.2     return POLLIN | POLLRDNORM;
385              else
386                return 0;
387            }
388            
389            
390 jelson 1.1 static struct file_operations emlog_fops = {
391 jelson 1.2   read   : emlog_read,
392              write  : emlog_write,
393              open   : emlog_open,
394              release: emlog_release,
395              poll   : emlog_poll,
396 jelson 1.1 };
397            
398            
399            int init_module(void)
400            {
401              if (register_chrdev(EMLOG_MAJOR_NUMBER, "emlog", &emlog_fops) < 0) {
402                printk("emlog: unable to register character device %d\n",
403                       EMLOG_MAJOR_NUMBER);
404                return -EIO;
405 jelson 1.6   } else {
406                printk("emlog: version %s running, using major number %d\n", EMLOG_VERSION,
407            	   EMLOG_MAJOR_NUMBER);
408 jelson 1.1   }
409            
410              return 0;
411            }
412            
413            
414            void cleanup_module(void)
415            {
416              unregister_chrdev(EMLOG_MAJOR_NUMBER, "emlog");
417            
418              /* clean up any still-allocated memory */
419              while (emlog_info_list != NULL)
420                free_einfo(emlog_info_list);
421 jelson 1.6 
422              printk("emlog: unloading\n");
423 jelson 1.1 }
424            

CENS CVS Mailing List
Powered by
ViewCVS 0.9.2