aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--kernel/relay.c144
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 */
100static void *relay_alloc_buf(struct rchan_buf *buf, unsigned long size) 101static 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 */
758static int relay_file_read_avail(struct rchan_buf *buf, size_t read_pos) 762static 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
911static 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
970static 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
911struct file_operations relay_file_operations = { 996struct 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};
919EXPORT_SYMBOL_GPL(relay_file_operations); 1005EXPORT_SYMBOL_GPL(relay_file_operations);