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

CENS CVS Mailing List
Powered by
ViewCVS 0.9.2