> On 18 Jan 2024, at 5:08 PM, Alexey Kuznetsov <kuz...@virtuozzo.com> wrote:
> 
>> Is it difficult to separate the verification of inputs, such as fds, size of 
>> src buffs against dest buffs etc, from the process of copying data? Similar 
>> to what’s been done in ‘copy_out_args()’.
> 
> I am not sure I understand what you mean here. Show, plz.
> 
> Do you really think copy_out_args is even "readable"? :-)
> From my pov it is awful. Actually, I hold 4d patch in series
> which replaces copy_out_args with normal human intellibible
> function which copies data 10% faster. And I do not think
> it was by accident, ugly code is usually a bad code.

OK, what I meant was can we break the big for loop in two smaller ones.  In the 
first one, just verify whether inputs are valid, something like 

+       for (i = 0; i < nsplices; i++) {
+               void *src, *dst;
+               struct fd f = fdget(fdarr[i]);
+
+               if (f.file) {
+                       unsigned int head, tail, mask;
+
+                       if (unlikely(f.file->f_op != fuse_g_splice_ops)) {
+                               if (fuse_g_splice_ops == NULL) {
+                                       struct file *probe_files[2];
+
+                                       if (create_pipe_files(probe_files, 0)) {
+                                               err = -EBADF;
+                                               goto out;
+                                       }
+                                       fuse_g_splice_ops = 
probe_files[0]->f_op;
+                                       fput(probe_files[0]);
+                                       fput(probe_files[1]);
+                               }
+                               if (unlikely(f.file->f_op != 
fuse_g_splice_ops)) {
+                                       err = -EBADF;
+                                       goto out;
+                               }
+                       }
+
+                       pipe = f.file->private_data;
+
+                       if (pipe == NULL) {
+                               err = -EBADF;
+                               goto out;
+                       }
+               } else {
+                       err = -EBADF;
+                       goto out;
+               }
                
                /* count total size of inputs */

                fdput(f);
+       }
        /* check whether size of inputs exceeds available buff in req->ap here 
*/


after all checks pass, do data copying in the second loop.  Something like:

        for (i = 0; i < nsplices; i++) {
+               void *src, *dst;
+               struct fd f = fdget(fdarr[i]);

+               pipe = f.file->private_data;
+               pipe_lock(pipe);
+
+               head = pipe->head;
+               mask = pipe->ring_size - 1;
+
+               for (tail = pipe->tail; tail != head; tail++) {
+                       struct page *ipage = pipe->bufs[tail & mask].page;
+                       int ioff = pipe->bufs[tail & mask].offset;
+                       int ilen = pipe->bufs[tail & mask].len;

+                       src = kmap_atomic(ipage);
+                       while (ilen > 0) {
+                               int copy = ilen;

+                               if (doff >= dend) {
+                                       didx++;
+                                       dpage = ap->pages[didx];
+                                       doff = ap->descs[didx].offset;
+                                       dend = doff + ap->descs[didx].length;
+                               }
+
+                               if (copy > dend - doff)
+                                       copy = dend - doff;
+                                       dst = kmap_atomic(dpage);
+                                       memcpy(dst + doff, src + ioff, copy);
+                                       kunmap_atomic(dst);
+
+                                       doff += copy;
+                                       ioff += copy;
+                                       ilen -= copy;
+                               }
                        }
+                       kunmap_atomic(src);
+                       put_page(ipage);
+                       pipe->bufs[tail & mask].ops = NULL;
+                       pipe->bufs[tail & mask].page = NULL;
+                       pipe->tail = tail + 1;
+               }
+               pipe_unlock(pipe);
+               fdput(f);
        }
 
This way we can fail before starting to copy data if there’s something wrong in 
inputs, however it does fdget/ fdput twice, I’m not sure how expensive 
fdget/fdput can be. 
If it’s expensive, maybe we can cache fdget() from the first loop and do 
fdput() after second loop. 

BTW I just found out there’s no fdput() in the error path. 

Lastly I was definitely not suggesting to do something similar in 
fuse_copy_args(), which I agree is not that readable.   



_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel

Reply via email to