(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.2  * $Id: emlog.c,v 1.1 2000/06/06 09:11:13 jelson Exp $
  8 jelson 1.1  */
  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            #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 jelson 1.1 #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 int emlog_debug;
 42            
 43            #define MIN(x, y) ((x) < (y) ? (x) : y)
 44            
 45            
 46            /* find the emlog-info structure associated with an inode.  returns a
 47             * pointer to the structure if found, NULL if not found */
 48            static struct emlog_info *get_einfo(struct inode *inode)
 49            {
 50 jelson 1.1   struct emlog_info *einfo;
 51            
 52              if (inode == NULL)
 53                return NULL;
 54            
 55              for (einfo = emlog_info_list; einfo != NULL; einfo = einfo->next)
 56                if (einfo->i_ino == inode->i_ino)
 57                  return einfo;
 58            
 59              return NULL;
 60            }
 61            
 62            
 63            /* create a new emlog buffer and its associated info structure.
 64             * returns an errno on failure, or 0 on success.  on success, the
 65             * pointer to the new struct is passed back using peinfo */
 66            static int create_einfo(struct inode *inode, int minor,
 67                                    struct emlog_info **peinfo)
 68            {
 69              struct emlog_info *einfo;
 70            
 71 jelson 1.1   /* make sure the memory requirement is legal */
 72              if (minor < 1 || minor > EMLOG_MAX_SIZE)
 73                return -EINVAL;
 74            
 75              /* allocate space for our metadata */
 76              if ((einfo = kmalloc(sizeof(struct emlog_info), GFP_KERNEL)) == NULL)
 77                goto struct_malloc_failed;
 78            
 79              /* figure out how much of a buffer this should be and allocate the buffer */
 80              einfo->size = 1024 * minor;
 81              if ((einfo->data = kmalloc(sizeof(char)*einfo->size, GFP_KERNEL))==NULL)
 82                goto data_malloc_failed;
 83            
 84              /* init the rest of the structure */
 85              einfo->i_ino = inode->i_ino;
 86              einfo->refcount = 0;
 87              einfo->read_point = 0;
 88              einfo->write_point = 0;
 89 jelson 1.2 
 90            #if defined(DECLARE_WAIT_QUEUE_HEAD)
 91              init_waitqueue_head(&einfo->read_q);
 92            #else
 93              init_waitqueue(&einfo->read_q);
 94            #endif
 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                printk("allocating resources associated with inode %d\n", einfo->i_ino);
102            
103              /* pass the struct back */
104              *peinfo = einfo;
105              return 0;
106            
107             other_failure: /* if we check for other errors later, jump here */
108              kfree(einfo->data);
109             data_malloc_failed:
110              kfree(einfo);
111             struct_malloc_failed:
112              return -ENOMEM;
113            }
114            
115            
116            /* this frees all data associated with an emlog_info buffer, including
117 jelson 1.1  * the struct that you pass to the function.  don't dereference this
118             * structure after calling free_einfo! */
119            void free_einfo(struct emlog_info *einfo)
120            {
121              struct emlog_info **ptr;
122            
123              if (einfo == NULL) {
124                printk("null passed to free_einfo... which is bad\n");
125                return;
126              }
127            
128              if (emlog_debug)
129                printk("freeing resources associated with inode %d\n", einfo->i_ino);
130            
131              kfree(einfo->data);
132            
133              /* now delete the 'einfo' structure from the linked list.  'ptr' is
134               * the pointer that needs to be changed... which is either the list
135               * head or one of the 'next' pointers on the list. */
136              ptr = &emlog_info_list;
137              while (*ptr != einfo) {
138 jelson 1.1     if (!*ptr) {
139                  printk("corrupt einfo list!\n");
140                  break;
141                } else
142                ptr = &((**ptr).next);
143            
144              }
145              *ptr = einfo->next;
146            
147            
148            }
149            
150            
151            
152            /************************ File Interface Functions ************************/
153            
154            
155            
156            static int emlog_open(struct inode *inode, struct file *file)
157            {
158              int minor = MINOR(inode->i_rdev);
159 jelson 1.1 
160              struct emlog_info *einfo = NULL;
161              int retval;
162            
163              if ((einfo = get_einfo(inode)) == NULL) {
164                /* never heard of this inode before... create a new record */
165                if ((retval = create_einfo(inode, minor, &einfo)) < 0)
166                  return retval;
167              }
168            
169              if (einfo == NULL) {
170                printk("BUG IN EMLOG!\n");
171                return -EIO;
172              }
173            
174              EMLOG_REFCOUNT(einfo)++;
175              MOD_INC_USE_COUNT;
176              return 0;
177            }
178            
179            
180 jelson 1.1 /* this is called when a file is closed */
181            static int emlog_release(struct inode *inode, struct file *file)
182            {
183              struct emlog_info *einfo;
184            
185              MOD_DEC_USE_COUNT;
186            
187              /* get the buffer info */
188              if ((einfo = get_einfo(inode)) == NULL) {
189                printk("emlog: releasing unknown file!  zoinks!\n");
190                return -EINVAL;
191              }
192            
193              /* decrement the reference count.  if no one has this file open and
194               * it's not holding any data, delete the record. */
195              einfo->refcount--;
196            
197              if (einfo->refcount == 0 && EMLOG_EMPTY(einfo))
198                free_einfo(einfo);
199            
200              return 0;
201 jelson 1.1 }
202            
203            
204            /* read_from_emlog reads bytes out of a circular buffer with
205             * wraparound.  returns caddr_t, pointer to data read, which the
206             * caller must free.  length is the number of bytes to be read, which
207             * we assume is <= the number of bytes available. */
208            caddr_t read_from_emlog(struct emlog_info *einfo, int length)
209            {
210              caddr_t retval;
211              int bytes_copied = 0, n;
212              
213              if (length > EMLOG_SIZE(einfo)) {
214                printk("emlog: trying to read more (%d) than we have (%d)\n",
215                       length, EMLOG_SIZE(einfo));
216                return NULL;
217              }
218            
219              if ((retval = kmalloc(sizeof(char) * length, GFP_KERNEL)) == NULL)
220                return NULL;
221            
222 jelson 1.1   while (length) {
223                n = MIN(length, einfo->size - einfo->read_point /* contiguous bytes */);
224                memcpy(retval + bytes_copied, einfo->data + einfo->read_point, n);
225                bytes_copied += n;
226                length -= n;
227                einfo->read_point = (einfo->read_point + n) % einfo->size;
228              }
229            
230              return retval;
231            }
232            
233            static ssize_t emlog_read(struct file *file,
234                char *buffer,    /* The buffer to fill with data */
235                size_t length,   /* The length of the buffer */
236                loff_t *offset)  /* Our offset in the file */
237            {
238              int n, retval;
239              caddr_t data_to_return;
240              struct emlog_info *einfo;
241            
242              if ((einfo = get_einfo(file->f_dentry->d_inode)) == NULL) {
243 jelson 1.1     printk("emlog_read: record not found\n");
244                return -EIO;
245              }
246            
247              /* wait until there's data available (unless we do nonblocking reads) */
248              while (EMLOG_EMPTY(einfo)) {
249                if (file->f_flags & O_NONBLOCK)
250                  return -EAGAIN;
251            
252 jelson 1.2     interruptible_sleep_on(EMLOG_READQ(einfo));
253 jelson 1.1 
254                /* see if a signal woke us up */
255                if (signal_pending(current))
256                  return -ERESTARTSYS;
257              }
258            
259              /* read the data out of the internal buffer.  the following two
260               * statements must be must be atomic with respect to
261               * emlog_info... okay since syscalls are not interrupted */
262              n = MIN(length, EMLOG_SIZE(einfo));
263              if ((data_to_return = read_from_emlog(einfo, n)) == NULL)
264                return -EIO;
265            
266              /* another thread might run here if we block accessing userspace (in
267               * copy_to_user).  this is why i remove the message from the read
268               * queue before copying it back to the user -- to preserve the
269               * atomicity of read_from_emlog.  otherwise, a block here might
270               * cause the same data to be returned to two different threads
271               * trying to read from the same device.
272               *
273               * note that this function is reentrant by virtue of the fact that
274 jelson 1.1    * each thread has its own stack, and we keep the message
275               * temporarily buffered in local variables (i.e. on the stack) */
276              if (copy_to_user(buffer, data_to_return, n) > 0)
277                retval = -EFAULT;
278              else
279                retval = n;
280              kfree(data_to_return);
281              return retval;
282            }
283            
284            
285            
286            /* write_to_emlog writes to a circular buffer with wraparound.  in the
287             * case of an overflow, it overwrites the oldest unread data. */
288            void write_to_emlog(struct emlog_info *einfo, caddr_t buf, int length)
289            {
290              caddr_t retval;
291              int bytes_copied = 0, n;
292              int overflow;
293            
294              overflow = length + EMLOG_SIZE(einfo) > einfo->size;
295 jelson 1.1 
296              while (length) {
297                n = MIN(length, einfo->size - einfo->write_point /* contiguous bytes */);
298                memcpy(einfo->data + einfo->write_point, buf + bytes_copied, n);
299                bytes_copied += n;
300                length -= n;
301                einfo->write_point = (einfo->write_point + n) % einfo->size;
302              }
303            
304              /* if there is an overflow, reset the read point to read whatever is
305               * the oldest data that we have, that has not yet been
306               * overwritten. */
307              if (overflow)
308                einfo->read_point = (einfo->write_point + 1) % einfo->size;
309            }
310            
311            
312            static ssize_t emlog_write(struct file *file,
313                const char *buffer,
314                size_t length,
315                loff_t *offset)
316 jelson 1.1 {
317              caddr_t message = NULL;
318              int n;
319              struct emlog_info *einfo;
320            
321              if ((einfo = get_einfo(file->f_dentry->d_inode)) == NULL)
322                return -EIO;
323            
324              /* if the message is longer than the buffer, just take the beginning
325               * of it, in hopes that the reader (if any) will have time to read
326               * before we wrap around and obliterate it */
327              n = MIN(length, einfo->size - 1);
328            
329              /* make sure we have the memory for it */
330              if ((message = kmalloc(n, GFP_KERNEL)) == NULL)
331                return -ENOMEM;
332            
333              if (copy_from_user(message, buffer, n) > 0) {
334                kfree(message);
335                return -EFAULT;
336              }
337 jelson 1.1 
338              /* another thread might run here if we end up blocking on the
339               * user-space access, so it's important for reentrancy that the
340               * "message" variable is local.  see note about atomicity and
341               * reentrancy in emlog_read. */
342              write_to_emlog(einfo, message, n);
343              kfree(message);
344 jelson 1.2   wake_up_interruptible(EMLOG_READQ(einfo));
345 jelson 1.1   schedule(); /* hope that a reader wakes up! */
346              return n;
347            }
348            
349 jelson 1.2 
350            static unsigned int emlog_poll(struct file *file, poll_table *wait)
351            {
352              struct emlog_info *einfo;
353            
354              if ((einfo = get_einfo(file->f_dentry->d_inode)) == NULL)
355                return -EIO;
356            
357              poll_wait(file, EMLOG_READQ(einfo), wait);
358            
359              if (!EMLOG_EMPTY(einfo))
360                return POLLIN | POLLRDNORM;
361              else
362                return 0;
363            }
364            
365            
366 jelson 1.1 static struct file_operations emlog_fops = {
367 jelson 1.2   read   : emlog_read,
368              write  : emlog_write,
369              open   : emlog_open,
370              release: emlog_release,
371              poll   : emlog_poll,
372 jelson 1.1 };
373            
374            
375            int init_module(void)
376            {
377              if (register_chrdev(EMLOG_MAJOR_NUMBER, "emlog", &emlog_fops) < 0) {
378                printk("emlog: unable to register character device %d\n",
379                       EMLOG_MAJOR_NUMBER);
380                return -EIO;
381              }
382            
383              return 0;
384            }
385            
386            
387            void cleanup_module(void)
388            {
389              unregister_chrdev(EMLOG_MAJOR_NUMBER, "emlog");
390            
391              /* clean up any still-allocated memory */
392              while (emlog_info_list != NULL)
393 jelson 1.1     free_einfo(emlog_info_list);
394            }
395 jelson 1.2 
396            
397 jelson 1.1 

CENS CVS Mailing List
Powered by
ViewCVS 0.9.2