This is second part of kevent based AIO reading implementation. Signed-off-by: Evgeniy Polyakov <[EMAIL PROTECTED]>
diff --git a/include/linux/kevent.h b/include/linux/kevent.h index 376fedc..521119e 100644 --- a/include/linux/kevent.h +++ b/include/linux/kevent.h @@ -27,12 +27,14 @@ */ #define KEVENT_REQ_ONESHOT 0x1 /* Process this event only once and then dequeue. */ +#define KEVENT_REQ_ENQUEUE 0x2 /* Always add that kevent, even if it is ready immediately. */ /* * Kevent return flags. */ #define KEVENT_RET_BROKEN 0x1 /* Kevent is broken. */ #define KEVENT_RET_DONE 0x2 /* Kevent processing was finished successfully. */ +#define KEVENT_RET_STACK 0x4 /* Kevent was allocated in stack. Can only be used for immediate ready kevents. */ /* * Kevent type set. @@ -202,7 +204,6 @@ struct kevent_user unsigned int max_ready_num; /* Requested number of kevents. */ struct semaphore ctl_mutex; /* Protects against simultaneous kevent_user control manipulations. */ - struct semaphore wait_mutex; /* Protects against simultaneous kevent_user waits. */ wait_queue_head_t wait; /* Wait until some events are ready. */ atomic_t refcnt; /* Reference counter, increased for each new kevent. */ @@ -210,6 +211,7 @@ struct kevent_user unsigned long im_num; unsigned long wait_num; unsigned long total; + unsigned long enfile; #endif }; @@ -220,7 +222,9 @@ void kevent_free(struct kevent *k); int kevent_enqueue(struct kevent *k); int kevent_dequeue(struct kevent *k); int kevent_init(struct kevent *k); -void kevent_requeue(struct kevent *k); +int kevent_requeue(struct kevent *k); +struct kevent *kevent_recreate(struct kevent *kevent, gfp_t mask); +void kevent_finish_user(struct kevent *k, int lock, int deq); #define list_for_each_entry_reverse_safe(pos, n, head, member) \ for (pos = list_entry((head)->prev, typeof(*pos), member), \ @@ -241,7 +245,7 @@ int kevent_init_aio(struct kevent *k); void kevent_storage_ready(struct kevent_storage *st, kevent_callback_t ready_callback, u32 event); int kevent_storage_init(void *origin, struct kevent_storage *st); void kevent_storage_fini(struct kevent_storage *st); -int kevent_storage_enqueue(struct kevent_storage *st, struct kevent *k); +void kevent_storage_enqueue(struct kevent_storage *st, struct kevent *k); void kevent_storage_dequeue(struct kevent_storage *st, struct kevent *k); int kevent_user_add_ukevent(struct ukevent *uk, struct kevent_user *u); diff --git a/kernel/kevent/kevent.c b/kernel/kevent/kevent.c index 9bccc66..a055f40 100644 --- a/kernel/kevent/kevent.c +++ b/kernel/kevent/kevent.c @@ -116,7 +116,7 @@ int kevent_init(struct kevent *k) * Called from ->enqueue() callback when reference counter for given * origin (socket, inode...) has been increased. */ -int kevent_storage_enqueue(struct kevent_storage *st, struct kevent *k) +void kevent_storage_enqueue(struct kevent_storage *st, struct kevent *k) { unsigned long flags; @@ -125,7 +125,6 @@ int kevent_storage_enqueue(struct kevent list_add_tail(&k->storage_entry, &st->list); st->qlen++; spin_unlock_irqrestore(&st->lock, flags); - return 0; } /* @@ -146,7 +145,7 @@ void kevent_storage_dequeue(struct keven spin_unlock_irqrestore(&st->lock, flags); } -static void __kevent_requeue(struct kevent *k, u32 event) +static int kevent_callback(struct kevent *k) { int err, rem = 0; unsigned long flags; @@ -162,30 +161,45 @@ static void __kevent_requeue(struct keve } rem = (k->event.req_flags & KEVENT_REQ_ONESHOT); spin_unlock_irqrestore(&k->lock, flags); + + if (err && rem) { + list_del(&k->storage_entry); + k->st->qlen--; + } - if (err) { - if (rem) { - list_del(&k->storage_entry); - k->st->qlen--; - } - - spin_lock_irqsave(&k->user->ready_lock, flags); - if (k->ready_entry.next == LIST_POISON1) { - list_add_tail(&k->ready_entry, &k->user->ready_list); - k->user->ready_num++; - } - spin_unlock_irqrestore(&k->user->ready_lock, flags); - wake_up(&k->user->wait); + return err; +} + +static void __kevent_requeue(struct kevent *k, u32 event) +{ + unsigned long flags; + + spin_lock_irqsave(&k->user->ready_lock, flags); + if (k->ready_entry.next == LIST_POISON1) { + list_add_tail(&k->ready_entry, &k->user->ready_list); + k->user->ready_num++; } + spin_unlock_irqrestore(&k->user->ready_lock, flags); + wake_up(&k->user->wait); } -void kevent_requeue(struct kevent *k) + +/* + * It is called + */ +int kevent_requeue(struct kevent *k) { + int err; unsigned long flags; - spin_lock_irqsave(&k->st->lock, flags); - __kevent_requeue(k, 0); - spin_unlock_irqrestore(&k->st->lock, flags); + err = kevent_callback(k); + + if (err) { + spin_lock_irqsave(&k->st->lock, flags); + __kevent_requeue(k, 0); + spin_unlock_irqrestore(&k->st->lock, flags); + } + return err; } /* @@ -194,14 +208,26 @@ void kevent_requeue(struct kevent *k) void kevent_storage_ready(struct kevent_storage *st, kevent_callback_t ready_callback, u32 event) { struct kevent *k, *n; + unsigned long flags; + int err; spin_lock(&st->lock); list_for_each_entry_safe(k, n, &st->list, storage_entry) { + spin_lock_irqsave(&k->user->ready_lock, flags); + if (k->ready_entry.next != LIST_POISON1) { + spin_unlock_irqrestore(&k->user->ready_lock, flags); + continue; + } + spin_unlock_irqrestore(&k->user->ready_lock, flags); + if (ready_callback) ready_callback(k); - if (event & k->event.event) - __kevent_requeue(k, event); + if (event & k->event.event) { + err = kevent_callback(k); + if (err) + __kevent_requeue(k, event); + } } spin_unlock(&st->lock); } @@ -235,7 +261,6 @@ struct kevent *kevent_alloc(gfp_t mask) void kevent_free(struct kevent *k) { memset(k, 0xab, sizeof(struct kevent)); - if (kevent_cache) kmem_cache_free(kevent_cache, k); else diff --git a/kernel/kevent/kevent_aio.c b/kernel/kevent/kevent_aio.c index 3f76803..cfd8a16 100644 --- a/kernel/kevent/kevent_aio.c +++ b/kernel/kevent/kevent_aio.c @@ -25,18 +25,29 @@ #include <linux/spinlock.h> #include <linux/file.h> #include <linux/fs.h> +#include <linux/swap.h> #include <linux/pagemap.h> #include <linux/bio.h> #include <linux/buffer_head.h> #include <linux/kevent.h> +//#define KEVENT_AIO_DEBUG + +#ifdef KEVENT_AIO_DEBUG +#define dprintk(f, a...) printk(f, ##a) +#else +#define dprintk(f, a...) do {} while (0) +#endif + struct kevent_aio_private { struct page **pages; + int pg_first; int pg_num; int bio_num; size_t size; loff_t offset; + struct file *file; }; extern void bio_fs_destructor(struct bio *bio); @@ -44,6 +55,7 @@ extern void bio_fs_destructor(struct bio static void kevent_aio_bio_destructor(struct bio *bio) { struct inode *inode = bio->bi_private; + dprintk("%s: bio=%p, num=%u, inode=%p.\n", __func__, bio, bio->bi_vcnt, inode); kevent_storage_ready(&inode->st, NULL, KEVENT_AIO_BIO); bio_fs_destructor(bio); } @@ -78,6 +90,7 @@ static inline struct bio *kevent_mpage_b { if (bio) { bio->bi_end_io = kevent_mpage_end_io_read; + dprintk("%s: bio=%p, num=%u.\n", __func__, bio, bio->bi_vcnt); submit_bio(READ, bio); } return NULL; @@ -107,7 +120,7 @@ static struct bio *kevent_mpage_readpage bh.b_state = 0; if (block_in_file < last_block) { if (get_block(inode, block_in_file, &bh, 0)) { - printk("%s: confused: get_block failed: page_block=%u.\n", __func__, page_block); + dprintk("%s: confused: get_block failed: page_block=%u.\n", __func__, page_block); goto confused; } } @@ -132,14 +145,14 @@ static struct bio *kevent_mpage_readpage } if (first_hole != blocks_per_page) { - printk("%s: confused: page_block=%u, first_hole=%u, blocks_per_page=%u.\n", + dprintk("%s: confused: page_block=%u, first_hole=%u, blocks_per_page=%u.\n", __func__, page_block, first_hole, blocks_per_page); goto confused; /* hole -> non-hole */ } /* Contiguous blocks? */ if (page_block && blocks[page_block-1] != bh.b_blocknr-1) { - printk("%s: confused: page_block=%u, blocks=%Lu, bh.b_blocknr=%Lu.\n", + dprintk("%s: confused: page_block=%u, blocks=%Lu, bh.b_blocknr=%Lu.\n", __func__, page_block, blocks[page_block-1], bh.b_blocknr-1); goto confused; } @@ -188,14 +201,14 @@ alloc_new: length = first_hole << blkbits; if (bio_add_page(bio, page, length, 0) < length) { bio = kevent_mpage_bio_submit(READ, bio); - printk("%s: Failed to add a page: nr_pages=%d, length=%d, page=%p.\n", __func__, nr_pages, length, page); + dprintk("%s: Failed to add a page: nr_pages=%d, length=%d, page=%p.\n", __func__, nr_pages, length, page); goto alloc_new; } -#if 0 - printk("%s: bio=%p, b=%d, m=%d, u=%d, nr_pages=%d, offset=%Lu, size=%Lu. page_block=%u, page=%p.\n", + + dprintk("%s: bio=%p, b=%d, m=%d, u=%d, nr_pages=%d, offset=%Lu, size=%Lu. page_block=%u, page=%p.\n", __func__, bio, buffer_boundary(&bh), buffer_mapped(&bh), buffer_uptodate(&bh), nr_pages, *offset, i_size_read(inode), page_block, page); -#endif + *offset = *offset + length; if (buffer_boundary(&bh) || (first_hole != blocks_per_page)) @@ -207,6 +220,7 @@ out: return bio; confused: + dprintk("%s: confused. bio=%p.\n", __func__, bio); if (bio) bio = kevent_mpage_bio_submit(READ, bio); goto out; @@ -222,6 +236,7 @@ static int kevent_mpage_readpages(struct for (i=0; i<nr_pages; ++i) { struct page *page = pages[i]; + ClearPageUptodate(page); bio = kevent_mpage_readpage(bio, inode, page, nr_pages-i, get_block, offset, &last_block_in_bio, bio_num); } @@ -231,6 +246,83 @@ static int kevent_mpage_readpages(struct return 0; } +static int kevent_aio_vfs_read(struct kevent *k) +{ + struct kevent_aio_private *priv = k->priv; + struct address_space *mapping; + unsigned int size = k->event.id.raw[1]; + size_t isize; + int i; + + mapping = priv->file->f_mapping; + isize = i_size_read(priv->file->f_dentry->d_inode); + + dprintk("%s: start size=%u, offset=%Lu, isize=%zu, pg_num=%d.\n", __func__, size, priv->offset, isize, priv->pg_num); + + for (i=0; i<priv->pg_num; ++i) { + struct page *page; + struct page *upage = priv->pages[i]; + unsigned long nr = PAGE_CACHE_SIZE; + void *kptr, *uptr; + + cond_resched(); + page = find_get_page(mapping, priv->offset >> PAGE_CACHE_SHIFT); + if (unlikely(page == NULL)) { + break; + } + if (!PageUptodate(page)) { + page_cache_release(page); + break; + } + + if (mapping_writably_mapped(mapping)) + flush_dcache_page(page); + + mark_page_accessed(page); + + kptr = kmap_atomic(page, KM_USER0); + if (!kptr) { + page_cache_release(page); + break; + } + uptr = kmap_atomic(upage, KM_USER1); + if (!uptr) { + page_cache_release(page); + kunmap_atomic(kptr, KM_USER0); + break; + } + + if (nr + priv->offset > isize) + nr = isize - priv->offset; + if (nr > size) + nr = size; + + memcpy(uptr, kptr, nr); + + kunmap_atomic(uptr, KM_USER1); + kunmap_atomic(kptr, KM_USER0); + + page_cache_release(page); + SetPageUptodate(upage); + + priv->offset += nr; + priv->pg_first = i+1; + size -= nr; + + if (priv->offset >= isize || !size) + break; + } + + dprintk("%s: end first=%d, num=%d, left=%u, offset=%Lu.\n", __func__, priv->pg_first, priv->pg_num, size, priv->offset); + + k->event.id.raw[1] = size; + + if (priv->offset >= isize || !size) + i = priv->pg_num; + + return i; +} + static int kevent_aio_callback(struct kevent *k) { struct kevent_aio_private *priv = k->priv; @@ -238,7 +330,18 @@ static int kevent_aio_callback(struct ke size_t size; BUG_ON(!priv); + BUG_ON(!priv->pages); + dprintk("%s: k=%p, priv=%p, num=%u, bio_num=%u, first=%u, size=%zu, off=%Lu.\n", + __func__, k, priv, priv->pg_num, priv->bio_num, priv->pg_first, priv->size, priv->offset); + + if (!priv->bio_num) { + ready = kevent_aio_vfs_read(k); + if (ready > 0 && ready != priv->pg_num) + ready = 0; + goto out; + } + size = priv->size; for (i=0; i<priv->pg_num && ready > 0; ++i) { @@ -248,21 +351,35 @@ static int kevent_aio_callback(struct ke ready = -1; else if (!PageUptodate(page)) ready = 0; - else - size -= PAGE_SIZE; + else { + if (size < PAGE_SIZE) + size = 0; + else + size -= PAGE_SIZE; + } } if (ready) k->event.id.raw[1] = size; +out: + dprintk("%s: ready=%d, left=%u, size=%zu.\n", __func__, ready, k->event.id.raw[1], priv->size); + + if (ready) { + priv->bio_num = 0; + } + return ready; } int ext3_get_block(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create); +static unsigned int prev_offset; + static int kevent_aio_enqueue(struct kevent *k) { + struct kevent *nk; int err, i; unsigned long addr = (unsigned long)k->event.ptr; unsigned int size = k->event.id.raw[1]; @@ -272,9 +389,17 @@ static int kevent_aio_enqueue(struct kev struct inode *inode; struct kevent_aio_private *priv; + if (prev_offset != k->event.ret_data[1]) { + prev_offset = k->event.ret_data[1]; + } +#ifdef KEVENT_AIO_DEBUG + else if (printk_ratelimit()) + dprintk(KERN_INFO "%s: k=%p, prev_offset=%u, off=%u, size=%u.\n", __func__, k, prev_offset, k->event.ret_data[1], size); +#endif + if ((addr & PAGE_MASK) != addr) return -EINVAL; - + file = fget_light(k->event.id.raw[0], &fput_needed); if (!file) return -ENODEV; @@ -300,28 +425,47 @@ static int kevent_aio_enqueue(struct kev if (err <= 0) goto err_out_free; num = err; - + priv->pg_num = num; priv->size = size; priv->bio_num = 0; - priv->offset = 0; - + priv->offset = k->event.ret_data[1]; + priv->file = file; k->priv = priv; + + err = k->callback(k); + + if ((k->event.req_flags & KEVENT_REQ_ENQUEUE) || !err) { + nk = kevent_recreate(k, GFP_KERNEL); + if (!nk) { + err = -ENOMEM; + goto err_out_release; + } + + dprintk("%s: k=%p, priv=%p, num=%u, ready_num=%d, size=%zu, off=%Lu.\n", + __func__, nk, priv, priv->pg_num, priv->pg_first, priv->size, priv->offset); + + num -= priv->pg_first; - kevent_storage_enqueue(&inode->st, k); + kevent_storage_enqueue(&inode->st, nk); - err = kevent_mpage_readpages(inode, priv->pages, num, &ext3_get_block, &priv->offset, &priv->bio_num); - if (err) - goto err_out_dequeue; + if (num) { + err = kevent_mpage_readpages(inode, &priv->pages[priv->pg_first], num, &ext3_get_block, &priv->offset, &priv->bio_num); + if (err && !(k->event.req_flags & KEVENT_REQ_ENQUEUE)) + goto err_out_dequeue; + } + } fput_light(file, fput_needed); + dprintk("%s: k=%p, returning %d.\n", __func__, k, err); + return err; err_out_dequeue: - kevent_storage_dequeue(k->st, k); - - for (i=0; i<num; ++i) + kevent_finish_user(nk, 0, 1); +err_out_release: + for (i=0; i<priv->pg_num; ++i) page_cache_release(priv->pages[i]); err_out_free: kfree(priv); @@ -330,6 +474,8 @@ err_out_iput: err_out_fput: fput_light(file, fput_needed); + k->event.id.raw[1] = size; + return err; } diff --git a/kernel/kevent/kevent_inode.c b/kernel/kevent/kevent_inode.c index 3af0e11..ed6e0bc 100644 --- a/kernel/kevent/kevent_inode.c +++ b/kernel/kevent/kevent_inode.c @@ -46,14 +46,18 @@ static int kevent_inode_enqueue(struct k inode = igrab(file->f_dentry->d_inode); if (!inode) goto err_out_fput; - - err = kevent_storage_enqueue(&inode->st, k); - if (err) + + k = kevent_recreate(k, GFP_KERNEL); + if (!k) { + err = -ENOMEM; goto err_out_iput; + } + + kevent_storage_enqueue(&inode->st, k); fput_light(file, fput_needed); return 0; - + err_out_iput: iput(inode); err_out_fput: diff --git a/kernel/kevent/kevent_poll.c b/kernel/kevent/kevent_poll.c index 7149770..0544ec8 100644 --- a/kernel/kevent/kevent_poll.c +++ b/kernel/kevent/kevent_poll.c @@ -54,14 +54,9 @@ static int kevent_poll_wait_callback(wai struct kevent_poll_wait_container *cont = container_of(wait, struct kevent_poll_wait_container, wait); struct kevent *k = cont->k; struct file *file = k->st->origin; - unsigned long flags; - u32 revents, event; - + u32 revents; + revents = file->f_op->poll(file, NULL); - spin_lock_irqsave(&k->lock, flags); - event = k->event.event; - spin_unlock_irqrestore(&k->lock, flags); - kevent_storage_ready(k->st, NULL, revents); return 0; @@ -94,7 +89,7 @@ static void kevent_poll_qproc(struct fil static int kevent_poll_enqueue(struct kevent *k) { struct file *file; - int err, ready = 0; + int err; unsigned int revents; struct kevent_poll_ctl ctl; struct kevent_poll_private *priv; @@ -115,20 +110,27 @@ static int kevent_poll_enqueue(struct ke spin_lock_init(&priv->container_lock); INIT_LIST_HEAD(&priv->container_list); + k = kevent_recreate(k, GFP_KERNEL); + if (!k) { + err = -ENOMEM; + goto err_out_free; + } + k->priv = priv; + k->st = &file->st; ctl.k = k; init_poll_funcptr(&ctl.pt, &kevent_poll_qproc); + + kevent_storage_enqueue(&file->st, k); revents = file->f_op->poll(file, &ctl.pt); if (revents & k->event.event) - ready = 1; + err = 1; + else + err = 0; - err = kevent_storage_enqueue(&file->st, k); - if (err) - goto err_out_free; - - return ready; + return err; err_out_free: kmem_cache_free(kevent_poll_priv_cache, priv); diff --git a/kernel/kevent/kevent_socket.c b/kernel/kevent/kevent_socket.c index d3938a2..16ffc93 100644 --- a/kernel/kevent/kevent_socket.c +++ b/kernel/kevent/kevent_socket.c @@ -77,19 +77,24 @@ int kevent_socket_enqueue(struct kevent if (!inode) goto err_out_fput; - err = kevent_storage_enqueue(&inode->st, k); - if (err) + k = kevent_recreate(k, GFP_KERNEL); + if (!k) { + err = -ENOMEM; goto err_out_iput; + } + + kevent_storage_enqueue(&inode->st, k); err = k->callback(k); if (err) - goto err_out_dequeue; + goto err_out_free; fput_light(file, fput_needed); - return err; + return 0; -err_out_dequeue: +err_out_free: kevent_storage_dequeue(k->st, k); + kevent_finish_user(k, 1, 0); err_out_iput: iput(inode); err_out_fput: diff --git a/kernel/kevent/kevent_timer.c b/kernel/kevent/kevent_timer.c index 2f9291b..f678174 100644 --- a/kernel/kevent/kevent_timer.c +++ b/kernel/kevent/kevent_timer.c @@ -56,17 +56,19 @@ static int kevent_timer_enqueue(struct k err = kevent_storage_init(t, st); if (err) goto err_out_free; + + k = kevent_recreate(k, GFP_KERNEL); + if (!k) { + err = -ENOMEM; + goto err_out_free; + } - err = kevent_storage_enqueue(st, k); - if (err) - goto err_out_st_fini; + kevent_storage_enqueue(st, k); add_timer(t); return 0; -err_out_st_fini: - kevent_storage_fini(st); err_out_free: kfree(t); diff --git a/kernel/kevent/kevent_user.c b/kernel/kevent/kevent_user.c index 2f0a124..99fbf29 100644 --- a/kernel/kevent/kevent_user.c +++ b/kernel/kevent/kevent_user.c @@ -102,7 +102,6 @@ static struct kevent_user *kevent_user_a u->kevent_num = 0; init_MUTEX(&u->ctl_mutex); - init_MUTEX(&u->wait_mutex); init_waitqueue_head(&u->wait); u->max_ready_num = 0; @@ -151,7 +150,7 @@ static inline unsigned int kevent_user_h #else static inline unsigned int kevent_user_hash(struct ukevent *uk) { - return jhash_1word(uk->id.raw[0], uk->id.raw[1]) & KEVENT_HASH_MASK; + return jhash_1word(uk->id.raw[0], 0) & KEVENT_HASH_MASK; } #endif @@ -160,7 +159,7 @@ static inline unsigned int kevent_user_h * dequeue it from storage and decrease user's reference counter, * since this kevent does not exist anymore. That is why it is freed here. */ -static void kevent_finish_user(struct kevent *k, int lock, int deq) +void kevent_finish_user(struct kevent *k, int lock, int deq) { struct kevent_user *u = k->user; unsigned long flags; @@ -189,7 +188,9 @@ static void kevent_finish_user(struct ke spin_unlock_irqrestore(&u->ready_lock, flags); kevent_user_put(u); - kevent_free(k); + + if (!(k->event.ret_flags & KEVENT_RET_STACK)) + kevent_free(k); } /* @@ -228,8 +229,12 @@ static struct kevent *__kevent_search(st list_for_each_entry(k, &l->kevent_list, kevent_entry) { spin_lock(&k->lock); +#if 0 if (k->event.user[0] == uk->user[0] && k->event.user[1] == uk->user[1] && k->event.id.raw[0] == uk->id.raw[0] && k->event.id.raw[1] == uk->id.raw[1]) { +#else + if (k->event.id.raw[0] == uk->id.raw[0]) { +#endif found = 1; spin_unlock(&k->lock); break; @@ -247,20 +252,21 @@ static int kevent_modify(struct ukevent struct kevent_list *l = &u->kqueue[hash]; int err = -ENODEV; unsigned long flags; - + spin_lock_irqsave(&l->kevent_lock, flags); k = __kevent_search(l, uk, u); + spin_unlock_irqrestore(&l->kevent_lock, flags); + if (k) { spin_lock(&k->lock); - k->event.event = uk->event; - k->event.req_flags = uk->req_flags; + memcpy(&k->event, uk, sizeof(struct ukevent)); k->event.ret_flags = 0; spin_unlock(&k->lock); - kevent_requeue(k); - err = 0; + err = kevent_requeue(k); + if (err) + memcpy(uk, &k->event, sizeof(struct ukevent)); } - spin_unlock_irqrestore(&l->kevent_lock, flags); - + return err; } @@ -274,11 +280,11 @@ static int kevent_remove(struct ukevent spin_lock_irqsave(&l->kevent_lock, flags); k = __kevent_search(l, uk, u); + spin_unlock_irqrestore(&l->kevent_lock, flags); if (k) { - kevent_finish_user(k, 0, 1); + kevent_finish_user(k, 1, 1); err = 0; } - spin_unlock_irqrestore(&l->kevent_lock, flags); return err; } @@ -293,12 +299,14 @@ static int kevent_user_release(struct in struct kevent *k, *n; int i; + down(&u->ctl_mutex); for (i=0; i<KEVENT_HASH_MASK+1; ++i) { struct kevent_list *l = &u->kqueue[i]; list_for_each_entry_safe(k, n, &l->kevent_list, kevent_entry) kevent_finish_user(k, 1, 1); } + up(&u->ctl_mutex); kevent_user_put(u); file->private_data = NULL; @@ -320,9 +328,11 @@ static int kevent_user_ctl_modify(struct break; } - if (kevent_modify(&uk, u)) + err = kevent_modify(&uk, u); + if (err) + uk.ret_flags |= KEVENT_RET_DONE; + if (unlikely(err < 0)) uk.ret_flags |= KEVENT_RET_BROKEN; - uk.ret_flags |= KEVENT_RET_DONE; if (copy_to_user(arg, &uk, sizeof(struct ukevent))) { err = -EINVAL; @@ -369,52 +379,72 @@ static int kevent_user_ctl_remove(struct return err; } -int kevent_user_add_ukevent(struct ukevent *uk, struct kevent_user *u) +struct kevent *kevent_recreate(struct kevent *kevent, gfp_t mask) { struct kevent *k; + unsigned long flags; + unsigned int hash = kevent_user_hash(&kevent->event); + struct kevent_list *l = &kevent->user->kqueue[hash]; + + k = kevent; + if (kevent->event.ret_flags & KEVENT_RET_STACK) { + k = kevent_alloc(mask); + if (!k) + return NULL; + memcpy(k, kevent, sizeof(struct kevent)); + k->event.ret_flags &= ~KEVENT_RET_STACK; + } else if (!(k->event.req_flags & KEVENT_REQ_ENQUEUE)) + return k; + + spin_lock_irqsave(&l->kevent_lock, flags); + list_add_tail(&k->kevent_entry, &l->kevent_list); + k->user->kevent_num++; + kevent_user_get(k->user); + spin_unlock_irqrestore(&l->kevent_lock, flags); + + return k; +} + +int kevent_user_add_ukevent(struct ukevent *uk, struct kevent_user *u) +{ + struct kevent *k, kev; int err; - k = kevent_alloc(GFP_KERNEL); - if (!k) { - err = -ENOMEM; - goto err_out_exit; + k = &kev; + + uk->ret_flags = KEVENT_RET_STACK; + uk->ret_data[0] = 0; + + if (uk->req_flags & KEVENT_REQ_ENQUEUE) { + uk->req_flags &= ~KEVENT_REQ_ONESHOT; + uk->ret_flags = 0; + + k = kevent_alloc(GFP_KERNEL); + if (!k) + return -ENOMEM; } - memcpy(&k->event, uk, sizeof(struct ukevent)); + memset(k, 0, sizeof(struct kevent)); - k->event.ret_flags = 0; - k->event.ret_data[0] = 0; - k->event.ret_data[1] = 0; + memcpy(&k->event, uk, sizeof(struct ukevent)); err = kevent_init(k); if (err) { - kevent_free(k); + if (uk->req_flags & KEVENT_REQ_ENQUEUE) + kevent_free(k); goto err_out_exit; } k->user = u; #ifdef CONFIG_KEVENT_USER_STAT u->total++; #endif - { - unsigned long flags; - unsigned int hash = kevent_user_hash(&k->event); - struct kevent_list *l = &u->kqueue[hash]; - - spin_lock_irqsave(&l->kevent_lock, flags); - list_add_tail(&k->kevent_entry, &l->kevent_list); - u->kevent_num++; - kevent_user_get(u); - spin_unlock_irqrestore(&l->kevent_lock, flags); - } - err = kevent_enqueue(k); if (err) { memcpy(uk, &k->event, sizeof(struct ukevent)); if (err < 0) uk->ret_flags |= KEVENT_RET_BROKEN; uk->ret_flags |= KEVENT_RET_DONE; - kevent_finish_user(k, 1, 0); - } + } err_out_exit: return err; @@ -440,10 +470,14 @@ static int kevent_user_ctl_add(struct ke orig = arg; ctl_addr = arg - sizeof(struct kevent_user_control); -#if 1 +#if 0 err = -ENFILE; - if (u->kevent_num + ctl->num >= 1024) + if (u->kevent_num + ctl->num >= 10240) { +#ifdef CONFIG_KEVENT_USER_STAT + u->enfile++; +#endif goto err_out_remove; + } #endif for (i=0; i<ctl->num; ++i) { if (copy_from_user(&uk, arg, sizeof(struct ukevent))) { @@ -493,17 +527,21 @@ static int kevent_user_wait(struct file int cerr = 0, num = 0; void __user *ptr = arg + sizeof(struct kevent_user_control); - if (down_interruptible(&u->ctl_mutex)) - return -ERESTARTSYS; - if (!(file->f_flags & O_NONBLOCK)) { - if (ctl->timeout) + if (ctl->timeout) { wait_event_interruptible_timeout(u->wait, u->ready_num >= ctl->num, msecs_to_jiffies(ctl->timeout)); - else + //printk("%s: 1 timeout=%u, ready_num=%u, ctl->num=%u.\n", __func__, ctl->timeout, u->ready_num, ctl->num); + } else { wait_event_interruptible_timeout(u->wait, u->ready_num > 0, msecs_to_jiffies(1000)); + //printk("%s: 2 timeout=%u, ready_num=%u, ctl->num=%u.\n", __func__, 1000, u->ready_num, ctl->num); + } } + + if (down_interruptible(&u->ctl_mutex)) + return -ERESTARTSYS; + while (num < ctl->num && ((k = kqueue_dequeue_ready(u)) != NULL)) { if (copy_to_user(ptr + num*sizeof(struct ukevent), &k->event, sizeof(struct ukevent))) cerr = -EINVAL; -- Evgeniy Polyakov - To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to [EMAIL PROTECTED] More majordomo info at http://vger.kernel.org/majordomo-info.html