diff options
Diffstat (limited to 'kernel/relay.c')
-rw-r--r-- | kernel/relay.c | 205 |
1 files changed, 155 insertions, 50 deletions
diff --git a/kernel/relay.c b/kernel/relay.c index 95db8c79fe8f..3b299fb3855c 100644 --- a/kernel/relay.c +++ b/kernel/relay.c | |||
@@ -21,6 +21,7 @@ | |||
21 | #include <linux/vmalloc.h> | 21 | #include <linux/vmalloc.h> |
22 | #include <linux/mm.h> | 22 | #include <linux/mm.h> |
23 | #include <linux/cpu.h> | 23 | #include <linux/cpu.h> |
24 | #include <linux/splice.h> | ||
24 | 25 | ||
25 | /* list of open channels, for cpu hotplug */ | 26 | /* list of open channels, for cpu hotplug */ |
26 | static DEFINE_MUTEX(relay_channels_mutex); | 27 | static DEFINE_MUTEX(relay_channels_mutex); |
@@ -121,6 +122,7 @@ static void *relay_alloc_buf(struct rchan_buf *buf, size_t *size) | |||
121 | buf->page_array[i] = alloc_page(GFP_KERNEL); | 122 | buf->page_array[i] = alloc_page(GFP_KERNEL); |
122 | if (unlikely(!buf->page_array[i])) | 123 | if (unlikely(!buf->page_array[i])) |
123 | goto depopulate; | 124 | goto depopulate; |
125 | set_page_private(buf->page_array[i], (unsigned long)buf); | ||
124 | } | 126 | } |
125 | mem = vmap(buf->page_array, n_pages, VM_MAP, PAGE_KERNEL); | 127 | mem = vmap(buf->page_array, n_pages, VM_MAP, PAGE_KERNEL); |
126 | if (!mem) | 128 | if (!mem) |
@@ -970,43 +972,6 @@ static int subbuf_read_actor(size_t read_start, | |||
970 | return ret; | 972 | return ret; |
971 | } | 973 | } |
972 | 974 | ||
973 | /* | ||
974 | * subbuf_send_actor - send up to one subbuf's worth of data | ||
975 | */ | ||
976 | static int subbuf_send_actor(size_t read_start, | ||
977 | struct rchan_buf *buf, | ||
978 | size_t avail, | ||
979 | read_descriptor_t *desc, | ||
980 | read_actor_t actor) | ||
981 | { | ||
982 | unsigned long pidx, poff; | ||
983 | unsigned int subbuf_pages; | ||
984 | int ret = 0; | ||
985 | |||
986 | subbuf_pages = buf->chan->alloc_size >> PAGE_SHIFT; | ||
987 | pidx = (read_start / PAGE_SIZE) % subbuf_pages; | ||
988 | poff = read_start & ~PAGE_MASK; | ||
989 | while (avail) { | ||
990 | struct page *p = buf->page_array[pidx]; | ||
991 | unsigned int len; | ||
992 | |||
993 | len = PAGE_SIZE - poff; | ||
994 | if (len > avail) | ||
995 | len = avail; | ||
996 | |||
997 | len = actor(desc, p, poff, len); | ||
998 | if (desc->error) | ||
999 | break; | ||
1000 | |||
1001 | avail -= len; | ||
1002 | ret += len; | ||
1003 | poff = 0; | ||
1004 | pidx = (pidx + 1) % subbuf_pages; | ||
1005 | } | ||
1006 | |||
1007 | return ret; | ||
1008 | } | ||
1009 | |||
1010 | typedef int (*subbuf_actor_t) (size_t read_start, | 975 | typedef int (*subbuf_actor_t) (size_t read_start, |
1011 | struct rchan_buf *buf, | 976 | struct rchan_buf *buf, |
1012 | size_t avail, | 977 | size_t avail, |
@@ -1067,19 +1032,159 @@ static ssize_t relay_file_read(struct file *filp, | |||
1067 | NULL, &desc); | 1032 | NULL, &desc); |
1068 | } | 1033 | } |
1069 | 1034 | ||
1070 | static ssize_t relay_file_sendfile(struct file *filp, | 1035 | static void relay_consume_bytes(struct rchan_buf *rbuf, int bytes_consumed) |
1071 | loff_t *ppos, | ||
1072 | size_t count, | ||
1073 | read_actor_t actor, | ||
1074 | void *target) | ||
1075 | { | 1036 | { |
1076 | read_descriptor_t desc; | 1037 | rbuf->bytes_consumed += bytes_consumed; |
1077 | desc.written = 0; | 1038 | |
1078 | desc.count = count; | 1039 | if (rbuf->bytes_consumed >= rbuf->chan->subbuf_size) { |
1079 | desc.arg.data = target; | 1040 | relay_subbufs_consumed(rbuf->chan, rbuf->cpu, 1); |
1080 | desc.error = 0; | 1041 | rbuf->bytes_consumed %= rbuf->chan->subbuf_size; |
1081 | return relay_file_read_subbufs(filp, ppos, subbuf_send_actor, | 1042 | } |
1082 | actor, &desc); | 1043 | } |
1044 | |||
1045 | static void relay_pipe_buf_release(struct pipe_inode_info *pipe, | ||
1046 | struct pipe_buffer *buf) | ||
1047 | { | ||
1048 | struct rchan_buf *rbuf; | ||
1049 | |||
1050 | rbuf = (struct rchan_buf *)page_private(buf->page); | ||
1051 | relay_consume_bytes(rbuf, buf->private); | ||
1052 | } | ||
1053 | |||
1054 | static struct pipe_buf_operations relay_pipe_buf_ops = { | ||
1055 | .can_merge = 0, | ||
1056 | .map = generic_pipe_buf_map, | ||
1057 | .unmap = generic_pipe_buf_unmap, | ||
1058 | .confirm = generic_pipe_buf_confirm, | ||
1059 | .release = relay_pipe_buf_release, | ||
1060 | .steal = generic_pipe_buf_steal, | ||
1061 | .get = generic_pipe_buf_get, | ||
1062 | }; | ||
1063 | |||
1064 | /** | ||
1065 | * subbuf_splice_actor - splice up to one subbuf's worth of data | ||
1066 | */ | ||
1067 | static int subbuf_splice_actor(struct file *in, | ||
1068 | loff_t *ppos, | ||
1069 | struct pipe_inode_info *pipe, | ||
1070 | size_t len, | ||
1071 | unsigned int flags, | ||
1072 | int *nonpad_ret) | ||
1073 | { | ||
1074 | unsigned int pidx, poff, total_len, subbuf_pages, ret; | ||
1075 | struct rchan_buf *rbuf = in->private_data; | ||
1076 | unsigned int subbuf_size = rbuf->chan->subbuf_size; | ||
1077 | size_t read_start = ((size_t)*ppos) % rbuf->chan->alloc_size; | ||
1078 | size_t read_subbuf = read_start / subbuf_size; | ||
1079 | size_t padding = rbuf->padding[read_subbuf]; | ||
1080 | size_t nonpad_end = read_subbuf * subbuf_size + subbuf_size - padding; | ||
1081 | struct page *pages[PIPE_BUFFERS]; | ||
1082 | struct partial_page partial[PIPE_BUFFERS]; | ||
1083 | struct splice_pipe_desc spd = { | ||
1084 | .pages = pages, | ||
1085 | .nr_pages = 0, | ||
1086 | .partial = partial, | ||
1087 | .flags = flags, | ||
1088 | .ops = &relay_pipe_buf_ops, | ||
1089 | }; | ||
1090 | |||
1091 | if (rbuf->subbufs_produced == rbuf->subbufs_consumed) | ||
1092 | return 0; | ||
1093 | |||
1094 | /* | ||
1095 | * Adjust read len, if longer than what is available | ||
1096 | */ | ||
1097 | if (len > (subbuf_size - read_start % subbuf_size)) | ||
1098 | len = subbuf_size - read_start % subbuf_size; | ||
1099 | |||
1100 | subbuf_pages = rbuf->chan->alloc_size >> PAGE_SHIFT; | ||
1101 | pidx = (read_start / PAGE_SIZE) % subbuf_pages; | ||
1102 | poff = read_start & ~PAGE_MASK; | ||
1103 | |||
1104 | for (total_len = 0; spd.nr_pages < subbuf_pages; spd.nr_pages++) { | ||
1105 | unsigned int this_len, this_end, private; | ||
1106 | unsigned int cur_pos = read_start + total_len; | ||
1107 | |||
1108 | if (!len) | ||
1109 | break; | ||
1110 | |||
1111 | this_len = min_t(unsigned long, len, PAGE_SIZE - poff); | ||
1112 | private = this_len; | ||
1113 | |||
1114 | spd.pages[spd.nr_pages] = rbuf->page_array[pidx]; | ||
1115 | spd.partial[spd.nr_pages].offset = poff; | ||
1116 | |||
1117 | this_end = cur_pos + this_len; | ||
1118 | if (this_end >= nonpad_end) { | ||
1119 | this_len = nonpad_end - cur_pos; | ||
1120 | private = this_len + padding; | ||
1121 | } | ||
1122 | spd.partial[spd.nr_pages].len = this_len; | ||
1123 | spd.partial[spd.nr_pages].private = private; | ||
1124 | |||
1125 | len -= this_len; | ||
1126 | total_len += this_len; | ||
1127 | poff = 0; | ||
1128 | pidx = (pidx + 1) % subbuf_pages; | ||
1129 | |||
1130 | if (this_end >= nonpad_end) { | ||
1131 | spd.nr_pages++; | ||
1132 | break; | ||
1133 | } | ||
1134 | } | ||
1135 | |||
1136 | if (!spd.nr_pages) | ||
1137 | return 0; | ||
1138 | |||
1139 | ret = *nonpad_ret = splice_to_pipe(pipe, &spd); | ||
1140 | if (ret < 0 || ret < total_len) | ||
1141 | return ret; | ||
1142 | |||
1143 | if (read_start + ret == nonpad_end) | ||
1144 | ret += padding; | ||
1145 | |||
1146 | return ret; | ||
1147 | } | ||
1148 | |||
1149 | static ssize_t relay_file_splice_read(struct file *in, | ||
1150 | loff_t *ppos, | ||
1151 | struct pipe_inode_info *pipe, | ||
1152 | size_t len, | ||
1153 | unsigned int flags) | ||
1154 | { | ||
1155 | ssize_t spliced; | ||
1156 | int ret; | ||
1157 | int nonpad_ret = 0; | ||
1158 | |||
1159 | ret = 0; | ||
1160 | spliced = 0; | ||
1161 | |||
1162 | while (len) { | ||
1163 | ret = subbuf_splice_actor(in, ppos, pipe, len, flags, &nonpad_ret); | ||
1164 | if (ret < 0) | ||
1165 | break; | ||
1166 | else if (!ret) { | ||
1167 | if (spliced) | ||
1168 | break; | ||
1169 | if (flags & SPLICE_F_NONBLOCK) { | ||
1170 | ret = -EAGAIN; | ||
1171 | break; | ||
1172 | } | ||
1173 | } | ||
1174 | |||
1175 | *ppos += ret; | ||
1176 | if (ret > len) | ||
1177 | len = 0; | ||
1178 | else | ||
1179 | len -= ret; | ||
1180 | spliced += nonpad_ret; | ||
1181 | nonpad_ret = 0; | ||
1182 | } | ||
1183 | |||
1184 | if (spliced) | ||
1185 | return spliced; | ||
1186 | |||
1187 | return ret; | ||
1083 | } | 1188 | } |
1084 | 1189 | ||
1085 | const struct file_operations relay_file_operations = { | 1190 | const struct file_operations relay_file_operations = { |
@@ -1089,7 +1194,7 @@ const struct file_operations relay_file_operations = { | |||
1089 | .read = relay_file_read, | 1194 | .read = relay_file_read, |
1090 | .llseek = no_llseek, | 1195 | .llseek = no_llseek, |
1091 | .release = relay_file_release, | 1196 | .release = relay_file_release, |
1092 | .sendfile = relay_file_sendfile, | 1197 | .splice_read = relay_file_splice_read, |
1093 | }; | 1198 | }; |
1094 | EXPORT_SYMBOL_GPL(relay_file_operations); | 1199 | EXPORT_SYMBOL_GPL(relay_file_operations); |
1095 | 1200 | ||