Building pagetables is certainly more expensive than not doing so; this patch just separates the concepts of get-handle-to-kernel-memory and pin-kernel-memory-for-direct-access, which seems entirely sensible. See also sendfile(), which is a special case of splice().
I *think* splice() should be able to accomplish the same feats as this patchset providing both ends can provide an FD that adequately represents what we want to do with the buffer; e.g. in the case that we're piping network packets to a video device, the video driver needs to be able to provide an FD representing the target video buffer, pixel format, etc, so something like:
// fd is a socket
int fd2 = ioctl(video_control_fd, IOCTL_GET_SINK_FD, /* description of sink buffer */);
splice(fd, fd2, ...);
In the language of this patchset the network driver would provide an ioctl that yields a buffer FD, and the video driver would provide one that copies buffer data into specified video memory.
int fd = ioctl(socket_or_if_control_fd, IOCTL_GET_PACKET_BUFFER, &bufferfd);
ioctl(video_control_fd, IOCTL_COPY_BUFFER_TO_VMEM, fd2, /* buffer description */);
Basically a splice()-based approach would need more ioctls in order to establish something that looks like a "connection endpoint" that means what we want it to, but mean less FD table operations if we want to repeatedly perform a similar operation (likely?), whilst an ioctl() + fd-per-buffer approach means lots of fd table operations (efficient locks in the table?) but less syscalls if we're routing buffers in a way that would effectively compel a splice operation to create a new channel per operation.