root/src/system/kernel/device_manager/IORequest.h
/*
 * Copyright 2008-2011, Ingo Weinhold, ingo_weinhold@gmx.de.
 * Copyright 2008, Axel Dörfler, axeld@pinc-software.de.
 * Distributed under the terms of the MIT License.
 */
#ifndef IO_REQUEST_H
#define IO_REQUEST_H


#include <sys/uio.h>

#include <new>

#include <fs_interface.h>

#include <condition_variable.h>
#include <lock.h>
#include <util/DoublyLinkedList.h>

#include "dma_resources.h"


#define B_PHYSICAL_IO_REQUEST   0x01    /* buffer points to physical memory */
#define B_VIP_IO_REQUEST                0x02    /* used by the page writer -- make sure
                                                                                   allocations won't fail */
#define B_DELETE_IO_REQUEST             0x04    /* delete request when finished */

class DMABuffer;
struct IOOperation;

typedef struct IOOperation io_operation;

class IOBuffer : public DoublyLinkedListLinkImpl<IOBuffer> {
public:
        static  IOBuffer*                       Create(uint32 count, bool vip);
                        void                            Delete();

                        bool                            IsVirtual() const { return !fPhysical; }
                        bool                            IsPhysical() const { return fPhysical; }
                        bool                            IsUser() const { return fUser; }

                        void                            SetVecs(generic_size_t firstVecOffset,
                                                                        generic_size_t lastVecSize,
                                                                        const generic_io_vec* vecs, uint32 count,
                                                                        generic_size_t length, uint32 flags);

                        void                            SetPhysical(bool physical)
                                                                        { fPhysical = physical; }
                        void                            SetUser(bool user) { fUser = user; }
                        void                            SetLength(generic_size_t length)
                                                                        { fLength = length; }
                        void                            SetVecCount(uint32 count) { fVecCount = count; }

                        generic_size_t          Length() const { return fLength; }

                        generic_io_vec*         Vecs() { return fVecs; }
                        generic_io_vec&         VecAt(size_t index) { return fVecs[index]; }
                        size_t                          VecCount() const { return fVecCount; }
                        size_t                          Capacity() const { return fCapacity; }

                        status_t                        GetNextVirtualVec(void*& cookie, iovec& vector);
                        void                            FreeVirtualVecCookie(void* cookie);

                        status_t                        LockMemory(team_id team, bool isWrite);
                        void                            UnlockMemory(team_id team, bool isWrite);
                        bool                            IsMemoryLocked() const
                                                                        { return fMemoryLocked; }

                        void                            Dump() const;

private:
                                                                IOBuffer();
                                                                ~IOBuffer();
                                                                        // not implemented
                        void                            _UnlockMemory(team_id team, size_t count,
                                                                        bool isWrite);

                        bool                            fUser;
                        bool                            fPhysical;
                        bool                            fVIP;
                        bool                            fMemoryLocked;
                        generic_size_t          fLength;
                        size_t                          fVecCount;
                        size_t                          fCapacity;
                        generic_io_vec          fVecs[1];
};


struct IORequest;
struct IORequestOwner;


class IORequestChunk {
public:
                                                                IORequestChunk();
        virtual                                         ~IORequestChunk();

                        IORequest*                      Parent() const { return fParent; }
                        void                            SetParent(IORequest* parent)
                                                                        { fParent = parent; }

                        status_t                        Status() const { return fStatus; }

                        DoublyLinkedListLink<IORequestChunk>*
                                                                        ListLink()      { return &fListLink; }

protected:
                        void                            SetStatus(status_t status)
                                                                        { fStatus = status; }
                        void                            ResetStatus()
                                                                        { fStatus = 1; }

protected:
                        IORequest*                      fParent;
                        status_t                        fStatus;

public:
                        DoublyLinkedListLink<IORequestChunk> fListLink;
};

typedef DoublyLinkedList<IORequestChunk,
        DoublyLinkedListMemberGetLink<IORequestChunk, &IORequestChunk::fListLink> >
                IORequestChunkList;


struct IOOperation : IORequestChunk, DoublyLinkedListLinkImpl<IOOperation> {
public:
                        bool                            Finish();
                                                                        // returns true, if it can be recycled
                                                                        // otherwise, there is more to be done

                        void                            SetStatus(status_t status, generic_size_t completedLength);

                        status_t                        Prepare(IORequest* request);
                        void                            SetOriginalRange(off_t offset,
                                                                        generic_size_t length);
                                                                        // also sets range
                        void                            SetRange(off_t offset, generic_size_t length);

                        off_t                           Offset() const;
                        generic_size_t          Length() const;

                        off_t                           OriginalOffset() const
                                                                        { return fOriginalOffset; }
                        generic_size_t          OriginalLength() const
                                                                        { return fOriginalLength; }

                        generic_size_t          TransferredBytes() const
                                                                        { return fTransferredBytes; }

                        generic_io_vec*         Vecs() const;
                        uint32                          VecCount() const;

                        void                            SetPartial(bool partialBegin, bool partialEnd);
                        bool                            HasPartialBegin() const
                                                                        { return fPartialBegin; }
                        bool                            HasPartialEnd() const
                                                                        { return fPartialEnd; }
                        bool                            IsWrite() const;
                        bool                            IsRead() const;

                        void                            SetBlockSize(generic_size_t blockSize)
                                                                        { fBlockSize  = blockSize; }

                        bool                            UsesBounceBuffer() const
                                                                        { return fUsesBounceBuffer; }
                        void                            SetUsesBounceBuffer(bool uses)
                                                                        { fUsesBounceBuffer = uses; }

                        DMABuffer*                      Buffer() const { return fDMABuffer; }
                        void                            SetBuffer(DMABuffer* buffer)
                                                                        { fDMABuffer = buffer; }

                        void                            Dump() const;

protected:
                        void                            _PrepareVecs();
                        status_t                        _CopyPartialBegin(bool isWrite,
                                                                        bool& partialBlockOnly);
                        status_t                        _CopyPartialEnd(bool isWrite);

                        DMABuffer*                      fDMABuffer;
                        off_t                           fOffset;
                        off_t                           fOriginalOffset;
                        generic_size_t          fLength;
                        generic_size_t          fOriginalLength;
                        generic_size_t          fTransferredBytes;
                        generic_size_t          fBlockSize;
                        uint16                          fSavedVecIndex;
                        uint16                          fSavedVecLength;
                        uint8                           fPhase;
                        bool                            fPartialBegin;
                        bool                            fPartialEnd;
                        bool                            fUsesBounceBuffer;
};


typedef IOOperation io_operation;
typedef DoublyLinkedList<IOOperation> IOOperationList;

typedef struct IORequest io_request;
typedef void (*io_request_finished_callback)(void* data,
                        io_request* request, status_t status, bool partialTransfer,
                        generic_size_t transferredBytes);
typedef status_t (*io_request_iterate_callback)(void* data,
                        io_request* request, bool* _partialTransfer);


struct IORequest : IORequestChunk, DoublyLinkedListLinkImpl<IORequest> {
                                                                IORequest();
        virtual                                         ~IORequest();

        static  IORequest*                      Create(bool vip);

                        status_t                        Init(off_t offset, generic_addr_t buffer,
                                                                        generic_size_t length, bool write,
                                                                        uint32 flags);
                        status_t                        Init(off_t offset, const generic_io_vec* vecs,
                                                                        size_t count, generic_size_t length,
                                                                        bool write, uint32 flags)
                                                                        { return Init(offset, 0, 0, vecs, count,
                                                                                length, write, flags); }
                        status_t                        Init(off_t offset,
                                                                        generic_size_t firstVecOffset,
                                                                        generic_size_t lastVecSize,
                                                                        const generic_io_vec* vecs, size_t count,
                                                                        generic_size_t length, bool write,
                                                                        uint32 flags);

                        void                            SetOwner(IORequestOwner* owner)
                                                                        { fOwner = owner; }
                        IORequestOwner*         Owner() const   { return fOwner; }

                        status_t                        CreateSubRequest(off_t parentOffset,
                                                                        off_t offset, generic_size_t length,
                                                                        IORequest*& subRequest);
                        void                            DeleteSubRequests();

                        void                            SetFinishedCallback(
                                                                        io_request_finished_callback callback,
                                                                        void* cookie);
                        void                            SetIterationCallback(
                                                                        io_request_iterate_callback callback,
                                                                        void* cookie);
                        io_request_finished_callback FinishedCallback(
                                                                        void** _cookie = NULL) const;

                        status_t                        Wait(uint32 flags = 0, bigtime_t timeout = 0);

                        bool                            IsFinished() const
                                                                        { return fStatus != 1
                                                                                && fPendingChildren == 0; }
                        void                            NotifyFinished();
                        bool                            HasCallbacks() const;
                        void                            SetStatusAndNotify(status_t status);

                        void                            OperationFinished(IOOperation* operation);
                        void                            SubRequestFinished(IORequest* request,
                                                                        status_t status, bool partialTransfer,
                                                                        generic_size_t transferEndOffset);
                        void                            SetUnfinished();

                        generic_size_t          RemainingBytes() const
                                                                        { return fRemainingBytes; }
                        generic_size_t          TransferredBytes() const
                                                                        { return fTransferSize; }
                        bool                            IsPartialTransfer() const
                                                                        { return fPartialTransfer; }
                        void                            SetTransferredBytes(bool partialTransfer,
                                                                        generic_size_t transferredBytes);

                        void                            SetSuppressChildNotifications(bool suppress);
                        bool                            SuppressChildNotifications() const
                                                                        { return fSuppressChildNotifications; }

                        bool                            IsWrite() const { return fIsWrite; }
                        bool                            IsRead() const  { return !fIsWrite; }
                        team_id                         TeamID() const          { return fTeam; }
                        thread_id                       ThreadID() const        { return fThread; }
                        uint32                          Flags() const   { return fFlags; }

                        IOBuffer*                       Buffer() const  { return fBuffer; }
                        off_t                           Offset() const  { return fOffset; }
                        generic_size_t          Length() const  { return fLength; }

                        void                            SetOffset(off_t offset) { fOffset = offset; }

                        uint32                          VecIndex() const        { return fVecIndex; }
                        generic_size_t          VecOffset() const       { return fVecOffset; }

                        void                            Advance(generic_size_t bySize);

                        IORequest*                      FirstSubRequest();
                        IORequest*                      NextSubRequest(IORequest* previous);

                        void                            AddOperation(IOOperation* operation);
                        void                            RemoveOperation(IOOperation* operation);

                        status_t                        CopyData(off_t offset, void* buffer,
                                                                        size_t size);
                        status_t                        CopyData(const void* buffer, off_t offset,
                                                                        size_t size);
                        status_t                        ClearData(off_t offset, generic_size_t size);

                        void                            Dump() const;

private:
                        status_t                        _CopyData(void* buffer, off_t offset,
                                                                        size_t size, bool copyIn);
        static  status_t                        _CopySimple(void* bounceBuffer,
                                                                        generic_addr_t external, size_t size,
                                                                        team_id team, bool copyIn);
        static  status_t                        _CopyPhysical(void* bounceBuffer,
                                                                        generic_addr_t external, size_t size,
                                                                        team_id team, bool copyIn);
        static  status_t                        _CopyUser(void* bounceBuffer,
                                                                        generic_addr_t external, size_t size,
                                                                        team_id team, bool copyIn);
        static  status_t                        _ClearDataSimple(generic_addr_t external,
                                                                        generic_size_t size, team_id team);
        static  status_t                        _ClearDataPhysical(generic_addr_t external,
                                                                        generic_size_t size, team_id team);
        static  status_t                        _ClearDataUser(generic_addr_t external,
                                                                        generic_size_t size, team_id team);

                        mutex                           fLock;
                        IORequestOwner*         fOwner;
                        IOBuffer*                       fBuffer;
                        off_t                           fOffset;
                        generic_size_t          fLength;
                        generic_size_t          fTransferSize;
                                                                        // After all subrequests/operations have
                                                                        // finished, number of contiguous bytes at
                                                                        // the beginning of the request that have
                                                                        // actually been transferred.
                        generic_size_t          fRelativeParentOffset;
                                                                        // offset of this request relative to its
                                                                        // parent
                        IORequestChunkList      fChildren;
                        int32                           fPendingChildren;
                        uint32                          fFlags;
                        team_id                         fTeam;
                        thread_id                       fThread;
                        bool                            fIsWrite;
                        bool                            fPartialTransfer;
                        bool                            fSuppressChildNotifications;
                        bool                            fIsNotified;

                        io_request_finished_callback    fFinishedCallback;
                        void*                           fFinishedCookie;
                        io_request_iterate_callback     fIterationCallback;
                        void*                           fIterationCookie;
                        ConditionVariable       fFinishedCondition;

                        // these are for iteration
                        uint32                          fVecIndex;
                        generic_size_t          fVecOffset;
                        generic_size_t          fRemainingBytes;
};


typedef DoublyLinkedList<IORequest> IORequestList;


#endif  // IO_REQUEST_H