On Sat, Nov 21, 2020 at 02:13:21PM +0000, David Howells wrote: > I had a go switching the iov_iter stuff away from using a type bitmask to > using an ops table to get rid of the if-if-if-if chains that are all over > the place. After I pushed it, someone pointed me at Pavel's two patches. > > I have another iterator class that I want to add - which would lengthen the > if-if-if-if chains. A lot of the time, there's a conditional clause at the > beginning of a function that just jumps off to a type-specific handler or > to reject the operation for that type. An ops table can just point to that > instead. So, given the performance problem, how about turning this inside out? struct iov_step { union { void *kaddr; void __user *uaddr; }; unsigned int len; bool user_addr; bool kmap; struct page *page; }; bool iov_iterate(struct iov_step *step, struct iov_iter *i, size_t max) { if (step->page) kunmap(page) else if (step->kmap) kunmap_atomic(step->kaddr); if (max == 0) return false; if (i->type & ITER_IOVEC) { step->user_addr = true; step->uaddr = i->iov.iov_base + i->iov_offset; return true; } if (i->type & ITER_BVEC) { ... get the page ... } else if (i->type & ITER_KVEC) { ... get the page ... } else ... kmap or kmap_atomic as appropriate ... ...set kaddr & len ... return true; } size_t copy_from_iter(void *addr, size_t bytes, struct iov_iter *i) { struct iov_step step = {}; while (iov_iterate(&step, i, bytes)) { if (user_addr) copy_from_user(addr, step.uaddr, step.len); else memcpy(addr, step.kaddr, step.len); bytes -= step.len; } }