Skip to content

Commit 8401073

Browse files
gerd-rauschSomasundaram Krishnasamy
authored andcommitted
net/rds: make copy_page_from_iter and copy_page_to_iter stay within page boundaries (WORKAROUND!)
The original UEK-4.1 code allocated multiple pages via rds_page_remainder_alloc up to RDS_MAX_FRAG_SIZE (16kiB), as introduced in commit 23f90cc. Before commit 23f90cc, as well as the upstream-Linux version allocates single pages only. Since these multiple pages are allocated in a physically contiguous area (alloc_pages), neither the review nor tests revealed the fact that copy_page_from_iter does not support crossing page-boundaries: The code wasn't tested on platforms where HIGHMEM pages would have to be mapped into the kernel (kmap) explicitly, such as i386 (32bit) anymore. On those platforms it most likely would have just crashed! Only when commit 72e809e came along, that introduced an explicit check for not crossing page boundaries in iov_iter.c came along, did this problem become obvious. There's also the issue with this approach of allocating physically contiguous 16KiB areas: it does not back down and try to allocate smaller orders (down to 0), in case allocation failed. Also, copy_page_from_iter may not be the only function mapping HIGHMEM pages, hence unable to cross page boundaries (as kmap maps a single page). So the idea came up to simply revert commit 23f90cc, since it was also just introduced on behalf of the PSIF project, which is now defunct. Unfortunately and unexpectedly the old code (of allocating single pages) no longer works! That leads to the obvious conclusion: Some code change that happened after commit 23f90cc in UEK makes either an explicit assumption (without or with hidden note), or an implicit assumption (e.g. a one-off bug) about the presence of this multi-page allocation. Orabug: 27222215 Orabug: 27364391 Signed-off-by: Gerd Rausch <[email protected]> Reviewed-by: Sudhakar Dindukurti <[email protected]> Signed-off-by: Somasundaram Krishnasamy <[email protected]>
1 parent 32d8fc0 commit 8401073

File tree

2 files changed

+61
-59
lines changed

2 files changed

+61
-59
lines changed

net/rds/ib_recv.c

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -785,40 +785,40 @@ int rds_ib_inc_copy_to_user(struct rds_incoming *inc, struct iov_iter *to)
785785
struct rds_ib_connection *ic = inc->i_conn->c_transport_data;
786786
struct rds_ib_incoming *ibinc;
787787
struct rds_page_frag *frag;
788-
unsigned long to_copy;
789-
unsigned long frag_off = 0;
790-
int copied = 0;
791-
int ret;
792-
u32 len;
788+
size_t len, saved_len, to_copy, copied, offset, chunk;
793789

794790
ibinc = container_of(inc, struct rds_ib_incoming, ii_inc);
795791
frag = list_entry(ibinc->ii_frags.next, struct rds_page_frag, f_item);
792+
796793
len = be32_to_cpu(inc->i_hdr.h_len);
794+
if (iov_iter_count(to) < len)
795+
len = iov_iter_count(to);
797796

798-
while (iov_iter_count(to) && copied < len) {
799-
if (frag_off == ic->i_frag_sz) {
800-
frag = list_entry(frag->f_item.next,
801-
struct rds_page_frag, f_item);
802-
frag_off = 0;
803-
}
804-
to_copy = min_t(unsigned long, iov_iter_count(to),
805-
ic->i_frag_sz - frag_off);
806-
to_copy = min_t(unsigned long, to_copy, len - copied);
797+
saved_len = len;
798+
799+
while (len > 0) {
800+
to_copy = len < ic->i_frag_sz ? len : ic->i_frag_sz;
807801

808-
/* XXX needs + offset for multiple recvs per page */
809802
rds_stats_add(s_copy_to_user, to_copy);
810-
ret = copy_page_to_iter(sg_page(&frag->f_sg),
811-
frag->f_sg.offset + frag_off,
812-
to_copy,
813-
to);
814-
if (ret != to_copy)
815-
return -EFAULT;
816803

817-
frag_off += to_copy;
818-
copied += to_copy;
804+
for (copied = 0; copied < to_copy; copied += chunk) {
805+
offset = frag->f_sg.offset + copied;
806+
chunk = offset < PAGE_SIZE ? PAGE_SIZE - offset : PAGE_SIZE;
807+
if (copied + chunk > to_copy)
808+
chunk = to_copy - copied;
809+
810+
if (copy_page_to_iter(sg_page(&frag->f_sg) + (offset >> PAGE_SHIFT),
811+
offset & ~PAGE_MASK,
812+
chunk, to) != chunk)
813+
return -EFAULT;
814+
}
815+
816+
frag = list_entry(frag->f_item.next,
817+
struct rds_page_frag, f_item);
818+
len -= copied;
819819
}
820820

821-
return copied;
821+
return saved_len;
822822
}
823823

824824
/* ic starts out kzalloc()ed */

net/rds/message.c

Lines changed: 37 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -295,9 +295,8 @@ struct scatterlist *rds_message_alloc_sgs(struct rds_message *rm, int nents)
295295
int rds_message_copy_from_user(struct rds_message *rm, struct iov_iter *from,
296296
gfp_t gfp, bool large_page)
297297
{
298-
unsigned long to_copy, nbytes;
299-
unsigned long sg_off;
300298
struct scatterlist *sg;
299+
size_t to_copy, copied, offset, chunk;
301300
int ret = 0;
302301

303302
rm->m_inc.i_hdr.h_len = cpu_to_be32(iov_iter_count(from));
@@ -306,7 +305,6 @@ int rds_message_copy_from_user(struct rds_message *rm, struct iov_iter *from,
306305
* now allocate and copy in the data payload.
307306
*/
308307
sg = rm->data.op_sg;
309-
sg_off = 0; /* Dear gcc, sg->page will be null from kzalloc. */
310308

311309
while (iov_iter_count(from)) {
312310
if (!sg_page(sg)) {
@@ -318,65 +316,69 @@ int rds_message_copy_from_user(struct rds_message *rm, struct iov_iter *from,
318316
if (ret)
319317
return ret;
320318
rm->data.op_nents++;
321-
sg_off = 0;
322319
}
323320

324-
to_copy = min_t(unsigned long, iov_iter_count(from),
325-
sg->length - sg_off);
321+
to_copy = iov_iter_count(from);
322+
if (to_copy > sg->length)
323+
to_copy = sg->length;
326324

327325
rds_stats_add(s_copy_from_user, to_copy);
328-
nbytes = copy_page_from_iter(sg_page(sg), sg->offset + sg_off,
329-
to_copy, from);
330-
if (nbytes != to_copy)
331-
return -EFAULT;
332326

333-
sg_off += to_copy;
327+
for (copied = 0; copied < to_copy; copied += chunk) {
328+
offset = sg->offset + copied;
329+
chunk = offset < PAGE_SIZE ? PAGE_SIZE - offset : PAGE_SIZE;
330+
if (copied + chunk > to_copy)
331+
chunk = to_copy - copied;
334332

335-
if (sg_off == sg->length)
336-
sg++;
333+
if (copy_page_from_iter(sg_page(sg) + (offset >> PAGE_SHIFT),
334+
offset & ~PAGE_MASK,
335+
chunk, from) != chunk)
336+
return -EFAULT;
337+
}
338+
339+
sg++;
337340
}
338341

339-
return ret;
342+
return 0;
340343
}
341344

342345
int rds_message_inc_copy_to_user(struct rds_incoming *inc, struct iov_iter *to)
343346
{
344347
struct rds_message *rm;
345348
struct scatterlist *sg;
346-
unsigned long to_copy;
347-
unsigned long vec_off;
348-
int copied;
349-
int ret;
350-
u32 len;
349+
size_t len, saved_len, to_copy, copied, offset, chunk;
351350

352351
rm = container_of(inc, struct rds_message, m_inc);
353352
len = be32_to_cpu(rm->m_inc.i_hdr.h_len);
353+
if (iov_iter_count(to) < len)
354+
len = iov_iter_count(to);
354355

355356
sg = rm->data.op_sg;
356-
vec_off = 0;
357-
copied = 0;
358357

359-
while (iov_iter_count(to) && copied < len) {
360-
to_copy = min_t(unsigned long, iov_iter_count(to),
361-
sg->length - vec_off);
362-
to_copy = min_t(unsigned long, to_copy, len - copied);
358+
saved_len = len;
359+
360+
while (len > 0) {
361+
to_copy = len < sg->length ? len : sg->length;
363362

364363
rds_stats_add(s_copy_to_user, to_copy);
365-
ret = copy_page_to_iter(sg_page(sg), sg->offset + vec_off,
366-
to_copy, to);
367-
if (ret != to_copy)
368-
return -EFAULT;
369364

370-
vec_off += to_copy;
371-
copied += to_copy;
365+
for (copied = 0; copied < to_copy; copied += chunk) {
366+
offset = sg->offset + copied;
367+
chunk = offset < PAGE_SIZE ? PAGE_SIZE - offset : PAGE_SIZE;
368+
if (copied + chunk > to_copy)
369+
chunk = to_copy - copied;
372370

373-
if (vec_off == sg->length) {
374-
vec_off = 0;
375-
sg++;
371+
if (copy_page_to_iter(sg_page(sg) + (offset >> PAGE_SHIFT),
372+
offset & ~PAGE_MASK,
373+
chunk, to) != chunk)
374+
return -EFAULT;
376375
}
376+
377+
sg++;
378+
len -= copied;
377379
}
378380

379-
return copied;
381+
return saved_len;
380382
}
381383

382384
/*

0 commit comments

Comments
 (0)