(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             * Jeremy Elson
  5             * USC/ISI
  6             *
  7 jelson 1.5  * $Id: emlog.c,v 1.4 2001/08/13 06:01:31 jelson Exp $
  8 jelson 1.1  */
  9            
 10 jelson 1.3 #ifdef MODVERSIONS
 11            #include <linux/modversions.h>
 12            #endif
 13 jelson 1.1 
 14            #include <linux/config.h>
 15            #include <linux/stddef.h>
 16            #include <linux/tqueue.h>
 17            #include <linux/sched.h>
 18            #include <linux/kernel.h>
 19            #ifdef MODULE
 20            #include <linux/module.h>
 21            #endif
 22            #include <linux/timer.h>
 23            #include <linux/delay.h>
 24            #include <linux/errno.h>
 25            #include <linux/malloc.h>
 26 jelson 1.4 #include <linux/vmalloc.h>
 27 jelson 1.1 #include <linux/types.h>
 28            #include <linux/fs.h>
 29            #include <linux/poll.h>
 30            
 31            #include <asm/uaccess.h>
 32            
 33            
 34            #include "emlog.h"
 35            
 36            MODULE_PARM(emlog_debug, "i");
 37            
 38            struct emlog_info *emlog_info_list = NULL;
 39            static int emlog_debug;
 40            
 41            #define MIN(x, y) ((x) < (y) ? (x) : y)
 42            
 43            
 44            /* find the emlog-info structure associated with an inode.  returns a
 45             * pointer to the structure if found, NULL if not found */
 46            static struct emlog_info *get_einfo(struct inode *inode)
 47            {
 48 jelson 1.1   struct emlog_info *einfo;
 49            
 50              if (inode == NULL)
 51                return NULL;
 52            
 53              for (einfo = emlog_info_list; einfo != NULL; einfo = einfo->next)
 54                if (einfo->i_ino == inode->i_ino)
 55                  return einfo;
 56            
 57              return NULL;
 58            }
 59            
 60            
 61            /* create a new emlog buffer and its associated info structure.
 62             * returns an errno on failure, or 0 on success.  on success, the
 63             * pointer to the new struct is passed back using peinfo */
 64            static int create_einfo(struct inode *inode, int minor,
 65                                    struct emlog_info **peinfo)
 66            {
 67              struct emlog_info *einfo;
 68            
 69 jelson 1.1   /* make sure the memory requirement is legal */
 70              if (minor < 1 || minor > EMLOG_MAX_SIZE)
 71                return -EINVAL;
 72            
 73 jelson 1.5   /* allocate space for our metadata and initialize it */
 74 jelson 1.1   if ((einfo = kmalloc(sizeof(struct emlog_info), GFP_KERNEL)) == NULL)
 75                goto struct_malloc_failed;
 76            
 77 jelson 1.5   memset(einfo, 0, sizeof(struct emlog_info));
 78 jelson 1.1   einfo->i_ino = inode->i_ino;
 79 jelson 1.2 
 80            #if defined(DECLARE_WAIT_QUEUE_HEAD)
 81              init_waitqueue_head(&einfo->read_q);
 82            #else
 83              init_waitqueue(&einfo->read_q);
 84            #endif
 85            
 86 jelson 1.5   /* figure out how much of a buffer this should be and allocate the buffer */
 87              einfo->size = 1024 * minor;
 88              if ((einfo->data = (char *) vmalloc(sizeof(char) * einfo->size)) == NULL)
 89                goto data_malloc_failed;
 90            
 91 jelson 1.1   /* add it to our linked list */
 92              einfo->next = emlog_info_list;
 93              emlog_info_list = einfo;
 94            
 95              if (emlog_debug)
 96                printk("allocating resources associated with inode %d\n", einfo->i_ino);
 97            
 98              /* pass the struct back */
 99              *peinfo = einfo;
100              return 0;
101            
102             other_failure: /* if we check for other errors later, jump here */
103 jelson 1.4   vfree(einfo->data);
104 jelson 1.1  data_malloc_failed:
105              kfree(einfo);
106             struct_malloc_failed:
107              return -ENOMEM;
108            }
109            
110            
111            /* this frees all data associated with an emlog_info buffer, including
112             * the struct that you pass to the function.  don't dereference this
113             * structure after calling free_einfo! */
114            void free_einfo(struct emlog_info *einfo)
115            {
116              struct emlog_info **ptr;
117            
118              if (einfo == NULL) {
119                printk("null passed to free_einfo... which is bad\n");
120                return;
121              }
122            
123              if (emlog_debug)
124                printk("freeing resources associated with inode %d\n", einfo->i_ino);
125 jelson 1.1 
126 jelson 1.4   vfree(einfo->data);
127 jelson 1.1 
128              /* now delete the 'einfo' structure from the linked list.  'ptr' is
129               * the pointer that needs to be changed... which is either the list
130               * head or one of the 'next' pointers on the list. */
131              ptr = &emlog_info_list;
132              while (*ptr != einfo) {
133                if (!*ptr) {
134                  printk("corrupt einfo list!\n");
135                  break;
136                } else
137                ptr = &((**ptr).next);
138            
139              }
140              *ptr = einfo->next;
141            
142            
143            }
144            
145            
146            
147            /************************ File Interface Functions ************************/
148 jelson 1.1 
149            
150            
151            static int emlog_open(struct inode *inode, struct file *file)
152            {
153              int minor = MINOR(inode->i_rdev);
154            
155              struct emlog_info *einfo = NULL;
156              int retval;
157            
158              if ((einfo = get_einfo(inode)) == NULL) {
159                /* never heard of this inode before... create a new record */
160                if ((retval = create_einfo(inode, minor, &einfo)) < 0)
161                  return retval;
162              }
163            
164              if (einfo == NULL) {
165                printk("BUG IN EMLOG!\n");
166                return -EIO;
167              }
168            
169 jelson 1.5   einfo->refcount++;
170 jelson 1.1   MOD_INC_USE_COUNT;
171              return 0;
172            }
173            
174            
175            /* this is called when a file is closed */
176            static int emlog_release(struct inode *inode, struct file *file)
177            {
178              struct emlog_info *einfo;
179            
180              MOD_DEC_USE_COUNT;
181            
182              /* get the buffer info */
183              if ((einfo = get_einfo(inode)) == NULL) {
184                printk("emlog: releasing unknown file!  zoinks!\n");
185                return -EINVAL;
186              }
187            
188              /* decrement the reference count.  if no one has this file open and
189               * it's not holding any data, delete the record. */
190              einfo->refcount--;
191 jelson 1.1 
192              if (einfo->refcount == 0 && EMLOG_EMPTY(einfo))
193                free_einfo(einfo);
194            
195              return 0;
196            }
197            
198            
199            /* read_from_emlog reads bytes out of a circular buffer with
200             * wraparound.  returns caddr_t, pointer to data read, which the
201 jelson 1.5  * caller must free.  length is (a pointer to) the number of bytes to
202             * be read, which will be set by this function to be the number of
203             * bytes actually returned */
204            caddr_t read_from_emlog(struct emlog_info *einfo, int *length, loff_t *offset)
205 jelson 1.1 {
206              caddr_t retval;
207 jelson 1.5   int bytes_copied = 0, n, start_point, remaining;
208            
209              /* is the user trying to read data that has already scrolled off? */
210              if (*offset < einfo->offset)
211                *offset = einfo->offset;
212            
213              /* is the user trying to read past EOF? */
214              if (*offset >= einfo->offset + EMLOG_QLEN(einfo))
215 jelson 1.1     return NULL;
216            
217 jelson 1.5   /* find the smaller of the total bytes we have available and what
218               * the user is asking for */
219              *length = MIN(*length, EMLOG_QLEN(einfo) - (*offset - einfo->offset));
220              remaining = *length;
221            
222              /* figure out where to start based on user's offset */
223              start_point = einfo->read_point + (*offset - einfo->offset);
224              start_point = start_point % einfo->size;
225            
226              /* allocate memory to return */
227              if ((retval = kmalloc(sizeof(char) * remaining, GFP_KERNEL)) == NULL)
228 jelson 1.1     return NULL;
229            
230 jelson 1.5   /* copy the (possibly noncontiguous) data to our buffer */
231              while (remaining) {
232                n = MIN(remaining, einfo->size - start_point);
233                memcpy(retval + bytes_copied, einfo->data + start_point, n);
234 jelson 1.1     bytes_copied += n;
235 jelson 1.5     remaining -= n;
236                start_point = (start_point + n) % einfo->size;
237 jelson 1.1   }
238            
239 jelson 1.5   /* advance user's file pointer */
240              *offset += *length;
241 jelson 1.1   return retval;
242            }
243            
244            static ssize_t emlog_read(struct file *file,
245                char *buffer,    /* The buffer to fill with data */
246                size_t length,   /* The length of the buffer */
247                loff_t *offset)  /* Our offset in the file */
248            {
249 jelson 1.5   int retval;
250 jelson 1.1   caddr_t data_to_return;
251              struct emlog_info *einfo;
252            
253 jelson 1.5   printk("got a read at offset %d\n", *offset);
254            
255              /* get the metadata about this emlog */
256 jelson 1.1   if ((einfo = get_einfo(file->f_dentry->d_inode)) == NULL) {
257                printk("emlog_read: record not found\n");
258                return -EIO;
259              }
260            
261 jelson 1.5 #if 0
262 jelson 1.1   /* wait until there's data available (unless we do nonblocking reads) */
263              while (EMLOG_EMPTY(einfo)) {
264                if (file->f_flags & O_NONBLOCK)
265                  return -EAGAIN;
266            
267 jelson 1.2     interruptible_sleep_on(EMLOG_READQ(einfo));
268 jelson 1.1 
269                /* see if a signal woke us up */
270                if (signal_pending(current))
271                  return -ERESTARTSYS;
272              }
273 jelson 1.5 #endif
274 jelson 1.1 
275 jelson 1.5   if ((data_to_return = read_from_emlog(einfo, &length, offset)) == NULL)
276                return 0;
277 jelson 1.1 
278 jelson 1.5   if (copy_to_user(buffer, data_to_return, length) > 0)
279 jelson 1.1     retval = -EFAULT;
280              else
281 jelson 1.5     retval = length;
282 jelson 1.1   kfree(data_to_return);
283              return retval;
284            }
285            
286            
287            
288            /* write_to_emlog writes to a circular buffer with wraparound.  in the
289             * case of an overflow, it overwrites the oldest unread data. */
290            void write_to_emlog(struct emlog_info *einfo, caddr_t buf, int length)
291            {
292              caddr_t retval;
293 jelson 1.5   int bytes_copied = 0;
294              int overflow = 0;
295              int n;
296            
297              if (length + EMLOG_QLEN(einfo) >= (einfo->size-1)) {
298                overflow = 1;
299 jelson 1.1 
300 jelson 1.5     /* in case of overflow, figure out where the new buffer will
301                 * begin.  we start by figuring out where the current buffer ENDS:
302                 * einfo->offset + EMLOG_QLEN.  we then advance the end-offset
303                 * by the length of the current write, and work backwards to
304                 * figure out what the oldest unoverwritten data will be (i.e.,
305                 * size of the buffer).  was that all quite clear? :-) */
306                einfo->offset = einfo->offset + EMLOG_QLEN(einfo) + length
307                  - einfo->size + 1;
308              }
309 jelson 1.1 
310              while (length) {
311 jelson 1.5     /* how many contiguous bytes are available from the write point to
312                 * the end of the circular buffer? */
313                n = MIN(length, einfo->size - einfo->write_point);
314 jelson 1.1     memcpy(einfo->data + einfo->write_point, buf + bytes_copied, n);
315                bytes_copied += n;
316                length -= n;
317                einfo->write_point = (einfo->write_point + n) % einfo->size;
318              }
319            
320              /* if there is an overflow, reset the read point to read whatever is
321               * the oldest data that we have, that has not yet been
322               * overwritten. */
323              if (overflow)
324                einfo->read_point = (einfo->write_point + 1) % einfo->size;
325            }
326            
327            
328            static ssize_t emlog_write(struct file *file,
329                const char *buffer,
330                size_t length,
331                loff_t *offset)
332            {
333              caddr_t message = NULL;
334              int n;
335 jelson 1.1   struct emlog_info *einfo;
336            
337 jelson 1.5   /* get the metadata about this emlog */
338 jelson 1.1   if ((einfo = get_einfo(file->f_dentry->d_inode)) == NULL)
339                return -EIO;
340            
341              /* if the message is longer than the buffer, just take the beginning
342               * of it, in hopes that the reader (if any) will have time to read
343               * before we wrap around and obliterate it */
344              n = MIN(length, einfo->size - 1);
345            
346              /* make sure we have the memory for it */
347              if ((message = kmalloc(n, GFP_KERNEL)) == NULL)
348                return -ENOMEM;
349            
350 jelson 1.5   /* copy into our temp buffer */
351 jelson 1.1   if (copy_from_user(message, buffer, n) > 0) {
352                kfree(message);
353                return -EFAULT;
354              }
355            
356 jelson 1.5   /* now copy it into the circular buffer and free our temp copy */
357 jelson 1.1   write_to_emlog(einfo, message, n);
358              kfree(message);
359 jelson 1.5 
360              /* update the size in the inode, so tail -f works */
361              file->f_dentry->d_inode->i_size = EMLOG_QLEN(einfo) + einfo->offset - 1;
362              printk("size now %d\n",   file->f_dentry->d_inode->i_size );
363            
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   schedule();
369            
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              if (!EMLOG_EMPTY(einfo))
385                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              }
407            
408              return 0;
409            }
410            
411            
412            void cleanup_module(void)
413            {
414              unregister_chrdev(EMLOG_MAJOR_NUMBER, "emlog");
415            
416              /* clean up any still-allocated memory */
417              while (emlog_info_list != NULL)
418 jelson 1.1     free_einfo(emlog_info_list);
419            }
420 jelson 1.2 
421            
422 jelson 1.1 

CENS CVS Mailing List
Powered by
ViewCVS 0.9.2