> -----Original Message-----
> From: lng-odp-boun...@lists.linaro.org [mailto:lng-odp-
> boun...@lists.linaro.org] On Behalf Of ext Ola Liljedahl
> Sent: Wednesday, January 28, 2015 7:06 PM
> To: LNG ODP Mailman List
> Subject: [lng-odp] API questions - sizes and numelems as inputs and
> outputs
> 
> Talking about API's that take some type of element count or buffer
> size as input and return a value related to that. There seems to be a
> number of different models.
> 
> /**
>  * Generate random byte string
>  *
>  * @param buf          Pointer to store result
>  * @param len          Pointer to input length value as well as return
> value
> I assume the "return value" is the number of result bytes stored. But
> this is not very clear.
> 
>  * @param use_entropy  Use entropy
>  *
>  * @todo Define the implication of the use_entropy parameter
>  *
>  * @retval 0 on success
> Not the same "return value" as described above.
> 
>  * @retval <0 on failure
>  */
> int odp_hw_random_get(uint8_t *buf, size_t *len, bool use_entropy);
> Here we return the actual size written (I assume) through the len
> parameter which is consequently passed by reference.

How use full the function is if it does not fill the whole buffer with rand 
data? I think in this case should be success vs. failure. In success the whole 
buffer is written. 

> 
> /**
>  * Write CPU mask as a string of hexadecimal digits
>  *
>  * @param mask   CPU mask to write
>  * @param buf    Output buffer
>  * @param bufsz  Size of output buffer
>  *
>  * @return >0 on success. Number of characters written (including the
>  * terminating null char).
>  * @return 0 on failure (output buffer too small).
>  */
> size_t odp_cpumask_to_str(const odp_cpumask_t *mask, char *buf, size_t
> bufsz);
> The new definition of odp_cpumask_to_str() as recently agreed. Number
> of characters written is returned. It can't be 0 so 0 can be used as
> an error indicator. In other formatting-type of functions, 0 might be
> a valid return value (it primarily depends on whether the terminating
> null char should be included in the count).
> 
>   * @param[out] mac_addr  Storage for MAC address of the packet IO
> interface.
>   * @param      addr_size Storage size for the address
>   *
>  * @retval Number of bytes written on success, 0 on failure.
>   */
> size_t odp_pktio_mac_addr(odp_pktio_t id, void *mac_addr, size_t
> addr_size);
> This function returns 0 for failure (but this is incidental and
> prevents us to support pktio types that have zero-length MAC address
> (i.e. does not have any MAC address)).

What is a successful copy of a zero length MAC address? I think it's OK to 
return failure (==0), if user asks for a copy of MAC address from a interface 
that do not have one.

> 
> /**
>  * Copy data from packet
>  *
>  * Copy 'len' bytes of data from the packet level offset to the
> destination
>  * address.
>  *
>  * @param pkt    Packet handle
>  * @param offset Byte offset into the packet
>  * @param len    Number of bytes to copy
>  * @param dst    Destination address
>  *
>  * @retval 0 on success
>  * @retval <0 on failure
> Not documented what constitutes a failure.
> */
> int odp_packet_copydata_out(odp_packet_t pkt, uint32_t offset,
>                             uint32_t len, void *dst);
> What if less than len bytes could be copied (because reaching end of
> packet)? How is this reported? Should we use "uint32_t *len" and
> update it on return with the actual number of bytes copied? Or is this
> situation considered an error and reported with a negative return
> value? Seems excessive. Better to just specify len as the size of the
> output buffer and in some way be notified about the actual number of
> bytes copied.

It's better to keep this simple: success or failure. In success "len" bytes was 
copied. It's not important how many bytes were copied in failure. 

Failures:
offset > odp_packet_len()
offset + len > odp_packet_len()


> 
> /**
>  * Copy data into packet
>  *
>  * Copy    'len' bytes of data from the source address into the packet
> level
>  * offset. Maximum number of bytes to copy is packet data length minus the
>  * offset. Packet is not modified on an error.
>  *
>  * @param pkt    Packet handle
>  * @param offset Byte offset into the packet
>  * @param len    Number of bytes to copy
>  * @param src    Source address
>  *
>  * @retval 0 on success
>  * @retval <0 on failure
> Not documented what constitutes a failure.
>  */
> int odp_packet_copydata_in(odp_packet_t pkt, uint32_t offset,
>                            uint32_t len, const void *src);
> Similar for this function. Is it an error if 'len' bytes does not fit
> into the current packet size? Is this good semantics?

Failures:
offset > odp_packet_len()
offset + len > odp_packet_len()

> 
> /**
>  * Receive packets
>  *
>  * @param id          ODP packet IO handle
>  * @param pkt_table[] Storage for received packets (filled by function)
>  * @param len         Length of pkt_table[], i.e. max number of pkts to
> receive
>  *
>  * @return Number of packets received
>  * @retval <0 on failure
>  */
> int odp_pktio_recv(odp_pktio_t id, odp_packet_t pkt_table[], unsigned
> len);
> int odp_pktio_send(odp_pktio_t id, odp_packet_t pkt_table[], unsigned
> len);
> Here is a classic. Attempt to receive up to N items. Return actual
> number of items received or <0 on failure. 0 is a valid success code
> (not a failure). Why can't this be our model?
> The prototype uses unsigned and signed ints. How is different from
> using size_t and ssize_t?


This is why we are going through the APIs and check/harmonize return values ... 
the function definition is from Nov 13.

Int is good return type here. Should we use "int len" param to match the return 
value?

int i = 0;
int len = 10;

while (len) {
        int sent = odp_pktio_send(id, &pkt[i], len); 

        if (sent < 0)
                ABORT();

        i   += sent;
        len -= sent;
}


This would generate compiler warning if mixing unsigned and int:

unsigned len = 10;

int sent = odp_pktio_send(id, &pkt[i], len);

if (sent != len)
        LOG("could not send everything at once");


> 
> /**
>  * Enqueue multiple events to a queue
>  *
>  * @param queue   Queue handle
>  * @param ev      Event handles
>  * @param num     Number of event handles
>  *
>  * @retval 0 on success
>  * @retval <0 on failure
>  */
> int odp_queue_enq_multi(odp_queue_t queue, odp_event_t ev[], int num);
> Here we use "int" for the number of items, at least this matches with
> the return type. But there is no indication that the function returns
> the number of events that were enqueued. What happens if we are trying
> to enqueue events to a fixed-size queue (e.g. ring buffer) and all
> items do not fit? Is this considered a failure? It could be
> problematic to have to retry this call until it is possible to enqueue
> all items in the same go. Incremental success should be allowed for
> unless there is a good explanation why this "atomic" semantics is
> desired. The application can decide what to do with those events that
> are not enqueued.
> 
> Same for this:
> int odp_queue_deq_multi(odp_queue_t queue, odp_event_t events[], int num);

True. It's reasonable to allow incremental usage.

@return Number of enqueued events on success.


> 
> /**
>  * Schedule multiple events
>  *
>  * Like odp_schedule(), but returns multiple events from a queue.
>  *
>  * @param from    Output parameter for the source queue (where the event
> was
>  *                dequeued from). Ignored if NULL.
>  * @param wait    Minimum time to wait for an event. Waits infinitely, if
> set to
>  *                ODP_SCHED_WAIT. Does not wait, if set to
> ODP_SCHED_NO_WAIT.
>  *                Use odp_schedule_wait_time() to convert time to other
> wait
>  *                values.
>  * @param events  Event array for output
>  * @param num     Maximum number of events to output
>  *
>  * @return Number of events outputed (0 ... num)
> Number of events scheduled and stored in the events array?
>  */
> int odp_schedule_multi(odp_queue_t *from, uint64_t wait, odp_event_t
> events[],
>                        unsigned int num);
> Unsigned int for number of items, int used as return type (number of
> items processed) but no errors are supposed to occur so negative range
> is not used. Why then use int and not unsigned int for return type?

True. Currently no failure specified, so it could return unsigned. However, to 
harmonize and to enable  forward-compatible failure case: return int / int num?


> 
> So a lot of different models for basically two cases.
> Case 1: Function writes data (size possibly not known in advance) to a
> buffer. Caller must specify buffer size. Function must return number
> of bytes actually written. And be able to return 1) output buffer too
> small and 2) minimum required size of output buffer for successful
> operation.

I think, this 2) should be avoided for now and specify CONFIG_XXX_MAX macros 
instead. That avoids "int *len" output parameter or complex return value ranges 
(len for failure). 

> 
> Case 2: Caller specifies number of items for function to consume or
> produce. Possibly that many items cannot be consumed (e.g. not room
> enough in some ring buffer) or cannot be produced (not available at
> source). Not all items have to be consumed or produced for success
> (incremental success is allowed), indeed even 0 items consumed or
> produced is a success, no error has occurred. Function must be able to
> return number of items consumed/produced. Some functions must also be
> able to return errors.

This has two sub classes:
1) 0 consumed/produced is an error
   - Resource is full. User have to do something about it before a re-try may 
succeed.
2) 0 is acceptable
  - Resource is currently busy, but re-try can succeed later on (without user
    doing anything else in between)


> 
> For #1 I suggest:
> ssize_t func(void *buf, ssize_t *bufsize);
> @return >= 0 on success, number of bytes written (0 is OK when applicable)
> @return <= on failure, if buffer too small (need specific return code
> to indicate this?) then "*bufsize" is updated with the required size

"Required size" not needed feature => output param not needed.

ssize_t is problematic with sizeof().


# if cannot fail or zero can indicate failure
size_t func(void* buf, size_t len)

# if can fail and zero cannot be the failure
ssize_t func(void* buf, size_t len)


> 
> Alternatively if the function is not working with bytes or chars but
> some other type of object, use "int" instead of "ssize_t".
> 
> For #2 I suggest:
> int func(odp_handle_t hdl, /const/ odp_object_t array[], int
> num_elems_in_array);
> @return >= 0 on success, number of elements consumed or produced (0 is
> normally OK)
> @return <0 on failure (when applicable, some functions may not
> actually need to report failures but we can keep this for consistency)

# This is OK. We just have to spec if 0 is success or failure.
int func(hdl, array[], int num_elems_in_array) 

-Petri


> 
> Could there be exceptions to these design rules? Some function that
> might need to operate on >=half the memory space as a consecutive
> area? Not going to happen for 64-bit architectures. Plausible for
> 32-bit architectures but how likely in practice?
> 
> _______________________________________________
> lng-odp mailing list
> lng-odp@lists.linaro.org
> http://lists.linaro.org/mailman/listinfo/lng-odp

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

Reply via email to