diff options
| -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); |
