diff options
author | Jens Axboe <axboe@suse.de> | 2006-03-23 13:57:55 -0500 |
---|---|---|
committer | Jens Axboe <axboe@suse.de> | 2006-03-23 13:57:55 -0500 |
commit | 221415d76231d9012871e6e6abcbad906c46626a (patch) | |
tree | fd045e6cac9f0e27608f56f4a5639790bc5fe331 /kernel | |
parent | b86ff981a8252d83d6a7719ae09f3a05307e3592 (diff) |
[PATCH] relay: add sendfile() support
Signed-off-by: Jens Axboe <axboe@suse.de>
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/relay.c | 144 |
1 files changed, 115 insertions, 29 deletions
diff --git a/kernel/relay.c b/kernel/relay.c index 9358e8eb8476..fefe2b2a7277 100644 --- a/kernel/relay.c +++ b/kernel/relay.c | |||
@@ -95,15 +95,16 @@ int relay_mmap_buf(struct rchan_buf *buf, struct vm_area_struct *vma) | |||
95 | * @buf: the buffer struct | 95 | * @buf: the buffer struct |
96 | * @size: total size of the buffer | 96 | * @size: total size of the buffer |
97 | * | 97 | * |
98 | * Returns a pointer to the resulting buffer, NULL if unsuccessful | 98 | * Returns a pointer to the resulting buffer, NULL if unsuccessful. The |
99 | * passed in size will get page aligned, if it isn't already. | ||
99 | */ | 100 | */ |
100 | static void *relay_alloc_buf(struct rchan_buf *buf, unsigned long size) | 101 | static void *relay_alloc_buf(struct rchan_buf *buf, size_t *size) |
101 | { | 102 | { |
102 | void *mem; | 103 | void *mem; |
103 | unsigned int i, j, n_pages; | 104 | unsigned int i, j, n_pages; |
104 | 105 | ||
105 | size = PAGE_ALIGN(size); | 106 | *size = PAGE_ALIGN(*size); |
106 | n_pages = size >> PAGE_SHIFT; | 107 | n_pages = *size >> PAGE_SHIFT; |
107 | 108 | ||
108 | buf->page_array = kcalloc(n_pages, sizeof(struct page *), GFP_KERNEL); | 109 | buf->page_array = kcalloc(n_pages, sizeof(struct page *), GFP_KERNEL); |
109 | if (!buf->page_array) | 110 | if (!buf->page_array) |
@@ -118,7 +119,7 @@ static void *relay_alloc_buf(struct rchan_buf *buf, unsigned long size) | |||
118 | if (!mem) | 119 | if (!mem) |
119 | goto depopulate; | 120 | goto depopulate; |
120 | 121 | ||
121 | memset(mem, 0, size); | 122 | memset(mem, 0, *size); |
122 | buf->page_count = n_pages; | 123 | buf->page_count = n_pages; |
123 | return mem; | 124 | return mem; |
124 | 125 | ||
@@ -146,7 +147,7 @@ struct rchan_buf *relay_create_buf(struct rchan *chan) | |||
146 | if (!buf->padding) | 147 | if (!buf->padding) |
147 | goto free_buf; | 148 | goto free_buf; |
148 | 149 | ||
149 | buf->start = relay_alloc_buf(buf, chan->alloc_size); | 150 | buf->start = relay_alloc_buf(buf, &chan->alloc_size); |
150 | if (!buf->start) | 151 | if (!buf->start) |
151 | goto free_buf; | 152 | goto free_buf; |
152 | 153 | ||
@@ -543,6 +544,9 @@ size_t relay_switch_subbuf(struct rchan_buf *buf, size_t length) | |||
543 | old_subbuf = buf->subbufs_produced % buf->chan->n_subbufs; | 544 | old_subbuf = buf->subbufs_produced % buf->chan->n_subbufs; |
544 | buf->padding[old_subbuf] = buf->prev_padding; | 545 | buf->padding[old_subbuf] = buf->prev_padding; |
545 | buf->subbufs_produced++; | 546 | buf->subbufs_produced++; |
547 | buf->dentry->d_inode->i_size += buf->chan->subbuf_size - | ||
548 | buf->padding[old_subbuf]; | ||
549 | smp_mb(); | ||
546 | if (waitqueue_active(&buf->read_wait)) { | 550 | if (waitqueue_active(&buf->read_wait)) { |
547 | PREPARE_WORK(&buf->wake_readers, wakeup_readers, buf); | 551 | PREPARE_WORK(&buf->wake_readers, wakeup_readers, buf); |
548 | schedule_delayed_work(&buf->wake_readers, 1); | 552 | schedule_delayed_work(&buf->wake_readers, 1); |
@@ -757,37 +761,33 @@ static void relay_file_read_consume(struct rchan_buf *buf, | |||
757 | */ | 761 | */ |
758 | static int relay_file_read_avail(struct rchan_buf *buf, size_t read_pos) | 762 | static int relay_file_read_avail(struct rchan_buf *buf, size_t read_pos) |
759 | { | 763 | { |
760 | size_t bytes_produced, bytes_consumed, write_offset; | ||
761 | size_t subbuf_size = buf->chan->subbuf_size; | 764 | size_t subbuf_size = buf->chan->subbuf_size; |
762 | size_t n_subbufs = buf->chan->n_subbufs; | 765 | size_t n_subbufs = buf->chan->n_subbufs; |
763 | size_t produced = buf->subbufs_produced % n_subbufs; | 766 | size_t produced = buf->subbufs_produced; |
764 | size_t consumed = buf->subbufs_consumed % n_subbufs; | 767 | size_t consumed = buf->subbufs_consumed; |
765 | 768 | ||
766 | write_offset = buf->offset > subbuf_size ? subbuf_size : buf->offset; | 769 | relay_file_read_consume(buf, read_pos, 0); |
767 | 770 | ||
768 | if (consumed > produced) { | 771 | if (unlikely(buf->offset > subbuf_size)) { |
769 | if ((produced > n_subbufs) && | 772 | if (produced == consumed) |
770 | (produced + n_subbufs - consumed <= n_subbufs)) | 773 | return 0; |
771 | produced += n_subbufs; | 774 | return 1; |
772 | } else if (consumed == produced) { | ||
773 | if (buf->offset > subbuf_size) { | ||
774 | produced += n_subbufs; | ||
775 | if (buf->subbufs_produced == buf->subbufs_consumed) | ||
776 | consumed += n_subbufs; | ||
777 | } | ||
778 | } | 775 | } |
779 | 776 | ||
780 | if (buf->offset > subbuf_size) | 777 | if (unlikely(produced - consumed >= n_subbufs)) { |
781 | bytes_produced = (produced - 1) * subbuf_size + write_offset; | 778 | consumed = (produced / n_subbufs) * n_subbufs; |
782 | else | 779 | buf->subbufs_consumed = consumed; |
783 | bytes_produced = produced * subbuf_size + write_offset; | 780 | } |
784 | bytes_consumed = consumed * subbuf_size + buf->bytes_consumed; | 781 | |
785 | 782 | produced = (produced % n_subbufs) * subbuf_size + buf->offset; | |
786 | if (bytes_produced == bytes_consumed) | 783 | consumed = (consumed % n_subbufs) * subbuf_size + buf->bytes_consumed; |
784 | |||
785 | if (consumed > produced) | ||
786 | produced += n_subbufs * subbuf_size; | ||
787 | |||
788 | if (consumed == produced) | ||
787 | return 0; | 789 | return 0; |
788 | 790 | ||
789 | relay_file_read_consume(buf, read_pos, 0); | ||
790 | |||
791 | return 1; | 791 | return 1; |
792 | } | 792 | } |
793 | 793 | ||
@@ -908,6 +908,91 @@ out: | |||
908 | return ret; | 908 | return ret; |
909 | } | 909 | } |
910 | 910 | ||
911 | static ssize_t relay_file_sendsubbuf(struct file *filp, loff_t *ppos, | ||
912 | size_t count, read_actor_t actor, | ||
913 | void *target) | ||
914 | { | ||
915 | struct rchan_buf *buf = filp->private_data; | ||
916 | read_descriptor_t desc; | ||
917 | size_t read_start, avail; | ||
918 | unsigned long pidx, poff; | ||
919 | unsigned int subbuf_pages; | ||
920 | ssize_t ret = 0; | ||
921 | |||
922 | if (!relay_file_read_avail(buf, *ppos)) | ||
923 | return 0; | ||
924 | |||
925 | read_start = relay_file_read_start_pos(*ppos, buf); | ||
926 | avail = relay_file_read_subbuf_avail(read_start, buf); | ||
927 | if (!avail) | ||
928 | return 0; | ||
929 | |||
930 | count = min(count, avail); | ||
931 | |||
932 | desc.written = 0; | ||
933 | desc.count = count; | ||
934 | desc.arg.data = target; | ||
935 | desc.error = 0; | ||
936 | |||
937 | subbuf_pages = buf->chan->alloc_size >> PAGE_SHIFT; | ||
938 | pidx = (read_start / PAGE_SIZE) % subbuf_pages; | ||
939 | poff = read_start & ~PAGE_MASK; | ||
940 | while (count) { | ||
941 | struct page *p = buf->page_array[pidx]; | ||
942 | unsigned int len; | ||
943 | |||
944 | len = PAGE_SIZE - poff; | ||
945 | if (len > count) | ||
946 | len = count; | ||
947 | |||
948 | len = actor(&desc, p, poff, len); | ||
949 | |||
950 | if (desc.error) { | ||
951 | if (!ret) | ||
952 | ret = desc.error; | ||
953 | break; | ||
954 | } | ||
955 | |||
956 | count -= len; | ||
957 | ret += len; | ||
958 | poff = 0; | ||
959 | pidx = (pidx + 1) % subbuf_pages; | ||
960 | } | ||
961 | |||
962 | if (ret > 0) { | ||
963 | relay_file_read_consume(buf, read_start, ret); | ||
964 | *ppos = relay_file_read_end_pos(buf, read_start, ret); | ||
965 | } | ||
966 | |||
967 | return ret; | ||
968 | } | ||
969 | |||
970 | static ssize_t relay_file_sendfile(struct file *filp, loff_t *ppos, | ||
971 | size_t count, read_actor_t actor, | ||
972 | void *target) | ||
973 | { | ||
974 | ssize_t sent = 0, ret = 0; | ||
975 | |||
976 | if (!count) | ||
977 | return 0; | ||
978 | |||
979 | mutex_lock(&filp->f_dentry->d_inode->i_mutex); | ||
980 | |||
981 | do { | ||
982 | ret = relay_file_sendsubbuf(filp, ppos, count, actor, target); | ||
983 | if (ret < 0) { | ||
984 | if (!sent) | ||
985 | sent = ret; | ||
986 | break; | ||
987 | } | ||
988 | count -= ret; | ||
989 | sent += ret; | ||
990 | } while (count && ret); | ||
991 | |||
992 | mutex_unlock(&filp->f_dentry->d_inode->i_mutex); | ||
993 | return sent; | ||
994 | } | ||
995 | |||
911 | struct file_operations relay_file_operations = { | 996 | struct file_operations relay_file_operations = { |
912 | .open = relay_file_open, | 997 | .open = relay_file_open, |
913 | .poll = relay_file_poll, | 998 | .poll = relay_file_poll, |
@@ -915,5 +1000,6 @@ struct file_operations relay_file_operations = { | |||
915 | .read = relay_file_read, | 1000 | .read = relay_file_read, |
916 | .llseek = no_llseek, | 1001 | .llseek = no_llseek, |
917 | .release = relay_file_release, | 1002 | .release = relay_file_release, |
1003 | .sendfile = relay_file_sendfile, | ||
918 | }; | 1004 | }; |
919 | EXPORT_SYMBOL_GPL(relay_file_operations); | 1005 | EXPORT_SYMBOL_GPL(relay_file_operations); |