(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 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.12  * $Id: emlog.c,v 1.11 2002/03/08 20:59:47 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 jelson 1.12 #include <linux/slab.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 jelson 1.12 MODULE_LICENSE("GPL");
 41 jelson 1.1  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               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             

CENS CVS Mailing List
Powered by
ViewCVS 0.9.2