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
|