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

CENS CVS Mailing List
Powered by
ViewCVS 0.9.2