/*
 * Copyright 2010-2015 Intel Corporation.
 *
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, version 2.1.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301 USA.
 *
 * Disclaimer: The codes contained in these modules may be specific
 * to the Intel Software Development Platform codenamed Knights Ferry,
 * and the Intel product codenamed Knights Corner, and are not backward
 * compatible with other Intel products. Additionally, Intel will NOT
 * support the codes or instruction set in future products.
 *
 * Intel offers no warranty of any kind regarding the code. This code is
 * licensed on an "AS IS" basis and Intel is not obligated to provide
 * any support, assistance, installation, training, or other services
 * of any kind. Intel is also not obligated to provide any updates,
 * enhancements or extensions. Intel specifically disclaims any warranty
 * of merchantability, non-infringement, fitness for any particular
 * purpose, and any other warranty.
 *
 * Further, Intel disclaims all liability of any kind, including but
 * not limited to liability for infringement of any proprietary rights,
 * relating to the use of the code, even if Intel is notified of the
 * possibility of such liability. Except as expressly stated in an Intel
 * license agreement provided with this code and agreed upon with Intel,
 * no license, express or implied, by estoppel or otherwise, to any
 * intellectual property rights is granted herein.
 */

#ifndef _COIPIPELINE_SOURCE_H
#define _COIPIPELINE_SOURCE_H

/** @ingroup COIPipeline
 *  @addtogroup COIPipelineSource
@{
* @file source/COIPipeline_source.h
*/
#ifndef DOXYGEN_SHOULD_SKIP_THIS

#include "../common/COITypes_common.h"
#include "../common/COIResult_common.h"

#ifdef __cplusplus
extern "C" {
#endif
#endif // DOXYGEN_SHOULD_SKIP_THIS



//////////////////////////////////////////////////////////////////////////////
/// These flags specify how a buffer will be used within a run function. They
/// allow the runtime to make optimizations in how it moves the data around.
/// These flags can affect the correctness of an application, so they must be
/// set properly. For example, if a buffer is used in a run function with the
/// COI_SINK_READ flag and then mapped on the source, the runtime may use a
/// previously cached version of the buffer instead of retrieving data from
/// the sink.
typedef enum COI_ACCESS_FLAGS
{
    /// Specifies that the run function will only read the associated buffer.
    COI_SINK_READ = 1,

    /// Specifies that the run function will write to the associated buffer.
    COI_SINK_WRITE,

    /// Specifies that the run function will overwrite the entire associated
    /// buffer and therefore the buffer will not be synchronized with the
    /// source before execution.
    COI_SINK_WRITE_ENTIRE,

    /// Specifies that the run function will only read the associated buffer
    /// and will maintain the reference count on the buffer after
    /// run function exit.
    COI_SINK_READ_ADDREF,

    /// Specifies that the run function will write to the associated buffer
    /// and will maintain the reference count on the buffer after
    /// run function exit.
    COI_SINK_WRITE_ADDREF,

    /// Specifies that the run function will overwrite the entire associated
    /// buffer and therefore the buffer will not be synchronized with the
    /// source before execution and will maintain the reference count on the
    /// buffer after run function exit.
    COI_SINK_WRITE_ENTIRE_ADDREF
} COI_ACCESS_FLAGS;

#define COI_PIPELINE_MAX_PIPELINES 512
#define COI_PIPELINE_MAX_IN_BUFFERS 16384
#define COI_PIPELINE_MAX_IN_MISC_DATA_LEN 32768


///////////////////////////////////////////////////////////////////////////////
///
/// Create a pipeline associated with a remote process. This pipeline can
/// then be used to execute remote functions and to share data using
/// COIBuffers.
///
/// @param  in_Process
///         [in] A handle to an already existing process that the pipeline
///         will be associated with.
///
/// @param  in_Mask
///         [in] An optional mask of the set of hardware threads on which the
///         sink pipeline command processing thread could run.
///
/// @param  in_StackSize
///         [in] An optional value that will be used when the pipeline
///         processing thread is created on the sink. If the user passes in
///         0 the OS default stack size will be used. Otherwise the value
///         must be PTHREAD_STACK_MIN (16384) bytes or larger and must be
///         a multiple of a page (4096 bytes).
///
/// @param  out_pPipeline
///         [out] Handle returned to uniquely identify the pipeline that was
///         created for use in later API calls.
///
///
/// @return COI_SUCCESS if the pipeline was successfully created.
///
/// @return COI_INVALID_HANDLE if the in_Process handle passed in was invalid.
///
/// @return COI_INVALID_POINTER if the out_pPipeline pointer was NULL.
///
/// @return COI_RESOURCE_EXHAUSTED if no more COIPipelines can be created. The
///         maximum number of pipelines allowed is COI_PIPELINE_MAX_PIPELINES.
///         It is recommended in most cases to not exceed the number of CPU's
///         that are reported on the offload device, performance will suffer.
///
///
/// @return COI_OUT_OF_RANGE if the in_StackSize > 0 &&
///         in_StackSize < PTHREAD_STACK_MIN or if in_StackSize is not a
///         multiple of a page (4096 bytes).
///
/// @return COI_OUT_OF_RANGE if the in_Mask is set to all zeroes. If no mask
///         is desired then the in_Mask should be passed as NULL, otherwise
///         at least one thread must be set.
///
/// @return COI_TIME_OUT_REACHED if establishing the communication channel with
///         the remote pipeline timed out.
///
/// @return COI_RETRY if the pipeline cannot be created due to the number of
///         source-to-sink connections in use. A subsequent call to
///         COIPipelineCreate may succeed if resources are freed up.
///
/// @return COI_PROCESS_DIED if in_Process died.
///
COIACCESSAPI
COIRESULT
COIPipelineCreate(
            COIPROCESS          in_Process,
            COI_CPU_MASK        in_Mask,
            uint32_t            in_StackSize,
            COIPIPELINE*        out_pPipeline);

///////////////////////////////////////////////////////////////////////////////
///
/// Destroys the indicated pipeline, releasing its resources.
///
/// @param  in_Pipeline
///         [in] Pipeline to destroy.
///
///
/// @return COI_SUCCESS if the pipeline was destroyed
///
COIACCESSAPI
COIRESULT
COIPipelineDestroy(
            COIPIPELINE         in_Pipeline);


//////////////////////////////////////////////////////////////////////////////
///
/// Enqueues a function in the remote process binary to be executed. The
/// function execution is asynchronous in regards to the Source and all
/// run functions enqueued on a pipeline are executed in-order. The run
/// function will only execute when all of the required buffers are present
/// in the Sink's memory.
///
/// Potential Hazards while using Runfunctions:
///
/// 1. Proper care has to be taken while setting the input dependencies for
///    RunFunctions. Setting it incorrectly can lead to cyclic dependencies
///    and can cause the respective pipeline to stall.
/// 2. RunFunctions can also segfault if enough memory space is not available
///    on the sink for the buffers passed in. Pinned buffers and buffers that
///    are AddRef'd need to be accounted for available memory space. In other
///    words, this memory is not available for use until it is freed up.
/// 3. Unexpected segmentation faults or erroneous behavior can occur if
///    handles or data passed in to Runfunction gets destroyed before the
///    RunFunction finishes.
///    For example, if a variable passed in as Misc data or the buffer gets
///    destroyed before the runtime receives the completion notification
///    of the Runfunction, it can cause unexpected behavior. So it is always
///    recommended to wait for RunFunction completion event before any related
///    destroy event occurs.
///
/// The runtime expects users to handle such scenarios. COIPipelineRunFunction
/// returns COI_SUCCESS for above cases because it was queued up successfully.
/// Also if you try to destroy a pipeline with a stalled function then the
/// destroy call will hang. COIPipelineDestroy waits until all the functions
/// enqueued are finished executing.
///
/// @param  in_Pipeline
///         [in] Handle to a previously created pipeline that this run
///         function should be enqueued to.
///
/// @param  in_Function
///         [in] Previously returned handle from a call to
///         COIPipelineGetFunctionHandle() that represents a function in the
///         application running on the Sink process.
///
/// @param  in_NumBuffers
///         [in] The number of buffers that are being passed to the run
///         function. This number must match the number of buffers in the
///         in_pBuffers and in_pBufferAccessFlags arrays. Must be less than
///         COI_PIPELINE_MAX_IN_BUFFERS.
///
/// @param  in_pBuffers
///         [in] An array of COIBUFFER handles that the function is expected
///         to use during its execution. Each buffer when it arrives at the
///         Sink process will be at least 4k page aligned, thus, using a very
///         large number of small buffers is memory inefficient and should be
///         avoided.
///
/// @param  in_pBufferAccessFlags
///         [in] An array of flag values which correspond to the buffers
///         passed in the in_pBuffers parameter. These flags are used to
///         track dependencies between different run functions being
///         executed from different pipelines.
///
/// @param  in_NumDependencies
///         [in] The number of dependencies specified in the in_pDependencies
///         array. This may be 0 if the caller does not want the run function
///         to wait for any dependencies.
///
/// @param  in_pDependencies
///         [in] An optional array of COIEVENT objects that this run
///         function will wait for before executing. This allows the user to
///         create dependencies between run functions in different pipelines.
///         The user may pass in NULL if they do not wish to wait for any
///         dependencies to complete.
///
/// @param  in_pMiscData
///         [in] Pointer to user defined data, typically used to pass
///         parameters to Sink side functions. Should only be used for small
///         amounts data since the data will be placed directly in the
///         Driver's command buffer. COIBuffers should be used to pass large
///         amounts of data.
///
/// @param  in_MiscDataLen
///         [in] Size of the in_pMiscData in bytes. Must be less than
///         COI_PIPELINE_MAX_IN_MISC_DATA_LEN, and should usually be much
///         smaller, see documentation for the parameter in_pMiscData.
///
/// @param  out_pAsyncReturnValue
///         [out] Pointer to user-allocated memory where the return value from
///         the run function will be placed. This memory should not be read
///         until out_pCompletion has been signaled.
///
/// @param  in_AsyncReturnValueLen
///         [in] Size of the out_pAsyncReturnValue in bytes.
///
/// @param  out_pCompletion
///         [out] An optional pointer to a COIEVENT object
///         that will be signaled when this run function has completed
///         execution. The user may pass in NULL if they wish for this function
///         to be synchronous, otherwise if a COIEVENT object is passed in the
///         function is then asynchronous and closes after enqueuing the
///         RunFunction and passes back the COIEVENT that will be signaled
///         once the RunFunction has completed.
///
/// @return COI_SUCCESS if the function was successfully placed in a
///         pipeline for future execution. Note that the actual
///         execution of the function will occur in the future.
///
/// @return COI_OUT_OF_RANGE if in_NumBuffers is greater than
///         COI_PIPELINE_MAX_IN_BUFFERS or if in_MiscDataLen is greater than
///         COI_PIPELINE_MAX_IN_MISC_DATA_LEN.
///
/// @return COI_INVALID_HANDLE if the pipeline handle passed in was invalid.
///
/// @return COI_INVALID_HANDLE if the function handle passed in was invalid.
///
/// @return COI_INVALID_HANDLE if any of the buffers passed in are invalid.
///
/// @return COI_ARGUMENT_MISMATCH if in_NumDependencies is non-zero while
///         in_pDependencies was passed in as NULL.
///
/// @return COI_ARGUMENT_MISMATCH if in_pDependencies is non-NULL but
///         in_NumDependencies is zero.
///
/// @return COI_ARGUMENT_MISMATCH if in_MiscDataLen is non-zero while
///         in_pMiscData was passed in as NULL.
///
/// @return COI_ARGUMENT_MISMATCH if in_pMiscData is non-NULL but
///         in_MiscDataLen is zero.
///
/// @return COI_ARGUMENT_MISMATCH if in_NumBuffers is non-zero and in_pBuffers
///         or in_pBufferAccessFlags are NULL.
///
/// @return COI_ARGUMENT_MISMATCH if in_pBuffers is non-NULL but
///         in_NumBuffers is zero.
///
/// @return COI_ARGUMENT_MISMATCH if in_pBufferAccessFlags is non-NULL but
///         in_NumBuffers is zero.
///
/// @return COI_ARGUMENT_MISMATCH if in_ReturnValueLen is non-zero while
///         in_pReturnValue was passed in as NULL.
///
/// @return COI_ARGUMENT_MISMATCH if in_pReturnValue is non-NULL but
///         in_ReturnValueLen is zero.
///
/// @return COI_RETRY if any input buffers, which are not pinned buffers,
///         are still mapped when passed to the run function.
///
/// @return COI_MISSING_DEPENDENCY if buffer was not created on the process
///         associated with the pipeline that was passed in.
///
/// @return COI_OUT_OF_RANGE if any of the access flags in
///         in_pBufferAccessFlags is not a valid COI_ACCESS_FLAGS.
///
COIACCESSAPI
COIRESULT
COIPipelineRunFunction(
            COIPIPELINE         in_Pipeline,
            COIFUNCTION         in_Function,
            uint32_t            in_NumBuffers,
    const   COIBUFFER*          in_pBuffers,
    const   COI_ACCESS_FLAGS*   in_pBufferAccessFlags,
            uint32_t            in_NumDependencies,
    const   COIEVENT*           in_pDependencies,
    const   void*               in_pMiscData,
            uint16_t            in_MiscDataLen,
            void*               out_pAsyncReturnValue,
            uint16_t            in_AsyncReturnValueLen,
            COIEVENT*           out_pCompletion);


//////////////////////////////////////////////////////////////////////////////
///
/// Retrieve the engine that the pipeline is associated with.
///
/// @param  in_Pipeline
///         [in] Pipeline to query.
///
/// @param  out_pEngine
///         [out] The handle of the Engine.
///
/// @return COI_SUCCESS if the engine was retrieved.
///
/// @return COI_INVALID_HANDLE if the pipeline handle passed in was invalid.
///
/// @return COI_INVALID_POINTER if the out_pEngine parameter is NULL.
///
/// @return COI_PROCESS_DIED if the process associated with this engine died.
///
COIACCESSAPI
COIRESULT
COIPipelineGetEngine(
            COIPIPELINE         in_Pipeline,
            COIENGINE*          out_pEngine);

//////////////////////////////////////////////////////////////////////////////
///
/// Add a particular core:thread pair to a COI_CPU_MASK.
///
/// @param  in_Process
///         [in] A handle to an already existing process that the pipeline
///         will be associated with.
///
/// @param  in_CoreID
///         [in] Core to affinitize to; must be less than the number of cores
///         on the device.
///
/// @param  in_ThreadID
///         [in] Thread on the core to affinitize to (0 - 3).
///
/// @param  out_pMask
///         [out] Pointer to the mask to set.
///
/// @warning Unless it is explicitly done, the contents of the mask may not
///          be zero when creating or declaring a COI_CPU_MASK variable.
///
/// @return COI_SUCCESS if the mask was set.
///
/// @return COI_OUT_OF_RANGE if the in_CoreID or in_ThreadID is out of range.
///
/// @return COI_INVALID_POINTER if out_pMask is invalid.
///
/// @return COI_INVALID_HANDLE if in_Process is invalid.
///
COIACCESSAPI
COIRESULT
COIPipelineSetCPUMask(
            COIPROCESS          in_Process,
            uint32_t            in_CoreID,
            uint8_t             in_ThreadID,
            COI_CPU_MASK*       out_pMask);

//////////////////////////////////////////////////////////////////////////////
///
/// Clears a given mask. Note that the memory contents of COI_CPU_MASK are not
/// guaranteed to be zero when declaring a COI_CPU_MASK variable. Thus, prior
/// to setting a specific affinity to in_Mask it is important to call this
/// function first.
///
/// @param  in_Mask
///         [in] Pointer to the mask to clear.
///
/// @return COI_SUCCESS if the mask was cleared.
///
/// @return COI_INVALID_POINTER if in_Mask is invalid.
///
COIACCESSAPI
COIRESULT
COIPipelineClearCPUMask(
            COI_CPU_MASK*       in_Mask);

#ifdef __cplusplus
} /* extern "C" */
#endif

#endif /* _COIPIPELINE_SOURCE_H */

/*! @} */
