(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             * $Id$
  8             */
  9            
 10            
 11            #include <linux/config.h>
 12            #include <linux/stddef.h>
 13            #include <linux/tqueue.h>
 14            #include <linux/sched.h>
 15            #include <linux/kernel.h>
 16            #ifdef MODULE
 17            #include <linux/module.h>
 18            #endif
 19            #include <linux/timer.h>
 20            #include <linux/delay.h>
 21            #include <linux/errno.h>
 22 jelson 1.1 #include <linux/malloc.h>
 23            #include <linux/types.h>
 24            #include <linux/fs.h>
 25            #include <linux/poll.h>
 26            
 27            /* network interface headers */
 28            #include <linux/netdevice.h>
 29            #include <linux/etherdevice.h>
 30            #include <linux/skbuff.h>
 31            #include <linux/random.h>
 32            
 33            #include <asm/uaccess.h>
 34            
 35            
 36            #include "emlog.h"
 37            
 38            MODULE_PARM(emlog_debug, "i");
 39            
 40            struct emlog_info *emlog_info_list = NULL;
 41            static struct wait_queue *emlog_read_wait = NULL;
 42            static int emlog_debug;
 43 jelson 1.1 
 44            #define MIN(x, y) ((x) < (y) ? (x) : y)
 45            
 46            
 47            /* find the emlog-info structure associated with an inode.  returns a
 48             * pointer to the structure if found, NULL if not found */
 49            static struct emlog_info *get_einfo(struct inode *inode)
 50            {
 51              struct emlog_info *einfo;
 52            
 53              if (inode == NULL)
 54                return NULL;
 55            
 56              for (einfo = emlog_info_list; einfo != NULL; einfo = einfo->next)
 57                if (einfo->i_ino == inode->i_ino)
 58                  return einfo;
 59            
 60              return NULL;
 61            }
 62            
 63            
 64 jelson 1.1 /* create a new emlog buffer and its associated info structure.
 65             * returns an errno on failure, or 0 on success.  on success, the
 66             * pointer to the new struct is passed back using peinfo */
 67            static int create_einfo(struct inode *inode, int minor,
 68                                    struct emlog_info **peinfo)
 69            {
 70              struct emlog_info *einfo;
 71            
 72              /* make sure the memory requirement is legal */
 73              if (minor < 1 || minor > EMLOG_MAX_SIZE)
 74                return -EINVAL;
 75            
 76              /* allocate space for our metadata */
 77              if ((einfo = kmalloc(sizeof(struct emlog_info), GFP_KERNEL)) == NULL)
 78                goto struct_malloc_failed;
 79            
 80              /* figure out how much of a buffer this should be and allocate the buffer */
 81              einfo->size = 1024 * minor;
 82              if ((einfo->data = kmalloc(sizeof(char)*einfo->size, GFP_KERNEL))==NULL)
 83                goto data_malloc_failed;
 84            
 85 jelson 1.1   /* init the rest of the structure */
 86              einfo->i_ino = inode->i_ino;
 87              einfo->refcount = 0;
 88              einfo->read_point = 0;
 89              einfo->write_point = 0;
 90              
 91              /* 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              kfree(einfo->data);
104             data_malloc_failed:
105              kfree(einfo);
106 jelson 1.1  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            
126              kfree(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.1   EMLOG_REFCOUNT(einfo)++;
170              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 jelson 1.1   einfo->refcount--;
191            
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             * caller must free.  length is the number of bytes to be read, which
202             * we assume is <= the number of bytes available. */
203            caddr_t read_from_emlog(struct emlog_info *einfo, int length)
204            {
205              caddr_t retval;
206              int bytes_copied = 0, n;
207              
208              if (length > EMLOG_SIZE(einfo)) {
209                printk("emlog: trying to read more (%d) than we have (%d)\n",
210                       length, EMLOG_SIZE(einfo));
211 jelson 1.1     return NULL;
212              }
213            
214              if ((retval = kmalloc(sizeof(char) * length, GFP_KERNEL)) == NULL)
215                return NULL;
216            
217              while (length) {
218                n = MIN(length, einfo->size - einfo->read_point /* contiguous bytes */);
219                memcpy(retval + bytes_copied, einfo->data + einfo->read_point, n);
220                bytes_copied += n;
221                length -= n;
222                einfo->read_point = (einfo->read_point + n) % einfo->size;
223              }
224            
225              return retval;
226            }
227            
228            static ssize_t emlog_read(struct file *file,
229                char *buffer,    /* The buffer to fill with data */
230                size_t length,   /* The length of the buffer */
231                loff_t *offset)  /* Our offset in the file */
232 jelson 1.1 {
233              int n, retval;
234              caddr_t data_to_return;
235              struct emlog_info *einfo;
236            
237              if ((einfo = get_einfo(file->f_dentry->d_inode)) == NULL) {
238                printk("emlog_read: record not found\n");
239                return -EIO;
240              }
241            
242              /* wait until there's data available (unless we do nonblocking reads) */
243              while (EMLOG_EMPTY(einfo)) {
244                if (file->f_flags & O_NONBLOCK)
245                  return -EAGAIN;
246            
247                interruptible_sleep_on(&emlog_read_wait);
248            
249                /* see if a signal woke us up */
250                if (signal_pending(current))
251                  return -ERESTARTSYS;
252              }
253 jelson 1.1 
254              /* read the data out of the internal buffer.  the following two
255               * statements must be must be atomic with respect to
256               * emlog_info... okay since syscalls are not interrupted */
257              n = MIN(length, EMLOG_SIZE(einfo));
258              if ((data_to_return = read_from_emlog(einfo, n)) == NULL)
259                return -EIO;
260            
261              /* another thread might run here if we block accessing userspace (in
262               * copy_to_user).  this is why i remove the message from the read
263               * queue before copying it back to the user -- to preserve the
264               * atomicity of read_from_emlog.  otherwise, a block here might
265               * cause the same data to be returned to two different threads
266               * trying to read from the same device.
267               *
268               * note that this function is reentrant by virtue of the fact that
269               * each thread has its own stack, and we keep the message
270               * temporarily buffered in local variables (i.e. on the stack) */
271              if (copy_to_user(buffer, data_to_return, n) > 0)
272                retval = -EFAULT;
273              else
274 jelson 1.1     retval = n;
275              kfree(data_to_return);
276              return retval;
277            }
278            
279            
280            
281            /* write_to_emlog writes to a circular buffer with wraparound.  in the
282             * case of an overflow, it overwrites the oldest unread data. */
283            void write_to_emlog(struct emlog_info *einfo, caddr_t buf, int length)
284            {
285              caddr_t retval;
286              int bytes_copied = 0, n;
287              int overflow;
288            
289              overflow = length + EMLOG_SIZE(einfo) > einfo->size;
290            
291              while (length) {
292                n = MIN(length, einfo->size - einfo->write_point /* contiguous bytes */);
293                memcpy(einfo->data + einfo->write_point, buf + bytes_copied, n);
294                bytes_copied += n;
295 jelson 1.1     length -= n;
296                einfo->write_point = (einfo->write_point + n) % einfo->size;
297              }
298            
299              /* if there is an overflow, reset the read point to read whatever is
300               * the oldest data that we have, that has not yet been
301               * overwritten. */
302              if (overflow)
303                einfo->read_point = (einfo->write_point + 1) % einfo->size;
304            }
305            
306            
307            static ssize_t emlog_write(struct file *file,
308                const char *buffer,
309                size_t length,
310                loff_t *offset)
311            {
312              caddr_t message = NULL;
313              int n;
314              struct emlog_info *einfo;
315            
316 jelson 1.1   if ((einfo = get_einfo(file->f_dentry->d_inode)) == NULL)
317                return -EIO;
318            
319              /* if the message is longer than the buffer, just take the beginning
320               * of it, in hopes that the reader (if any) will have time to read
321               * before we wrap around and obliterate it */
322              n = MIN(length, einfo->size - 1);
323            
324              /* make sure we have the memory for it */
325              if ((message = kmalloc(n, GFP_KERNEL)) == NULL)
326                return -ENOMEM;
327            
328              if (copy_from_user(message, buffer, n) > 0) {
329                kfree(message);
330                return -EFAULT;
331              }
332            
333              /* another thread might run here if we end up blocking on the
334               * user-space access, so it's important for reentrancy that the
335               * "message" variable is local.  see note about atomicity and
336               * reentrancy in emlog_read. */
337 jelson 1.1   write_to_emlog(einfo, message, n);
338              kfree(message);
339              wake_up_interruptible(&emlog_read_wait);
340              schedule(); /* hope that a reader wakes up! */
341              return n;
342            }
343            
344            static struct file_operations emlog_fops = {
345              NULL, /* lseek */
346              emlog_read, /* read */
347              emlog_write, /* write */
348              NULL, /* readdir */
349              NULL, /* poll */
350              NULL, /* ioctl */
351              NULL, /* mmap */
352              emlog_open, /* open */
353              NULL, /* flush */
354              emlog_release, /* release */
355              NULL, /* fsync */
356              NULL  /* fasync */
357            };
358 jelson 1.1 
359            
360            int init_module(void)
361            {
362              if (register_chrdev(EMLOG_MAJOR_NUMBER, "emlog", &emlog_fops) < 0) {
363                printk("emlog: unable to register character device %d\n",
364                       EMLOG_MAJOR_NUMBER);
365                return -EIO;
366              }
367            
368              return 0;
369            }
370            
371            
372            void cleanup_module(void)
373            {
374              unregister_chrdev(EMLOG_MAJOR_NUMBER, "emlog");
375            
376              /* clean up any still-allocated memory */
377              while (emlog_info_list != NULL)
378                free_einfo(emlog_info_list);
379 jelson 1.1 }
380            

CENS CVS Mailing List
Powered by
ViewCVS 0.9.2