(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 jelson 1.8  * $Id: emlog.c,v 1.7 2001/08/13 21:29:20 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               * the pointer that needs to be changed... which is either the list
138               * head or one of the 'next' pointers on the list. */
139              ptr = &emlog_info_list;
140              while (*ptr != einfo) {
141                if (!*ptr) {
142                  printk("corrupt einfo list!\n");
143                  break;
144                } else
145                ptr = &((**ptr).next);
146            
147              }
148              *ptr = einfo->next;
149            
150            
151            }
152            
153            
154            
155            /************************ File Interface Functions ************************/
156 jelson 1.1 
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.2     interruptible_sleep_on(EMLOG_READQ(einfo));
281 jelson 1.1 
282                /* see if a signal woke us up */
283                if (signal_pending(current))
284                  return -ERESTARTSYS;
285              }
286            
287 jelson 1.5   if ((data_to_return = read_from_emlog(einfo, &length, offset)) == NULL)
288                return 0;
289 jelson 1.1 
290 jelson 1.5   if (copy_to_user(buffer, data_to_return, length) > 0)
291 jelson 1.1     retval = -EFAULT;
292              else
293 jelson 1.5     retval = length;
294 jelson 1.1   kfree(data_to_return);
295              return retval;
296            }
297            
298            
299            
300            /* write_to_emlog writes to a circular buffer with wraparound.  in the
301             * case of an overflow, it overwrites the oldest unread data. */
302            void write_to_emlog(struct emlog_info *einfo, caddr_t buf, int length)
303            {
304 jelson 1.5   int bytes_copied = 0;
305              int overflow = 0;
306              int n;
307            
308              if (length + EMLOG_QLEN(einfo) >= (einfo->size-1)) {
309                overflow = 1;
310 jelson 1.1 
311 jelson 1.5     /* in case of overflow, figure out where the new buffer will
312                 * begin.  we start by figuring out where the current buffer ENDS:
313                 * einfo->offset + EMLOG_QLEN.  we then advance the end-offset
314                 * by the length of the current write, and work backwards to
315                 * figure out what the oldest unoverwritten data will be (i.e.,
316                 * size of the buffer).  was that all quite clear? :-) */
317                einfo->offset = einfo->offset + EMLOG_QLEN(einfo) + length
318                  - einfo->size + 1;
319              }
320 jelson 1.1 
321              while (length) {
322 jelson 1.5     /* how many contiguous bytes are available from the write point to
323                 * the end of the circular buffer? */
324                n = MIN(length, einfo->size - einfo->write_point);
325 jelson 1.1     memcpy(einfo->data + einfo->write_point, buf + bytes_copied, n);
326                bytes_copied += n;
327                length -= n;
328                einfo->write_point = (einfo->write_point + n) % einfo->size;
329              }
330            
331              /* if there is an overflow, reset the read point to read whatever is
332               * the oldest data that we have, that has not yet been
333               * overwritten. */
334              if (overflow)
335                einfo->read_point = (einfo->write_point + 1) % einfo->size;
336            }
337            
338            
339            static ssize_t emlog_write(struct file *file,
340                const char *buffer,
341                size_t length,
342                loff_t *offset)
343            {
344              caddr_t message = NULL;
345              int n;
346 jelson 1.1   struct emlog_info *einfo;
347            
348 jelson 1.5   /* get the metadata about this emlog */
349 jelson 1.1   if ((einfo = get_einfo(file->f_dentry->d_inode)) == NULL)
350                return -EIO;
351            
352              /* if the message is longer than the buffer, just take the beginning
353               * of it, in hopes that the reader (if any) will have time to read
354               * before we wrap around and obliterate it */
355              n = MIN(length, einfo->size - 1);
356            
357              /* make sure we have the memory for it */
358              if ((message = kmalloc(n, GFP_KERNEL)) == NULL)
359                return -ENOMEM;
360            
361 jelson 1.5   /* copy into our temp buffer */
362 jelson 1.1   if (copy_from_user(message, buffer, n) > 0) {
363                kfree(message);
364                return -EFAULT;
365              }
366            
367 jelson 1.5   /* now copy it into the circular buffer and free our temp copy */
368 jelson 1.1   write_to_emlog(einfo, message, n);
369              kfree(message);
370 jelson 1.5 
371              /* wake up any readers that might be waiting for the data.  we call
372               * schedule in the vague hope that a reader will run before the
373               * writer's next write, to avoid losing 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