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
|