diff mbox

ring.h: introduce macros to handle monodirectional rings with multiple req sizes

Message ID 1487371099-21988-1-git-send-email-sstabellini@kernel.org (mailing list archive)
State New, archived
Headers show

Commit Message

Stefano Stabellini Feb. 17, 2017, 10:38 p.m. UTC
This patch introduces macros, structs and functions to handle rings in
the format described by docs/misc/pvcalls.markdown and
docs/misc/9pfs.markdown. The index page (struct __name##_data_intf)
contains the indexes and the grant refs to setup two rings.

               Indexes page
               +----------------------+
               |@0 $NAME_data_intf:   |
               |@76: ring_order = 1   |
               |@80: ref[0]+          |
               |@84: ref[1]+          |
               |           |          |
               |           |          |
               +----------------------+
                           |
                           v (data ring)
                   +-------+-----------+
                   |  @0->4098: in     |
                   |  ref[0]           |
                   |-------------------|
                   |  @4099->8196: out |
                   |  ref[1]           |
                   +-------------------+

$NAME_read_packet and $NAME_write_packet are provided to read or write
any data struct from/to the ring. In pvcalls, they are unused. In xen
9pfs, they are used to read or write the 9pfs header. In other protocols
they could be used to read/write the whole request structure. See
docs/misc/9pfs.markdown:Ring Usage to learn how to check how much data
is on the ring, and how to handle notifications.

There is a ring_size parameter to most functions so that protocols using
these macros don't have to have a statically defined ring order at build
time. In pvcalls for example, each new ring could have a different
order.

These macros don't help you share the indexes page or the event channels
needed for notifications. You can do that with other out of band
mechanisms, such as xenstore or another ring.

Signed-off-by: Stefano Stabellini <stefano@aporeto.com>
CC: konrad.wilk@oracle.com
CC: andr2000@gmail.com
CC: oleksandr_andrushchenko@epam.com
CC: andrii.anisov@gmail.com
CC: vlad.babchuk@gmail.com
CC: al1img@gmail.com
CC: joculator@gmail.com

---
Give a look at the following branch to see how they are used with
pvcalls and xen-9pfs (the drivers are still work in progress):

git://git.kernel.org/pub/scm/linux/kernel/git/sstabellini/xen.git 9pfs-async-v4
---
---
 xen/include/public/io/ring.h | 122 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 122 insertions(+)

Comments

Stefano Stabellini Feb. 17, 2017, 11:24 p.m. UTC | #1
On Fri, 17 Feb 2017, Stefano Stabellini wrote:
> This patch introduces macros, structs and functions to handle rings in
> the format described by docs/misc/pvcalls.markdown and
> docs/misc/9pfs.markdown. The index page (struct __name##_data_intf)
> contains the indexes and the grant refs to setup two rings.
> 
>                Indexes page
>                +----------------------+
>                |@0 $NAME_data_intf:   |
>                |@76: ring_order = 1   |
>                |@80: ref[0]+          |
>                |@84: ref[1]+          |
>                |           |          |
>                |           |          |
>                +----------------------+
>                            |
>                            v (data ring)
>                    +-------+-----------+
>                    |  @0->4098: in     |
>                    |  ref[0]           |
>                    |-------------------|
>                    |  @4099->8196: out |
>                    |  ref[1]           |
>                    +-------------------+
> 
> $NAME_read_packet and $NAME_write_packet are provided to read or write
> any data struct from/to the ring. In pvcalls, they are unused. In xen
> 9pfs, they are used to read or write the 9pfs header. In other protocols
> they could be used to read/write the whole request structure. See
> docs/misc/9pfs.markdown:Ring Usage to learn how to check how much data
> is on the ring, and how to handle notifications.
> 
> There is a ring_size parameter to most functions so that protocols using
> these macros don't have to have a statically defined ring order at build
> time. In pvcalls for example, each new ring could have a different
> order.
> 
> These macros don't help you share the indexes page or the event channels
> needed for notifications. You can do that with other out of band
> mechanisms, such as xenstore or another ring.
> 
> Signed-off-by: Stefano Stabellini <stefano@aporeto.com>
> CC: konrad.wilk@oracle.com
> CC: andr2000@gmail.com
> CC: oleksandr_andrushchenko@epam.com
> CC: andrii.anisov@gmail.com
> CC: vlad.babchuk@gmail.com
> CC: al1img@gmail.com
> CC: joculator@gmail.com
> 
> ---
> Give a look at the following branch to see how they are used with
> pvcalls and xen-9pfs (the drivers are still work in progress):
> 
> git://git.kernel.org/pub/scm/linux/kernel/git/sstabellini/xen.git 9pfs-async-v4
> ---
> ---
>  xen/include/public/io/ring.h | 122 +++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 122 insertions(+)
> 
> diff --git a/xen/include/public/io/ring.h b/xen/include/public/io/ring.h
> index 801c0da..ca9a8f0 100644
> --- a/xen/include/public/io/ring.h
> +++ b/xen/include/public/io/ring.h
> @@ -313,6 +313,128 @@ typedef struct __name##_back_ring __name##_back_ring_t
>      (_work_to_do) = RING_HAS_UNCONSUMED_RESPONSES(_r);                  \
>  } while (0)
>  
> +
> +/*
> + * DEFINE_XEN_FLEX_RING defines two monodirectional rings and functions
> + * to check if there is data on the ring, and to read and write to them.
> + *
> + * XEN_FLEX_RING_SIZE
> + *   Convenience macro to calculate the size of one of the two rings
> + *   from the overall order.
> + *
> + * $NAME_mask
> + *   Function to apply the size mask to an index, to reduce the index
> + *   within the range [0-size].
> + *
> + * $NAME_read_packet
> + *   Function to read a defined amount of data from the ring. The amount
> + *   of data read is sizeof(__packet_t).
> + *
> + * $NAME_write_packet
> + *   Function to write a defined amount of data to the ring. The amount
> + *   of data to write is sizeof(__packet_t).
> + *
> + * $NAME_data_intf
> + *   Indexes page, shared between frontend and backend. It also
> + *   contains the array of grant refs. Different protocols can have
> + *   extensions to the basic format, in such cases please define your
> + *   own data_intf struct.
> + *
> + * $NAME_queued
> + *   Function to calculate how many bytes are currently on the ring,
> + *   read to be read. It can also be used to calculate how much free
> + *   space is currently on the ring (ring_size - $NAME_queued()).
> + */
> +#define XEN_FLEX_RING_SIZE(__order)                                           \
> +    ((1 << ((__order) + XEN_PAGE_SHIFT)) / 2)

This should be PAGE_SHIFT. It's XEN_PAGE_SHIFT when used as a linux
header.


> +#define DEFINE_XEN_FLEX_RING(__name, __packet_t)                              \
> +                                                                              \
> +static inline RING_IDX __name##_mask(RING_IDX idx, RING_IDX ring_size)        \
> +{                                                                             \
> +    return ((idx) & (ring_size - 1));                                         \
> +}                                                                             \
> +                                                                              \
> +static inline RING_IDX __name##_mask_order(RING_IDX idx, RING_IDX ring_order) \
> +{                                                                             \
> +    return ((idx) & (XEN_FLEX_RING_SIZE(ring_order) - 1));                    \
> +}                                                                             \
> +                                                                              \
> +static inline void __name##_read_packet(char *buf,                            \
> +        RING_IDX *masked_prod, RING_IDX *masked_cons,                         \
> +        RING_IDX ring_size, __packet_t *h) {                                  \
> +    if (*masked_cons < *masked_prod) {                                        \
> +        memcpy(h, buf + *masked_cons, sizeof(*h));                            \
> +    } else {                                                                  \
> +        if (sizeof(*h) > ring_size - *masked_cons) {                          \
> +            memcpy(h, buf + *masked_cons, ring_size - *masked_cons);          \
> +            memcpy((char *)h + ring_size - *masked_cons, buf,                 \
> +                    sizeof(*h) - (ring_size - *masked_cons));                 \
> +        } else {                                                              \
> +            memcpy(h, buf + *masked_cons, sizeof(*h));                        \
> +        }                                                                     \
> +    }                                                                         \
> +    *masked_cons = __name##_mask(*masked_cons + sizeof(*h), ring_size);       \
> +}                                                                             \
> +                                                                              \
> +static inline void __name##_write_packet(char *buf,                           \
> +        RING_IDX *masked_prod, RING_IDX *masked_cons,                         \
> +        RING_IDX ring_size, __packet_t h) {                                   \
> +    if (*masked_prod < *masked_cons) {                                        \
> +        memcpy(buf + *masked_prod, &h, sizeof(h));                            \
> +    } else {                                                                  \
> +        if (sizeof(h) > ring_size - *masked_prod) {                           \
> +            memcpy(buf + *masked_prod, &h, ring_size - *masked_prod);         \
> +            memcpy(buf, (char *)(&h) + (ring_size - *masked_prod),            \
> +                    sizeof(h) - (ring_size - *masked_prod));                  \
> +        } else {                                                              \
> +            memcpy(buf + *masked_prod, &h, sizeof(h));                        \
> +        }                                                                     \
> +    }                                                                         \
> +    *masked_prod = __name##_mask(*masked_prod + sizeof(h), ring_size);        \
> +}                                                                             \
> +                                                                              \
> +struct __name##_data {                                                        \
> +    char *in; /* half of the allocation */                                    \
> +    char *out; /* half of the allocation */                                   \
> +};                                                                            \
> +                                                                              \
> +struct __name##_data_intf {                                                   \
> +    RING_IDX in_cons, in_prod;                                                \
> +                                                                              \
> +    uint8_t pad1[56];                                                         \
> +                                                                              \
> +    RING_IDX out_cons, out_prod;                                              \
> +                                                                              \
> +    uint8_t pad2[56];                                                         \
> +                                                                              \
> +    RING_IDX ring_order;                                                      \
> +    grant_ref_t ref[];                                                        \
> +};                                                                            \
> +                                                                              \
> +static inline RING_IDX __name##_queued(RING_IDX prod,                         \
> +        RING_IDX cons, RING_IDX ring_size)                                    \
> +{                                                                             \
> +    RING_IDX size;                                                            \
> +                                                                              \
> +    if (prod == cons)                                                         \
> +        return 0;                                                             \
> +                                                                              \
> +    prod = __name##_mask(prod, ring_size);                                    \
> +    cons = __name##_mask(cons, ring_size);                                    \
> +                                                                              \
> +    if (prod == cons)                                                         \
> +        return ring_size;                                                     \
> +                                                                              \
> +    if (prod > cons)                                                          \
> +        size = prod - cons;                                                   \
> +    else {                                                                    \
> +        size = ring_size - cons;                                              \
> +        size += prod;                                                         \
> +    }                                                                         \
> +    return size;                                                              \
> +};
> +
>  #endif /* __XEN_PUBLIC_IO_RING_H__ */
>  
>  /*
> -- 
> 1.9.1
>
Jan Beulich Feb. 20, 2017, 9:59 a.m. UTC | #2
>>> On 17.02.17 at 23:38, <sstabellini@kernel.org> wrote:

For all of the comments below, please understand that I'm giving
them in the understanding that pre-existing code may be similarly
problematic; we shouldn't introduce new issues though.

> --- a/xen/include/public/io/ring.h
> +++ b/xen/include/public/io/ring.h
> @@ -313,6 +313,128 @@ typedef struct __name##_back_ring __name##_back_ring_t
>      (_work_to_do) = RING_HAS_UNCONSUMED_RESPONSES(_r);                  \
>  } while (0)
>  
> +
> +/*
> + * DEFINE_XEN_FLEX_RING defines two monodirectional rings and functions
> + * to check if there is data on the ring, and to read and write to them.
> + *
> + * XEN_FLEX_RING_SIZE
> + *   Convenience macro to calculate the size of one of the two rings
> + *   from the overall order.
> + *
> + * $NAME_mask
> + *   Function to apply the size mask to an index, to reduce the index
> + *   within the range [0-size].
> + *
> + * $NAME_read_packet
> + *   Function to read a defined amount of data from the ring. The amount
> + *   of data read is sizeof(__packet_t).
> + *
> + * $NAME_write_packet
> + *   Function to write a defined amount of data to the ring. The amount
> + *   of data to write is sizeof(__packet_t).
> + *
> + * $NAME_data_intf
> + *   Indexes page, shared between frontend and backend. It also
> + *   contains the array of grant refs. Different protocols can have
> + *   extensions to the basic format, in such cases please define your
> + *   own data_intf struct.
> + *
> + * $NAME_queued
> + *   Function to calculate how many bytes are currently on the ring,
> + *   read to be read. It can also be used to calculate how much free

ready to be ...

> + *   space is currently on the ring (ring_size - $NAME_queued()).
> + */
> +#define XEN_FLEX_RING_SIZE(__order)                                           \
> +    ((1 << ((__order) + XEN_PAGE_SHIFT)) / 2)

Double-underscore prefixed names are reserved. Please avoid using
leading underscores namely in public headers.

Also, while likely not relevant for the near future, may I suggest to
use 1UL here, or even (size_t)1 (to also cope with P64 environments)?
Further, with PAGE_SHIFT never zero, I think the expression would
better be

    (1UL << ((__order) + XEN_PAGE_SHIFT - 1))

> +#define DEFINE_XEN_FLEX_RING(__name, __packet_t)                              \
> +                                                                              \
> +static inline RING_IDX __name##_mask(RING_IDX idx, RING_IDX ring_size)        \
> +{                                                                             \
> +    return ((idx) & (ring_size - 1));                                         \

In an inline function you don't need to parenthesize parameter
name uses. But the use of inline functions here is questionable
in the first place - so far there are none, as they're not standard
C89. Granted they are being used in a macro only, so won't cause
immediate issues for code not using the macros, but anyway.

> +static inline void __name##_read_packet(char *buf,                            \

const

> +        RING_IDX *masked_prod, RING_IDX *masked_cons,                         \

You don't update *masked_prod - why do you need a pointer here?

> +        RING_IDX ring_size, __packet_t *h) {                                  \
> +    if (*masked_cons < *masked_prod) {                                        \
> +        memcpy(h, buf + *masked_cons, sizeof(*h));                            \
> +    } else {                                                                  \
> +        if (sizeof(*h) > ring_size - *masked_cons) {                          \
> +            memcpy(h, buf + *masked_cons, ring_size - *masked_cons);          \
> +            memcpy((char *)h + ring_size - *masked_cons, buf,                 \
> +                    sizeof(*h) - (ring_size - *masked_cons));                 \
> +        } else {                                                              \
> +            memcpy(h, buf + *masked_cons, sizeof(*h));                        \

This is the same as the first memcpy(). Care to adjust (merge) the
conditionals so you need this only once?

> +static inline void __name##_write_packet(char *buf,                           \
> +        RING_IDX *masked_prod, RING_IDX *masked_cons,                         \
> +        RING_IDX ring_size, __packet_t h) {                                   \

Passing a (possibly large) structure by value? Apart from that similar
comments as for read apply here.

> +struct __name##_data {                                                        \
> +    char *in; /* half of the allocation */                                    \
> +    char *out; /* half of the allocation */                                   \

Why plain char? Void would seem the most neutral option here, but
if arithmetic needs doing inside the header, then unsigned char may
need to be used.

> +struct __name##_data_intf {                                                   \
> +    RING_IDX in_cons, in_prod;                                                \
> +                                                                              \
> +    uint8_t pad1[56];                                                         \
> +                                                                              \
> +    RING_IDX out_cons, out_prod;                                              \
> +                                                                              \
> +    uint8_t pad2[56];                                                         \
> +                                                                              \
> +    RING_IDX ring_order;                                                      \
> +    grant_ref_t ref[];                                                        \
> +};                                                                            \

Above you talk about the option of defining private _data_intf
structures. How is that supposed to work when you define a
suitably named structure already here? Other than #define-s
one can't undo such a type definition.

> +static inline RING_IDX __name##_queued(RING_IDX prod,                         \
> +        RING_IDX cons, RING_IDX ring_size)                                    \
> +{                                                                             \
> +    RING_IDX size;                                                            \
> +                                                                              \
> +    if (prod == cons)                                                         \
> +        return 0;                                                             \
> +                                                                              \
> +    prod = __name##_mask(prod, ring_size);                                    \
> +    cons = __name##_mask(cons, ring_size);                                    \
> +                                                                              \
> +    if (prod == cons)                                                         \
> +        return ring_size;                                                     \
> +                                                                              \
> +    if (prod > cons)                                                          \
> +        size = prod - cons;                                                   \
> +    else {                                                                    \
> +        size = ring_size - cons;                                              \
> +        size += prod;                                                         \
> +    }                                                                       

To me this would read easier (due to matching up better with the
if() path) as

    else
        size = ring_size - (cons - prod);

Jan
Stefano Stabellini Feb. 20, 2017, 11:26 p.m. UTC | #3
On Mon, 20 Feb 2017, Jan Beulich wrote:
> >>> On 17.02.17 at 23:38, <sstabellini@kernel.org> wrote:
> 
> For all of the comments below, please understand that I'm giving
> them in the understanding that pre-existing code may be similarly
> problematic; we shouldn't introduce new issues though.

I understand, thanks for the many useful comments


> > --- a/xen/include/public/io/ring.h
> > +++ b/xen/include/public/io/ring.h
> > @@ -313,6 +313,128 @@ typedef struct __name##_back_ring __name##_back_ring_t
> >      (_work_to_do) = RING_HAS_UNCONSUMED_RESPONSES(_r);                  \
> >  } while (0)
> >  
> > +
> > +/*
> > + * DEFINE_XEN_FLEX_RING defines two monodirectional rings and functions
> > + * to check if there is data on the ring, and to read and write to them.
> > + *
> > + * XEN_FLEX_RING_SIZE
> > + *   Convenience macro to calculate the size of one of the two rings
> > + *   from the overall order.
> > + *
> > + * $NAME_mask
> > + *   Function to apply the size mask to an index, to reduce the index
> > + *   within the range [0-size].
> > + *
> > + * $NAME_read_packet
> > + *   Function to read a defined amount of data from the ring. The amount
> > + *   of data read is sizeof(__packet_t).
> > + *
> > + * $NAME_write_packet
> > + *   Function to write a defined amount of data to the ring. The amount
> > + *   of data to write is sizeof(__packet_t).
> > + *
> > + * $NAME_data_intf
> > + *   Indexes page, shared between frontend and backend. It also
> > + *   contains the array of grant refs. Different protocols can have
> > + *   extensions to the basic format, in such cases please define your
> > + *   own data_intf struct.
> > + *
> > + * $NAME_queued
> > + *   Function to calculate how many bytes are currently on the ring,
> > + *   read to be read. It can also be used to calculate how much free
> 
> ready to be ...

OK


> > + *   space is currently on the ring (ring_size - $NAME_queued()).
> > + */
> > +#define XEN_FLEX_RING_SIZE(__order)                                           \
> > +    ((1 << ((__order) + XEN_PAGE_SHIFT)) / 2)
> 
> Double-underscore prefixed names are reserved. Please avoid using
> leading underscores namely in public headers.

OK


> Also, while likely not relevant for the near future, may I suggest to
> use 1UL here, or even (size_t)1 (to also cope with P64 environments)?
> Further, with PAGE_SHIFT never zero, I think the expression would
> better be
> 
>     (1UL << ((__order) + XEN_PAGE_SHIFT - 1))

OK


> > +#define DEFINE_XEN_FLEX_RING(__name, __packet_t)                              \
> > +                                                                              \
> > +static inline RING_IDX __name##_mask(RING_IDX idx, RING_IDX ring_size)        \
> > +{                                                                             \
> > +    return ((idx) & (ring_size - 1));                                         \
> 
> In an inline function you don't need to parenthesize parameter
> name uses.

OK


> But the use of inline functions here is questionable
> in the first place - so far there are none, as they're not standard
> C89. Granted they are being used in a macro only, so won't cause
> immediate issues for code not using the macros, but anyway.

I did it because it is not possible to use a macro to define another
macro with a variable name. In other words, the following does not work:

  #define DEFINE_XEN_FLEX_RING(name, packet_t)                              \
  #define name##_mask(idx, ring_size) (idx & (ring_size - 1))      

I prefer to drop C89 compliance to keep cleaner code, but if you have
better suggestions, I would be happy to hear them.


> > +static inline void __name##_read_packet(char *buf,                            \
> 
> const

We cannot use const char *buf, because we are about to pass buf to
memcpy.


> > +        RING_IDX *masked_prod, RING_IDX *masked_cons,                         \
> 
> You don't update *masked_prod - why do you need a pointer here?

When using this functions in pvcalls and xen-9pfs, I found that it was
less confusing for me if the two index parameters had the same type. In
fact, I started with what you suggested, then changed it back to this.


> > +        RING_IDX ring_size, __packet_t *h) {                                  \
> > +    if (*masked_cons < *masked_prod) {                                        \
> > +        memcpy(h, buf + *masked_cons, sizeof(*h));                            \
> > +    } else {                                                                  \
> > +        if (sizeof(*h) > ring_size - *masked_cons) {                          \
> > +            memcpy(h, buf + *masked_cons, ring_size - *masked_cons);          \
> > +            memcpy((char *)h + ring_size - *masked_cons, buf,                 \
> > +                    sizeof(*h) - (ring_size - *masked_cons));                 \
> > +        } else {                                                              \
> > +            memcpy(h, buf + *masked_cons, sizeof(*h));                        \
> 
> This is the same as the first memcpy(). Care to adjust (merge) the
> conditionals so you need this only once?

Sure


> > +static inline void __name##_write_packet(char *buf,                           \
> > +        RING_IDX *masked_prod, RING_IDX *masked_cons,                         \
> > +        RING_IDX ring_size, __packet_t h) {                                   \
> 
> Passing a (possibly large) structure by value? Apart from that similar
> comments as for read apply here.

It makes sense to use a pointer here, I'll do that.


> > +struct __name##_data {                                                        \
> > +    char *in; /* half of the allocation */                                    \
> > +    char *out; /* half of the allocation */                                   \
> 
> Why plain char? Void would seem the most neutral option here, but
> if arithmetic needs doing inside the header, then unsigned char may
> need to be used.

The types here should match the type of the "buf" parameter used in the
read/write_packet functions. We cannot use void as we do arithmetic. If
you think unsigned char is more appropriate, I'll also change the type
of the buf arguments elsewhere.


> > +struct __name##_data_intf {                                                   \
> > +    RING_IDX in_cons, in_prod;                                                \
> > +                                                                              \
> > +    uint8_t pad1[56];                                                         \
> > +                                                                              \
> > +    RING_IDX out_cons, out_prod;                                              \
> > +                                                                              \
> > +    uint8_t pad2[56];                                                         \
> > +                                                                              \
> > +    RING_IDX ring_order;                                                      \
> > +    grant_ref_t ref[];                                                        \
> > +};                                                                            \
> 
> Above you talk about the option of defining private _data_intf
> structures. How is that supposed to work when you define a
> suitably named structure already here? Other than #define-s
> one can't undo such a type definition.

Yes, but it is always possible to use a different name. For example,
this is what I did in the pvcalls header:

  struct __pvcalls_data_intf {
    RING_IDX in_cons, in_prod, in_error;

    uint8_t pad1[52];

    RING_IDX out_cons, out_prod, out_error;

    uint8_t pad2[52];

    RING_IDX ring_order;
    grant_ref_t ref[];
  };

This struct is only here as a reference, we don't have to actually
define it in ring.h. If you think it's best, I could introduce it only
as a comment.


> > +static inline RING_IDX __name##_queued(RING_IDX prod,                         \
> > +        RING_IDX cons, RING_IDX ring_size)                                    \
> > +{                                                                             \
> > +    RING_IDX size;                                                            \
> > +                                                                              \
> > +    if (prod == cons)                                                         \
> > +        return 0;                                                             \
> > +                                                                              \
> > +    prod = __name##_mask(prod, ring_size);                                    \
> > +    cons = __name##_mask(cons, ring_size);                                    \
> > +                                                                              \
> > +    if (prod == cons)                                                         \
> > +        return ring_size;                                                     \
> > +                                                                              \
> > +    if (prod > cons)                                                          \
> > +        size = prod - cons;                                                   \
> > +    else {                                                                    \
> > +        size = ring_size - cons;                                              \
> > +        size += prod;                                                         \
> > +    }                                                                       
> 
> To me this would read easier (due to matching up better with the
> if() path) as
> 
>     else
>         size = ring_size - (cons - prod);
 
OK, I'll make the change.
Jan Beulich Feb. 21, 2017, 8:17 a.m. UTC | #4
>>> On 21.02.17 at 00:26, <sstabellini@kernel.org> wrote:
> On Mon, 20 Feb 2017, Jan Beulich wrote:
>> >>> On 17.02.17 at 23:38, <sstabellini@kernel.org> wrote:
>> But the use of inline functions here is questionable
>> in the first place - so far there are none, as they're not standard
>> C89. Granted they are being used in a macro only, so won't cause
>> immediate issues for code not using the macros, but anyway.
> 
> I did it because it is not possible to use a macro to define another
> macro with a variable name. In other words, the following does not work:
> 
>   #define DEFINE_XEN_FLEX_RING(name, packet_t)                              \
>   #define name##_mask(idx, ring_size) (idx & (ring_size - 1))      
> 
> I prefer to drop C89 compliance to keep cleaner code, but if you have
> better suggestions, I would be happy to hear them.

Well, okay then, but please add a respective remark to the commit
message then.

>> > +static inline void __name##_read_packet(char *buf,                            \
>> 
>> const
> 
> We cannot use const char *buf, because we are about to pass buf to
> memcpy.

I don't understand: memcpy()'s second parameter is const.

>> > +        RING_IDX *masked_prod, RING_IDX *masked_cons,                         \
>> 
>> You don't update *masked_prod - why do you need a pointer here?
> 
> When using this functions in pvcalls and xen-9pfs, I found that it was
> less confusing for me if the two index parameters had the same type. In
> fact, I started with what you suggested, then changed it back to this.

I don't find this a convincing argument. The parameters will differ
in constness anyway due to the different transfer direction, and
using an extra layer of indirection just for aesthetic reasons seems
at least odd to me. 

>> > +struct __name##_data {                                                        \
>> > +    char *in; /* half of the allocation */                                    \
>> > +    char *out; /* half of the allocation */                                   \
>> 
>> Why plain char? Void would seem the most neutral option here, but
>> if arithmetic needs doing inside the header, then unsigned char may
>> need to be used.
> 
> The types here should match the type of the "buf" parameter used in the
> read/write_packet functions. We cannot use void as we do arithmetic. If
> you think unsigned char is more appropriate, I'll also change the type
> of the buf arguments elsewhere.

Yes please - plain char is mainly meant for use with strings, not
arbitrary binary data.

>> > +struct __name##_data_intf {                                                   \
>> > +    RING_IDX in_cons, in_prod;                                                \
>> > +                                                                              \
>> > +    uint8_t pad1[56];                                                         \
>> > +                                                                              \
>> > +    RING_IDX out_cons, out_prod;                                              \
>> > +                                                                              \
>> > +    uint8_t pad2[56];                                                         \
>> > +                                                                              \
>> > +    RING_IDX ring_order;                                                      \
>> > +    grant_ref_t ref[];                                                        \
>> > +};                                                                            \
>> 
>> Above you talk about the option of defining private _data_intf
>> structures. How is that supposed to work when you define a
>> suitably named structure already here? Other than #define-s
>> one can't undo such a type definition.
> 
> Yes, but it is always possible to use a different name. For example,
> this is what I did in the pvcalls header:
> 
>   struct __pvcalls_data_intf {
>     RING_IDX in_cons, in_prod, in_error;
> 
>     uint8_t pad1[52];
> 
>     RING_IDX out_cons, out_prod, out_error;
> 
>     uint8_t pad2[52];
> 
>     RING_IDX ring_order;
>     grant_ref_t ref[];
>   };
> 
> This struct is only here as a reference, we don't have to actually
> define it in ring.h. If you think it's best, I could introduce it only
> as a comment.

How about having two variants of the macro - one (the base)
version without this, and a second invoking the first and adding
this?

Jan
Stefano Stabellini Feb. 21, 2017, 11:24 p.m. UTC | #5
On Tue, 21 Feb 2017, Jan Beulich wrote:
> >>> On 21.02.17 at 00:26, <sstabellini@kernel.org> wrote:
> > On Mon, 20 Feb 2017, Jan Beulich wrote:
> >> >>> On 17.02.17 at 23:38, <sstabellini@kernel.org> wrote:
> >> But the use of inline functions here is questionable
> >> in the first place - so far there are none, as they're not standard
> >> C89. Granted they are being used in a macro only, so won't cause
> >> immediate issues for code not using the macros, but anyway.
> > 
> > I did it because it is not possible to use a macro to define another
> > macro with a variable name. In other words, the following does not work:
> > 
> >   #define DEFINE_XEN_FLEX_RING(name, packet_t)                              \
> >   #define name##_mask(idx, ring_size) (idx & (ring_size - 1))      
> > 
> > I prefer to drop C89 compliance to keep cleaner code, but if you have
> > better suggestions, I would be happy to hear them.
> 
> Well, okay then, but please add a respective remark to the commit
> message then.

OK


> >> > +static inline void __name##_read_packet(char *buf,                            \
> >> 
> >> const
> > 
> > We cannot use const char *buf, because we are about to pass buf to
> > memcpy.
> 
> I don't understand: memcpy()'s second parameter is const.

Ah, right


> >> > +        RING_IDX *masked_prod, RING_IDX *masked_cons,                         \
> >> 
> >> You don't update *masked_prod - why do you need a pointer here?
> > 
> > When using this functions in pvcalls and xen-9pfs, I found that it was
> > less confusing for me if the two index parameters had the same type. In
> > fact, I started with what you suggested, then changed it back to this.
> 
> I don't find this a convincing argument. The parameters will differ
> in constness anyway due to the different transfer direction, and
> using an extra layer of indirection just for aesthetic reasons seems
> at least odd to me. 

All right, I'll make the change


> >> > +struct __name##_data {                                                        \
> >> > +    char *in; /* half of the allocation */                                    \
> >> > +    char *out; /* half of the allocation */                                   \
> >> 
> >> Why plain char? Void would seem the most neutral option here, but
> >> if arithmetic needs doing inside the header, then unsigned char may
> >> need to be used.
> > 
> > The types here should match the type of the "buf" parameter used in the
> > read/write_packet functions. We cannot use void as we do arithmetic. If
> > you think unsigned char is more appropriate, I'll also change the type
> > of the buf arguments elsewhere.
> 
> Yes please - plain char is mainly meant for use with strings, not
> arbitrary binary data.

OK


> >> > +struct __name##_data_intf {                                                   \
> >> > +    RING_IDX in_cons, in_prod;                                                \
> >> > +                                                                              \
> >> > +    uint8_t pad1[56];                                                         \
> >> > +                                                                              \
> >> > +    RING_IDX out_cons, out_prod;                                              \
> >> > +                                                                              \
> >> > +    uint8_t pad2[56];                                                         \
> >> > +                                                                              \
> >> > +    RING_IDX ring_order;                                                      \
> >> > +    grant_ref_t ref[];                                                        \
> >> > +};                                                                            \
> >> 
> >> Above you talk about the option of defining private _data_intf
> >> structures. How is that supposed to work when you define a
> >> suitably named structure already here? Other than #define-s
> >> one can't undo such a type definition.
> > 
> > Yes, but it is always possible to use a different name. For example,
> > this is what I did in the pvcalls header:
> > 
> >   struct __pvcalls_data_intf {
> >     RING_IDX in_cons, in_prod, in_error;
> > 
> >     uint8_t pad1[52];
> > 
> >     RING_IDX out_cons, out_prod, out_error;
> > 
> >     uint8_t pad2[52];
> > 
> >     RING_IDX ring_order;
> >     grant_ref_t ref[];
> >   };
> > 
> > This struct is only here as a reference, we don't have to actually
> > define it in ring.h. If you think it's best, I could introduce it only
> > as a comment.
> 
> How about having two variants of the macro - one (the base)
> version without this, and a second invoking the first and adding
> this?
 
Sure
diff mbox

Patch

diff --git a/xen/include/public/io/ring.h b/xen/include/public/io/ring.h
index 801c0da..ca9a8f0 100644
--- a/xen/include/public/io/ring.h
+++ b/xen/include/public/io/ring.h
@@ -313,6 +313,128 @@  typedef struct __name##_back_ring __name##_back_ring_t
     (_work_to_do) = RING_HAS_UNCONSUMED_RESPONSES(_r);                  \
 } while (0)
 
+
+/*
+ * DEFINE_XEN_FLEX_RING defines two monodirectional rings and functions
+ * to check if there is data on the ring, and to read and write to them.
+ *
+ * XEN_FLEX_RING_SIZE
+ *   Convenience macro to calculate the size of one of the two rings
+ *   from the overall order.
+ *
+ * $NAME_mask
+ *   Function to apply the size mask to an index, to reduce the index
+ *   within the range [0-size].
+ *
+ * $NAME_read_packet
+ *   Function to read a defined amount of data from the ring. The amount
+ *   of data read is sizeof(__packet_t).
+ *
+ * $NAME_write_packet
+ *   Function to write a defined amount of data to the ring. The amount
+ *   of data to write is sizeof(__packet_t).
+ *
+ * $NAME_data_intf
+ *   Indexes page, shared between frontend and backend. It also
+ *   contains the array of grant refs. Different protocols can have
+ *   extensions to the basic format, in such cases please define your
+ *   own data_intf struct.
+ *
+ * $NAME_queued
+ *   Function to calculate how many bytes are currently on the ring,
+ *   read to be read. It can also be used to calculate how much free
+ *   space is currently on the ring (ring_size - $NAME_queued()).
+ */
+#define XEN_FLEX_RING_SIZE(__order)                                           \
+    ((1 << ((__order) + XEN_PAGE_SHIFT)) / 2)
+
+#define DEFINE_XEN_FLEX_RING(__name, __packet_t)                              \
+                                                                              \
+static inline RING_IDX __name##_mask(RING_IDX idx, RING_IDX ring_size)        \
+{                                                                             \
+    return ((idx) & (ring_size - 1));                                         \
+}                                                                             \
+                                                                              \
+static inline RING_IDX __name##_mask_order(RING_IDX idx, RING_IDX ring_order) \
+{                                                                             \
+    return ((idx) & (XEN_FLEX_RING_SIZE(ring_order) - 1));                    \
+}                                                                             \
+                                                                              \
+static inline void __name##_read_packet(char *buf,                            \
+        RING_IDX *masked_prod, RING_IDX *masked_cons,                         \
+        RING_IDX ring_size, __packet_t *h) {                                  \
+    if (*masked_cons < *masked_prod) {                                        \
+        memcpy(h, buf + *masked_cons, sizeof(*h));                            \
+    } else {                                                                  \
+        if (sizeof(*h) > ring_size - *masked_cons) {                          \
+            memcpy(h, buf + *masked_cons, ring_size - *masked_cons);          \
+            memcpy((char *)h + ring_size - *masked_cons, buf,                 \
+                    sizeof(*h) - (ring_size - *masked_cons));                 \
+        } else {                                                              \
+            memcpy(h, buf + *masked_cons, sizeof(*h));                        \
+        }                                                                     \
+    }                                                                         \
+    *masked_cons = __name##_mask(*masked_cons + sizeof(*h), ring_size);       \
+}                                                                             \
+                                                                              \
+static inline void __name##_write_packet(char *buf,                           \
+        RING_IDX *masked_prod, RING_IDX *masked_cons,                         \
+        RING_IDX ring_size, __packet_t h) {                                   \
+    if (*masked_prod < *masked_cons) {                                        \
+        memcpy(buf + *masked_prod, &h, sizeof(h));                            \
+    } else {                                                                  \
+        if (sizeof(h) > ring_size - *masked_prod) {                           \
+            memcpy(buf + *masked_prod, &h, ring_size - *masked_prod);         \
+            memcpy(buf, (char *)(&h) + (ring_size - *masked_prod),            \
+                    sizeof(h) - (ring_size - *masked_prod));                  \
+        } else {                                                              \
+            memcpy(buf + *masked_prod, &h, sizeof(h));                        \
+        }                                                                     \
+    }                                                                         \
+    *masked_prod = __name##_mask(*masked_prod + sizeof(h), ring_size);        \
+}                                                                             \
+                                                                              \
+struct __name##_data {                                                        \
+    char *in; /* half of the allocation */                                    \
+    char *out; /* half of the allocation */                                   \
+};                                                                            \
+                                                                              \
+struct __name##_data_intf {                                                   \
+    RING_IDX in_cons, in_prod;                                                \
+                                                                              \
+    uint8_t pad1[56];                                                         \
+                                                                              \
+    RING_IDX out_cons, out_prod;                                              \
+                                                                              \
+    uint8_t pad2[56];                                                         \
+                                                                              \
+    RING_IDX ring_order;                                                      \
+    grant_ref_t ref[];                                                        \
+};                                                                            \
+                                                                              \
+static inline RING_IDX __name##_queued(RING_IDX prod,                         \
+        RING_IDX cons, RING_IDX ring_size)                                    \
+{                                                                             \
+    RING_IDX size;                                                            \
+                                                                              \
+    if (prod == cons)                                                         \
+        return 0;                                                             \
+                                                                              \
+    prod = __name##_mask(prod, ring_size);                                    \
+    cons = __name##_mask(cons, ring_size);                                    \
+                                                                              \
+    if (prod == cons)                                                         \
+        return ring_size;                                                     \
+                                                                              \
+    if (prod > cons)                                                          \
+        size = prod - cons;                                                   \
+    else {                                                                    \
+        size = ring_size - cons;                                              \
+        size += prod;                                                         \
+    }                                                                         \
+    return size;                                                              \
+};
+
 #endif /* __XEN_PUBLIC_IO_RING_H__ */
 
 /*