(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.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             

CENS CVS Mailing List
Powered by
ViewCVS 0.9.2