Re: [PATCH 3/3] binder: defer copies of pre-patched txn data

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Wed, Nov 24, 2021 at 3:10 AM Dan Carpenter <dan.carpenter@xxxxxxxxxx> wrote:
>
> On Tue, Nov 23, 2021 at 11:17:37AM -0800, Todd Kjos wrote:
> > +/**
> > + * binder_do_deferred_txn_copies() - copy and fixup scatter-gather data
> > + * @alloc:   binder_alloc associated with @buffer
> > + * @buffer:  binder buffer in target process
> > + * @sgc_head:        list_head of scatter-gather copy list
> > + * @pf_head: list_head of pointer fixup list
> > + *
> > + * Processes all elements of @sgc_head, applying fixups from @pf_head
> > + * and copying the scatter-gather data from the source process' user
> > + * buffer to the target's buffer. It is expected that the list creation
> > + * and processing all occurs during binder_transaction() so these lists
> > + * are only accessed in local context.
> > + *
> > + * Return: 0=success, else -errno
>       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> This function is supposed to return negatives on error.
>
> > + */
> > +static int binder_do_deferred_txn_copies(struct binder_alloc *alloc,
> > +                                      struct binder_buffer *buffer,
> > +                                      struct list_head *sgc_head,
> > +                                      struct list_head *pf_head)
> > +{
> > +     int ret = 0;
> > +     struct list_head *entry, *tmp;
> > +     struct binder_ptr_fixup *pf =
> > +             list_first_entry_or_null(pf_head, struct binder_ptr_fixup,
> > +                                      node);
> > +
> > +     list_for_each_safe(entry, tmp, sgc_head) {
> > +             size_t bytes_copied = 0;
> > +             struct binder_sg_copy *sgc =
> > +                     container_of(entry, struct binder_sg_copy, node);
> > +
> > +             while (bytes_copied < sgc->length) {
> > +                     size_t copy_size;
> > +                     size_t bytes_left = sgc->length - bytes_copied;
> > +                     size_t offset = sgc->offset + bytes_copied;
> > +
> > +                     /*
> > +                      * We copy up to the fixup (pointed to by pf)
> > +                      */
> > +                     copy_size = pf ? min(bytes_left, (size_t)pf->offset - offset)
> > +                                    : bytes_left;
> > +                     if (!ret && copy_size)
> > +                             ret = binder_alloc_copy_user_to_buffer(
>                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> "ret" is the number of bytes remaining to be copied.

Good catch. Thanks. Will fix.

>
>
> > +                                             alloc, buffer,
> > +                                             offset,
> > +                                             sgc->uaddr + bytes_copied,
> > +                                             copy_size);
> > +                     bytes_copied += copy_size;
> > +                     if (copy_size != bytes_left) {
> > +                             BUG_ON(!pf);
> > +                             /* we stopped at a fixup offset */
> > +                             if (pf->skip_size) {
> > +                                     /*
> > +                                      * we are just skipping. This is for
> > +                                      * BINDER_TYPE_FDA where the translated
> > +                                      * fds will be fixed up when we get
> > +                                      * to target context.
> > +                                      */
> > +                                     bytes_copied += pf->skip_size;
> > +                             } else {
> > +                                     /* apply the fixup indicated by pf */
> > +                                     if (!ret)
> > +                                             ret = binder_alloc_copy_to_buffer(
> > +                                                     alloc, buffer,
> > +                                                     pf->offset,
> > +                                                     &pf->fixup_data,
> > +                                                     sizeof(pf->fixup_data));
> > +                                     bytes_copied += sizeof(pf->fixup_data);
> > +                             }
> > +                             list_del(&pf->node);
> > +                             kfree(pf);
> > +                             pf = list_first_entry_or_null(pf_head,
> > +                                             struct binder_ptr_fixup, node);
> > +                     }
> > +             }
> > +             list_del(&sgc->node);
> > +             kfree(sgc);
> > +     }
> > +     BUG_ON(!list_empty(pf_head));
> > +     BUG_ON(!list_empty(sgc_head));
> > +
> > +     return ret;
> > +}
> > +
> > +/**
> > + * binder_cleanup_deferred_txn_lists() - free specified lists
> > + * @sgc_head:        list_head of scatter-gather copy list
> > + * @pf_head: list_head of pointer fixup list
> > + *
> > + * Called to clean up @sgc_head and @pf_head if there is an
> > + * error.
> > + */
> > +static void binder_cleanup_deferred_txn_lists(struct list_head *sgc_head,
> > +                                           struct list_head *pf_head)
> > +{
> > +     struct list_head *entry, *tmp;
> > +
> > +     list_for_each_safe(entry, tmp, sgc_head) {
> > +             struct binder_sg_copy *sgc =
> > +                     container_of(entry, struct binder_sg_copy, node);
> > +             list_del(&sgc->node);
> > +             kfree(sgc);
> > +     }
> > +     list_for_each_safe(entry, tmp, pf_head) {
> > +             struct binder_ptr_fixup *pf =
> > +                     container_of(entry, struct binder_ptr_fixup, node);
> > +             list_del(&pf->node);
> > +             kfree(pf);
> > +     }
> > +}
> > +
> > +/**
> > + * binder_defer_copy() - queue a scatter-gather buffer for copy
> > + * @sgc_head:        list_head of scatter-gather copy list
> > + * @offset:  binder buffer offset in target process
> > + * @uaddr:   user address in source process
> > + * @length:  bytes to copy
> > + *
> > + * Specify a scatter-gather block to be copied. The actual copy must
> > + * be deferred until all the needed fixups are identified and queued.
> > + * Then the copy and fixups are done together so un-translated values
> > + * from the source are never visible in the target buffer.
> > + *
> > + * We are guaranteed that repeated calls to this function will have
> > + * monotonically increasing @offset values so the list will naturally
> > + * be ordered.
> > + *
> > + * Return: 0=success, else -errno
> > + */
> > +static int binder_defer_copy(struct list_head *sgc_head, binder_size_t offset,
> > +                          const void __user *uaddr, size_t length)
> > +{
> > +     struct binder_sg_copy *bc = kzalloc(sizeof(*bc), GFP_KERNEL);
> > +
> > +     if (!bc)
> > +             return -ENOMEM;
> > +
> > +     bc->offset = offset;
> > +     bc->uaddr = uaddr;
> > +     bc->length = length;
> > +     INIT_LIST_HEAD(&bc->node);
> > +
> > +     /*
> > +      * We are guaranteed that the deferred copies are in-order
> > +      * so just add to the tail.
> > +      */
> > +     list_add_tail(&bc->node, sgc_head);
> > +
> > +     return 0;
> > +}
> > +
> > +/**
> > + * binder_add_fixup() - queue a fixup to be applied to sg copy
> > + * @pf_head: list_head of binder ptr fixup list
> > + * @offset:  binder buffer offset in target process
> > + * @fixup:   bytes to be copied for fixup
> > + * @skip_size:       bytes to skip when copying (fixup will be applied later)
> > + *
> > + * Add the specified fixup to a list ordered by @offset. When copying
> > + * the scatter-gather buffers, the fixup will be copied instead of
> > + * data from the source buffer. For BINDER_TYPE_FDA fixups, the fixup
> > + * will be applied later (in target process context), so we just skip
> > + * the bytes specified by @skip_size. If @skip_size is 0, we copy the
> > + * value in @fixup.
> > + *
> > + * This function is called *mostly* in @offset order, but there are
> > + * exceptions. Since out-of-order inserts are relatively uncommon,
> > + * we insert the new element by searching backward from the tail of
> > + * the list.
> > + *
> > + * Return: 0=success, else -errno
> > + */
> > +static int binder_add_fixup(struct list_head *pf_head, binder_size_t offset,
> > +                         binder_uintptr_t fixup, size_t skip_size)
> > +{
> > +     struct binder_ptr_fixup *pf = kzalloc(sizeof(*pf), GFP_KERNEL);
> > +     struct list_head *tmp;
> > +
> > +     if (!pf)
> > +             return -ENOMEM;
> > +
> > +     pf->offset = offset;
> > +     pf->fixup_data = fixup;
> > +     pf->skip_size = skip_size;
> > +     INIT_LIST_HEAD(&pf->node);
> > +
> > +     /* Fixups are *mostly* added in-order, but there are some
> > +      * exceptions. Look backwards through list for insertion point.
> > +      */
> > +     if (!list_empty(pf_head)) {
>
> This condition is not required.  If list is empty we add it to the tail,
> but when there is only one item on the list, the first and last item are
> going to be the same.

Good point.

>
> > +             list_for_each_prev(tmp, pf_head) {
> > +                     struct binder_ptr_fixup *tmppf =
> > +                             list_entry(tmp, struct binder_ptr_fixup, node);
> > +
> > +                     if (tmppf->offset < pf->offset) {
> > +                             list_add(&pf->node, tmp);
> > +                             return 0;
> > +                     }
> > +             }
> > +             /*
> > +              * if we get here, then the new offset is the lowest so
> > +              * insert at the head
> > +              */
> > +             list_add(&pf->node, pf_head);
> > +             return 0;
> > +     }
> > +     list_add_tail(&pf->node, pf_head);
> > +     return 0;
> > +}
> > +
>
> regards,
> dan carpenter

Dan, Thanks for the detailed review on all 3 patches.

>
> --
> To unsubscribe from this group and stop receiving emails from it, send an email to kernel-team+unsubscribe@xxxxxxxxxxx.
>
_______________________________________________
devel mailing list
devel@xxxxxxxxxxxxxxxxxxxxxx
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel



[Index of Archives]     [Linux Driver Backports]     [DMA Engine]     [Linux GPIO]     [Linux SPI]     [Video for Linux]     [Linux USB Devel]     [Linux Coverity]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux