|
version 1.4, 2001/08/13 06:01:31
|
version 1.5, 2001/08/13 08:10:18
|
|
|
|
| if (minor < 1 || minor > EMLOG_MAX_SIZE) | if (minor < 1 || minor > EMLOG_MAX_SIZE) |
| return -EINVAL; | return -EINVAL; |
| | |
| /* allocate space for our metadata */ |
/* allocate space for our metadata and initialize it */ |
| if ((einfo = kmalloc(sizeof(struct emlog_info), GFP_KERNEL)) == NULL) | if ((einfo = kmalloc(sizeof(struct emlog_info), GFP_KERNEL)) == NULL) |
| goto struct_malloc_failed; | goto struct_malloc_failed; |
| | |
| /* figure out how much of a buffer this should be and allocate the buffer */ |
memset(einfo, 0, sizeof(struct emlog_info)); |
| einfo->size = 1024 * minor; |
|
| if ((einfo->data = (char *) vmalloc(sizeof(char) * einfo->size)) == NULL) |
|
| goto data_malloc_failed; |
|
| |
|
| /* init the rest of the structure */ |
|
| einfo->i_ino = inode->i_ino; | einfo->i_ino = inode->i_ino; |
| einfo->refcount = 0; |
|
| einfo->read_point = 0; |
|
| einfo->write_point = 0; |
|
| | |
| #if defined(DECLARE_WAIT_QUEUE_HEAD) | #if defined(DECLARE_WAIT_QUEUE_HEAD) |
| init_waitqueue_head(&einfo->read_q); | init_waitqueue_head(&einfo->read_q); |
|
|
|
| init_waitqueue(&einfo->read_q); | init_waitqueue(&einfo->read_q); |
| #endif | #endif |
| | |
| |
/* figure out how much of a buffer this should be and allocate the buffer */ |
| |
einfo->size = 1024 * minor; |
| |
if ((einfo->data = (char *) vmalloc(sizeof(char) * einfo->size)) == NULL) |
| |
goto data_malloc_failed; |
| |
|
| /* add it to our linked list */ | /* add it to our linked list */ |
| einfo->next = emlog_info_list; | einfo->next = emlog_info_list; |
| emlog_info_list = einfo; | emlog_info_list = einfo; |
|
|
|
| return -EIO; | return -EIO; |
| } | } |
| | |
| EMLOG_REFCOUNT(einfo)++; |
einfo->refcount++; |
| MOD_INC_USE_COUNT; | MOD_INC_USE_COUNT; |
| return 0; | return 0; |
| } | } |
|
|
|
| | |
| /* read_from_emlog reads bytes out of a circular buffer with | /* read_from_emlog reads bytes out of a circular buffer with |
| * wraparound. returns caddr_t, pointer to data read, which the | * wraparound. returns caddr_t, pointer to data read, which the |
| * caller must free. length is the number of bytes to be read, which |
* caller must free. length is (a pointer to) the number of bytes to |
| * we assume is <= the number of bytes available. */ |
* be read, which will be set by this function to be the number of |
| caddr_t read_from_emlog(struct emlog_info *einfo, int length) |
* bytes actually returned */ |
| |
caddr_t read_from_emlog(struct emlog_info *einfo, int *length, loff_t *offset) |
| { | { |
| caddr_t retval; | caddr_t retval; |
| int bytes_copied = 0, n; |
int bytes_copied = 0, n, start_point, remaining; |
| | |
| if (length > EMLOG_SIZE(einfo)) { |
/* is the user trying to read data that has already scrolled off? */ |
| printk("emlog: trying to read more (%d) than we have (%d)\n", |
if (*offset < einfo->offset) |
| length, EMLOG_SIZE(einfo)); |
*offset = einfo->offset; |
| |
|
| |
/* is the user trying to read past EOF? */ |
| |
if (*offset >= einfo->offset + EMLOG_QLEN(einfo)) |
| return NULL; | return NULL; |
| } |
|
| | |
| if ((retval = kmalloc(sizeof(char) * length, GFP_KERNEL)) == NULL) |
/* find the smaller of the total bytes we have available and what |
| |
* the user is asking for */ |
| |
*length = MIN(*length, EMLOG_QLEN(einfo) - (*offset - einfo->offset)); |
| |
remaining = *length; |
| |
|
| |
/* figure out where to start based on user's offset */ |
| |
start_point = einfo->read_point + (*offset - einfo->offset); |
| |
start_point = start_point % einfo->size; |
| |
|
| |
/* allocate memory to return */ |
| |
if ((retval = kmalloc(sizeof(char) * remaining, GFP_KERNEL)) == NULL) |
| return NULL; | return NULL; |
| | |
| while (length) { |
/* copy the (possibly noncontiguous) data to our buffer */ |
| n = MIN(length, einfo->size - einfo->read_point /* contiguous bytes */); |
while (remaining) { |
| memcpy(retval + bytes_copied, einfo->data + einfo->read_point, n); |
n = MIN(remaining, einfo->size - start_point); |
| |
memcpy(retval + bytes_copied, einfo->data + start_point, n); |
| bytes_copied += n; | bytes_copied += n; |
| length -= n; |
remaining -= n; |
| einfo->read_point = (einfo->read_point + n) % einfo->size; |
start_point = (start_point + n) % einfo->size; |
| } | } |
| | |
| |
/* advance user's file pointer */ |
| |
*offset += *length; |
| return retval; | return retval; |
| } | } |
| | |
|
|
|
| size_t length, /* The length of the buffer */ | size_t length, /* The length of the buffer */ |
| loff_t *offset) /* Our offset in the file */ | loff_t *offset) /* Our offset in the file */ |
| { | { |
| int n, retval; |
int retval; |
| caddr_t data_to_return; | caddr_t data_to_return; |
| struct emlog_info *einfo; | struct emlog_info *einfo; |
| | |
| |
printk("got a read at offset %d\n", *offset); |
| |
|
| |
/* get the metadata about this emlog */ |
| if ((einfo = get_einfo(file->f_dentry->d_inode)) == NULL) { | if ((einfo = get_einfo(file->f_dentry->d_inode)) == NULL) { |
| printk("emlog_read: record not found\n"); | printk("emlog_read: record not found\n"); |
| return -EIO; | return -EIO; |
| } | } |
| | |
| |
#if 0 |
| /* wait until there's data available (unless we do nonblocking reads) */ | /* wait until there's data available (unless we do nonblocking reads) */ |
| while (EMLOG_EMPTY(einfo)) { | while (EMLOG_EMPTY(einfo)) { |
| if (file->f_flags & O_NONBLOCK) | if (file->f_flags & O_NONBLOCK) |
|
|
|
| if (signal_pending(current)) | if (signal_pending(current)) |
| return -ERESTARTSYS; | return -ERESTARTSYS; |
| } | } |
| |
#endif |
| | |
| /* read the data out of the internal buffer. the following two |
if ((data_to_return = read_from_emlog(einfo, &length, offset)) == NULL) |
| * statements must be must be atomic with respect to |
return 0; |
| * emlog_info... okay since syscalls are not interrupted */ |
|
| n = MIN(length, EMLOG_SIZE(einfo)); |
|
| if ((data_to_return = read_from_emlog(einfo, n)) == NULL) |
|
| return -EIO; |
|
| | |
| /* another thread might run here if we block accessing userspace (in |
if (copy_to_user(buffer, data_to_return, length) > 0) |
| * copy_to_user). this is why i remove the message from the read |
|
| * queue before copying it back to the user -- to preserve the |
|
| * atomicity of read_from_emlog. otherwise, a block here might |
|
| * cause the same data to be returned to two different threads |
|
| * trying to read from the same device. |
|
| * |
|
| * note that this function is reentrant by virtue of the fact that |
|
| * each thread has its own stack, and we keep the message |
|
| * temporarily buffered in local variables (i.e. on the stack) */ |
|
| if (copy_to_user(buffer, data_to_return, n) > 0) |
|
| retval = -EFAULT; | retval = -EFAULT; |
| else | else |
| retval = n; |
retval = length; |
| kfree(data_to_return); | kfree(data_to_return); |
| return retval; | return retval; |
| } | } |
|
|
|
| void write_to_emlog(struct emlog_info *einfo, caddr_t buf, int length) | void write_to_emlog(struct emlog_info *einfo, caddr_t buf, int length) |
| { | { |
| caddr_t retval; | caddr_t retval; |
| int bytes_copied = 0, n; |
int bytes_copied = 0; |
| int overflow; |
int overflow = 0; |
| |
int n; |
| |
|
| |
if (length + EMLOG_QLEN(einfo) >= (einfo->size-1)) { |
| |
overflow = 1; |
| | |
| overflow = length + EMLOG_SIZE(einfo) > einfo->size; |
/* in case of overflow, figure out where the new buffer will |
| |
* begin. we start by figuring out where the current buffer ENDS: |
| |
* einfo->offset + EMLOG_QLEN. we then advance the end-offset |
| |
* by the length of the current write, and work backwards to |
| |
* figure out what the oldest unoverwritten data will be (i.e., |
| |
* size of the buffer). was that all quite clear? :-) */ |
| |
einfo->offset = einfo->offset + EMLOG_QLEN(einfo) + length |
| |
- einfo->size + 1; |
| |
} |
| | |
| while (length) { | while (length) { |
| n = MIN(length, einfo->size - einfo->write_point /* contiguous bytes */); |
/* how many contiguous bytes are available from the write point to |
| |
* the end of the circular buffer? */ |
| |
n = MIN(length, einfo->size - einfo->write_point); |
| memcpy(einfo->data + einfo->write_point, buf + bytes_copied, n); | memcpy(einfo->data + einfo->write_point, buf + bytes_copied, n); |
| bytes_copied += n; | bytes_copied += n; |
| length -= n; | length -= n; |
|
|
|
| int n; | int n; |
| struct emlog_info *einfo; | struct emlog_info *einfo; |
| | |
| |
/* get the metadata about this emlog */ |
| if ((einfo = get_einfo(file->f_dentry->d_inode)) == NULL) | if ((einfo = get_einfo(file->f_dentry->d_inode)) == NULL) |
| return -EIO; | return -EIO; |
| | |
|
|
|
| if ((message = kmalloc(n, GFP_KERNEL)) == NULL) | if ((message = kmalloc(n, GFP_KERNEL)) == NULL) |
| return -ENOMEM; | return -ENOMEM; |
| | |
| |
/* copy into our temp buffer */ |
| if (copy_from_user(message, buffer, n) > 0) { | if (copy_from_user(message, buffer, n) > 0) { |
| kfree(message); | kfree(message); |
| return -EFAULT; | return -EFAULT; |
| } | } |
| | |
| /* another thread might run here if we end up blocking on the |
/* now copy it into the circular buffer and free our temp copy */ |
| * user-space access, so it's important for reentrancy that the |
|
| * "message" variable is local. see note about atomicity and |
|
| * reentrancy in emlog_read. */ |
|
| write_to_emlog(einfo, message, n); | write_to_emlog(einfo, message, n); |
| kfree(message); | kfree(message); |
| |
|
| |
/* update the size in the inode, so tail -f works */ |
| |
file->f_dentry->d_inode->i_size = EMLOG_QLEN(einfo) + einfo->offset - 1; |
| |
printk("size now %d\n", file->f_dentry->d_inode->i_size ); |
| |
|
| |
/* wake up any readers that might be waiting for the data. we call |
| |
* schedule in the vague hope that a reader will run before the |
| |
* writer's next write, to avoid losing data. */ |
| wake_up_interruptible(EMLOG_READQ(einfo)); | wake_up_interruptible(EMLOG_READQ(einfo)); |
| schedule(); /* hope that a reader wakes up! */ |
schedule(); |
| |
|
| return n; | return n; |
| } | } |
| | |
|
|
|
| { | { |
| struct emlog_info *einfo; | struct emlog_info *einfo; |
| | |
| |
/* get the metadata about this emlog */ |
| if ((einfo = get_einfo(file->f_dentry->d_inode)) == NULL) | if ((einfo = get_einfo(file->f_dentry->d_inode)) == NULL) |
| return -EIO; | return -EIO; |
| | |