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

CENS CVS Mailing List
Powered by
ViewCVS 0.9.2