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
|