On 30/11/15 13:31, Savolainen, Petri (Nokia - FI/Espoo) wrote:

diff --git a/include/odp/api/pool.h b/include/odp/api/pool.h
index 01f770f..7b258c3 100644
--- a/include/odp/api/pool.h
+++ b/include/odp/api/pool.h
@@ -42,21 +42,25 @@ extern "C" {
   #define ODP_POOL_NAME_LEN  32

   /**
- * Packet user area initializer callback function for pools.
+ * Packet user area initialize callback function
    *
- * @param pkt                   Handle of the packet
- * @param uarea_init_arg        Opaque pointer defined in
odp_pool_param_t
+ * Packet pool uses this callback function to initialize user area in
each
+ * packet. The function is called once (per packet) in pool creation
phase for
+ * both persistent (user_area.persistent is set) and non-persistent
user areas.
+ * Additionally, it is called to re-initialize non-persistent user
area, when
+ * ODP has overwritten the area content.
    *
- * @note If the application specifies this pointer, it expects that
every buffer
- * is initialized exactly once with it when the underlying memory is
allocated.
- * It is not called from odp_packet_alloc(), unless the platform
chooses to
- * allocate the memory at that point. Applications can only assume that
this
- * callback is called once before the packet is first used. Any
subsequent
- * change to the user area might be preserved after odp_packet_free()
is called,
- * so applications should take care of (re)initialization if they
change data
- * preset by this function.
+ * @param pool       Packet pool handle
+ * @param init_arg   User defined 'user_area.init_arg' pointer in
+ *                   odp_pool_param_t
+ * @param user_area  Pointer to packet user area to initialize
+ * @param size       User area size in bytes
+ * @param pkt_index  Index of the packet. The range is from 0 to number
of
+ *                   packet in the pool (packet pool param 'num') minus
one.

Why would an application need this? It's not visible to the app. Also,

It's helpful for e.g. allocating per packet context memory at this point. This 
is just unique, opaque index from 0 to num - 1.

my_packet_ctx_t my_packet_ctx[MAX_PACKETS];

user_area_init(odp_pool_t pool, void *init_arg, void *user_area, uint32_t size, 
uint32_t pkt_index) {

        my_packet_user_area = user_area;
        my_packet_user_area->pkt_ctx = &my_packet_ctx[pkt_index];

}

And why couldn't you use the packet handle for that? This just seems to me an another unique id for the packet. This example seems to me like using the user area to store a pointer for an application allocated piece of memory related for that particular buffer. That would be a duplication of the functionality of odp_packet_user_ptr()



ODP-DPDK for example allocates some extra number of rte_mbuf's because
the per-thread caching can sit on packets, which in extreme cases makes
impossible to allocate 'num' packets. See this for details:

https://git.linaro.org/lng/odp-
dpdk.git/commitdiff/e1ac6c797539a62ee6b93554a1f0a7f5ba433a36

So, what would be pkt_index for these extra buffers? (NB. 'num' is only
the number we MUST provide, nothing says we can't have more than that)


'num' is: "The number of packets that the pool must provide that are packet length 'len' bytes 
or smaller" and "The number of packets may be less than 'num' when packets are larger 
than 'len'"

So, it's actually the maximum number of packets that can be allocated from the 
pool.

There could be less than 'num' packets if packets are bigger, yes, but the description doesn't restrict us that we can't have more. If you get the bill in the restaurant, you "must provide" that amount of money, but nothing prevents you to pay more.

Any per thread caching causes variation to actual number in-flight, but any 
time there should not be more than 'num' packets.

If you have a per-thread object cache for packets (sounds sensible to avoid locking during allocation), after a short time you'll have an arbitrary number of packets in those caches, waiting to be used. If the app requests 'num' while all packets are free, you can't always fulfill that request because some of the packets will be in other thread's cache. There is an unit test to check that, that's when I introduced this overcommit in odp-dpdk. Of course that opens up the possibility that you can have more than 'num' packets allocated at one point, but I don't see it as a problem. And as I said above, it isn't really forbidden in the spec. If you say there is a good reason to make that restriction, we should disable such object caches. (and change the API spec to be more explicit on that restriction)


However, for OVS we need the odp_packet_t of the packet, so we can call
ODP functions on the packet. And if you pass it, everything except
init_arg becomes unnecessary, because you can just take them with the
ODP functions.

The packet handle and all packet metadata are (potentially) invalid at this 
point.

My idea of this user area is that it's binded to the packet handle. This is a key requirement from OVS

Packet handle and metadata may be created and filled with valid metadata only 
after packet has created (by packet input HW or pool alloc in SW).

The handle could be even generation counted against double frees, etc SW/HW 
bugs.
We should also add then that the init function should be called when the handle is created. As mentioned above, from OVS point of view it is important that this user area is bound to a handle.

The same area could be linked with different packet handles, descriptors, etc 
during its life time.






    */
-typedef void (odp_packet_uarea_init_t)(odp_packet_t pkt, void
*uarea_init_arg);
+typedef void (odp_packet_user_area_init_t)(odp_pool_t pool, void
*init_arg,
+                                          void *user_area, uint32_t size,
+                                          uint32_t pkt_index);

   /**
    * Pool parameters
@@ -97,17 +101,35 @@ typedef struct odp_pool_param_t {
                            Use 0 for default. */
                        uint32_t seg_len;

-                       /** User area size in bytes. Specify as 0 if no user
-                           area is needed. */
-                       uint32_t uarea_size;
+                       struct {
+                               /** User area size in bytes. Specify as 0 if no
+                                   user area is needed. */
+                               uint32_t size;

-                       /** Initialize every packet's user area at allocation
-                           time. Use NULL if no initialization needed. */
-                       odp_packet_uarea_init_t *uarea_init;
+                               /** Callback function to initialize packet user
+                                   area. Use NULL if no initialization
+                                   needed. */
+                               odp_packet_user_area_init_t *init;

-                       /** Opaque pointer passed to packet user area
-                           constructor. */
-                       void *uarea_init_arg;
+                               /** Opaque pointer passed to packet user area
+                                   initialization callback function. */
+                               void *init_arg;
+
+                               /** 0: User area content may be overwritten when
+                                      packet is stored in the pool. If init
+                                      callback function is defined, it is
+                                      called to (re)initialize user area when
+                                      ever ODP has overwritten it. Init is not
+                                      necessarily called on every alloc, only
+                                      when needed (in pool creation and after
+                                      each overwrite).
+                                   1: User area content must remain constant
+                                      when packet is stored in the pool. If
+                                      init callback function is defined, it is
+                                      called once in pool creation.

If the platform allocates fresh memory for every packet and release it
at odp_packet_free() (absolutely possible, we don't prevent it in any
way), how does it keep the content of the user area without memory?

Persistent user area may simplify application packet context management. If 
user needs that, implementation must allocate user area from a memory location 
that is not overwritten by the implementation.


Each packet needs to be updated with a pointer to an user area before returning 
it to the user.
That happens already


Commonly this area would just extend a packet descriptor or packet buffer.

What is fresh memory in context of packet pools? Same handle (in different 
times) may point to different packet buffer address?

Yes.

Handles are unique?
Yes, for me it is obvious that handles are uniquely identify an actual packet in the ODP application. Even if they are randomly generated when the platform takes the memory from the pool. Otherwise the platform can't possibly determine e.g which packet's length is asked by the app.



And more importantly, what would be the benefit of such behavior? When
you allocate a packet from the pool, you get a random one, you won't
know if it was ever used before or not, if the user area was modified
since the first use or not etc.

Init callback initializes it. Application sees either initialized or already 
used contexts. Application controls the state of the packet context (user area).



"0: User area content may be overwritten when packet is stored in the pool. If init callback function is defined, it is called to (re)initialize user area whenever ODP has changed the underlying memory, or when the association between the packet handle and the user area changes. Init is not necessarily called on every alloc, only when needed (in pool creation, after each change by ODP in the underlying memory or the associated handle)."

Instead of "overwritten" I think "changed the underlying memory" covers better what we want to say. Also I've added the notion that changing the association between the handle and the user area needs to provoke this init function to be called.

"1: User area content must not be modified by the platform when packet is stored in the pool. If init callback function is defined, it is called once in pool creation."

As discussed on the call, this is the scenario when the app "caches" resources used by packets (e.g. a timer) in the user area, so once they are stored there at first use, the next time the user area is used with a packet the application can reuse them. The implementation has to make sure the memory stays there (even if normally it would allocate the memory on the fly). I can see two problems with this: - and what if the platform generates new handles for every new packet? You may call the init function again (losing performance), but how would the init function knows this is not pool creation time? Your pkt_index idea would solve that, but I've mentioned above the other reasons why it won't work. - a more severe problem comes when you want to release those resources stored in the user area. Allocating each packet and going through the user area might be a _very_ dirty way to do that, but it still doesn't work with the DPDK object cache scenario I've mentioned above.





What I need for OVS: to set up two fields in the user area once to store
data which will never change. OVS stores its packet structure there, one
field is to tell OVS this is an ODP packet, the other to store the
packet handle for a particular buffer. So I don't have to store these
data every time the packet is received from a pktio (== when it is
allocated). The real benefit comes when the platform allocate the user
area at pool creation time, so it has to do it only once, like probably
most platforms.


User area can hold constant parts of the OVS struct either it persistent or 
non-persistent (fixed init func) user area.

Then on each packet, update packet handle and any other dynamic data into the 
struct or pass those as arguments.

The packet handle IS half of the data we want to avoid initializing every time we get the packet. The other is the source field, which is the same value for every packet.

-Petri



_______________________________________________
lng-odp mailing list
lng-odp@lists.linaro.org
https://lists.linaro.org/mailman/listinfo/lng-odp

Reply via email to