/*************************************************************************** * * Copyright (C) 2001 International Business Machines * All rights reserved. * * This file is part of the GPFS mmfslinux kernel module. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *************************************************************************** */ /* @(#)12 1.27 src/avs/fs/mmfs/ts/kernext/ibm-kxi/cxiIOBuffer.h, mmfs, avs_rgpfs24, rgpfs240610b 5/4/04 10:12:05 */ /* * Abstraction of an I/O buffer * struct cxiIOBuffer_t * InitIOBuffer * GetDaemonIOBufferEndAddr * * Support for buffers that are not contiguous in memory * struct cxiDiscontiguousDirectoryBuffer_t * InitDiscontiguousBuffer * OffsetToPtr * MapContiguousBuffer */ #ifndef _h_cxiIOBuffer #define _h_cxiIOBuffer #include /* Use logging definitions for non portability layer */ #if !defined(GPFS_GPL) #include #endif /* !GPFS_GPL */ /* Abstraction of an I/O buffer. I/O buffers are contiguous in virtual memory from the point of view of the GPFS daemon. The view of the buffer from kernel code is platform-dependent. The first operation that must be performed on an I/O buffer must be to attach it to kernel memory. Once this is done, the buffer may be accessed indirectly by transferring data between it and buffers in user or kernel space. The buffer may also be mapped so that normal loads and stores from the kernel may access the data in the buffer. In general, such a mapping will not be contiguous in the address space of the kernel, so the code that accesses the contents of the buffer must be aware of page boundaries. Once all direct and indirect accesses to the buffer are complete, it must be detached from kernel memory. */ /* Forward declarations */ /* Kernel data structure associated with an I/O buffer */ struct cxiKernelIOBufferDesc_t; /* Result of making a read-only copy of a portion of an I/O buffer */ struct cxiContiguousBuffer_t; /* Result of mapping a buffer too large to be mapped contiguously */ struct cxiDiscontiguousDirectoryBuffer_t; /* Handle that describes a cxiIOBuffer_t that has been attached */ struct cxiIOBufferAttachment_t; struct cxiIOBuffer_t; /* Include platform-specific definitions and possibly define * __CXI_BUFFERS_ARE_CONTIGUOUS to affect the definition of * cxiDiscontiguousDirectoryBuffer_t below. */ #include #include #define BUDDY_NONE 0 #define BUDDY_FIRST 1 #define BUDDY_LAST 255 /* unsigned char max */ #ifndef __cplusplus /* forward declaration in C for Linux portability layer */ typedef struct Buffer Buffer; typedef Buffer *BufferXPtr; #endif struct cxiMemoryDesc_t { unsigned char vindex; /* index in shared seg memMapping array */ unsigned char buddyNum; /* buddies that can be rejoined share a number */ unsigned short klength; /* length of area in kilobytes */ unsigned int offset; /* offset within the segment */ /* If C++ would allow me to make a union between inactive and bufP * these could be coalesced to make the structure smaller. * (something on the inactive list shouldn't have a buffer) */ BufferXPtr bufP; /* pointer to owning buffer */ /* list of descriptors on inactive list */ DLink_t inactive; /* circular doubly linked list of memory siblings */ DLink_t sibling; }; typedef struct cxiMemoryDesc_t cxiMemoryDesc_t; #ifdef GPFS_GPL /* handle a few shared cxi structures (for swizzling) */ typedef cxiMemoryDesc_t *cxiMemoryDesc_tXPtr; #endif static inline void InitMemoryDesc(struct cxiMemoryDesc_t *mdP) { mdP->vindex = 0; mdP->buddyNum = BUDDY_NONE; mdP->klength = 0; mdP->offset = 0; mdP->bufP = NULL; DLinkInit(&mdP->inactive); DLinkInit(&mdP->sibling); } /* I/O buffer description On AIX, an I/O buffer in the virtual address space of the daemon must be pinned before it can be used for I/O, and must be mapped before accessing its contents. On AIX, the kernel mapping will be contiguous, although not necessarily at the same addresses as used by the daemon. On Linux, an I/O buffer in the virtual address space of the daemon must be pinned before it can be used for I/O, and must be mapped before accessing its contents. On Linux, pages of the buffer will in general not be contiguous in kernel virtual memory. The cxiK... routines just call into the kernel (kx...), which then calls the corresponding routines on cxiKernelIOBufferDesc_t's (Kibd...). */ struct cxiIOBuffer_t { /* Length of buffer in bytes */ int ioBufLen; /* Number of users that are currently using this buffer and require it to be pinned in memory. A value of 0 means the buffer is not pinned. */ int pinCount; /* Pointer to beginning of contiguous buffer in daemon virtual address space */ char* daemonBufP; /* Pointer to kernel data structure associated with this I/O buffer */ struct cxiKernelIOBufferDesc_t* kernelIOBufferDescP; /* A chain of possibly multiple memory descriptors */ cxiMemoryDesc_tXPtr descP; }; static inline char * OffsetToDataPtr(const struct cxiIOBuffer_t *iobP, int offset, int length) { return iobP->daemonBufP + offset; } /* Clear all fields of an cxiIOBuffer_t to default values */ static inline void InitIOBuffer(struct cxiIOBuffer_t* iobP) { iobP->ioBufLen = -1; iobP->pinCount = 0; iobP->daemonBufP = NULL; iobP->kernelIOBufferDescP = NULL; iobP->descP = NULL; } /* Return true if the fields describing the IOBuffer are self-consistent. */ static inline Boolean IOBufferIsConsistent(struct cxiIOBuffer_t* iobP) { return IOBUFFER_IS_CONSISTENT(iobP); }; /* Return the daemon address of the first byte beyond the end of the I/O buffer */ static inline char* GetDaemonIOBufferEndAddr(struct cxiIOBuffer_t* iobP) { return iobP->daemonBufP + iobP->ioBufLen; }; #if 0 /* Pin all the pages of a buffer. If the buffer is already pinned, just increment its pin count. Returns EOK if successful, other error codes if unsuccessful. */ int pin(); /* Decrement the pin count on the buffer. If the count reaches zero, unpin the pages of the buffer. */ void unpin(); #endif /* Attach an I/O buffer to the kernel's virtual address space. The cxiIOBufferAttachment_t returned in *attachP must be used as a parameter of most of the other operations on cxiIOBuffer_t's. */ EXTERNC void cxiAttachIOBuffer(struct cxiIOBuffer_t* iobP, struct cxiIOBufferAttachment_t* attachP); /* Detach a buffer from the kernel's virtual address space. */ EXTERNC void cxiDetachIOBuffer(struct cxiIOBuffer_t* iobP, struct cxiIOBufferAttachment_t* attachP); /* Transfer len bytes beginning at offset bufOffset within I/O buffer *iobP to or from a user buffer. The direction of the transfer is given with respect to the I/O buffer. Returns EOK if successful, other error codes if unsuccessful. */ EXTERNC int cxiUXfer(struct cxiIOBuffer_t* iobP, Boolean toIOBuffer, const struct cxiIOBufferAttachment_t* attachP, void* vkopP, int bufOffset, int len, struct cxiUio_t* uioP); #define CXI_XFER_TO_IOBUFFER true #define CXI_XFER_FROM_IOBUFFER false /* Perform cross-memory transfer of len bytes from user memory in current task to memory in specified address space. If toXmem is true then copy is from userAddrP to udataP/xmemP, otherwise the opposite. */ EXTERNC int cxiXmemXfer(char *userAddrP, int len, char *udataP, cxiXmem_t *xmemP, Boolean toXmem); /* Transfer len bytes beginning at offset bufOffset within I/O buffer *iobP to or from a contiguous kernel buffer. The direction of the transfer is given with respect to the I/O buffer. Returns EOK if successful, other error codes if unsuccessful. */ EXTERNC int cxiKXfer(struct cxiIOBuffer_t* iobP, Boolean toIOBuffer, const struct cxiIOBufferAttachment_t* attachP, int bufOffset, int len, char* kBufP); /* Set len bytes beginning at offset bufOffset within I/O buffer *iobP to zero. Returns EOK if successful, other error codes if unsuccessful. */ EXTERNC int cxiKZero(struct cxiIOBuffer_t* iobP, const struct cxiIOBufferAttachment_t* attachP, int bufOffset, int len); /* Map an I/O buffer so it can be read and written from kernel code running in the context of a user thread. Depending on the platform, the addresses at which the I/O buffer gets mapped may not be contiguous. The details of how the buffer got mapped are handled by the cxiDiscontiguousDirectoryBuffer_t object that is filled in by this call. On some platforms, mapping buffers using this call consumes scarce resources, so all cxiMapDiscontiguousRW calls should be promptly matched by unmapDiscontiguousRW calls as soon as the operation that required access to the I/O buffer completes. Returns EOK if successful, other error codes if unsuccessful. */ EXTERNC int cxiMapDiscontiguousRW(struct cxiIOBuffer_t* iobP, const struct cxiIOBufferAttachment_t* attachP, struct cxiDiscontiguousDirectoryBuffer_t* discontigP); /* Unmap an I/O buffer previously mapped */ EXTERNC void cxiUnmapDiscontiguousRW(struct cxiIOBuffer_t* iobP, struct cxiDiscontiguousDirectoryBuffer_t* discontigP); /* Return an address in kernel memory that holds a contigous read-only copy of a portion of an I/O buffer. If possible, this will be a mapping of the I/O buffer. If necessary, this routine will allocate a new block of kernel memory and copy the requested data to it. The returned cxiContiguousBuffer_t encapsulates what method was used, so that unmapContiguousRO can release whatever resources were obtained by this call. Returns EOK if successful, other error codes if unsuccessful. */ EXTERNC int cxiMapContiguousRO(struct cxiIOBuffer_t* iobP, const struct cxiIOBufferAttachment_t* attachP, int bufOffset, int len, const char** contigBasePP, struct cxiContiguousBuffer_t* contigP); /* Release a mapping or copy obtained with cxiMapContiguousRO */ EXTERNC void cxiUnmapContiguousRO(struct cxiIOBuffer_t* iobP, struct cxiContiguousBuffer_t* contigP); /* Maximum size of a directory block. Must be a multiple of PAGE_SIZE. */ #define MAX_DIRBLOCK_SIZE (32*1024) /* The size of the cxiDiscontiguousDirectoryBuffer_t object may be different depending on whether or not _KERNEL is defined, so do not define cxiDiscontiguousDirectoryBuffer_t if this code will be compiled once and used in both places. */ #ifndef USED_IN_DAEMON_AND_KERNEL /* Page size controlling alignment and unit of relocation for discontiguous buffers (must be equal to operating system page size) */ #define DISCONTIG_PAGE_SIZE PAGE_SIZE /* Description of a buffer that is not necessarily contiguous in memory. All buffers managed through this type must begin on page boundaries. */ struct cxiDiscontiguousDirectoryBuffer_t { /* Number of valid bytes in the buffer. A value of -1 indicates that the buffer is not mapped and should not be accessed. */ int mappedLen; #ifdef __CXI_BUFFERS_ARE_CONTIGUOUS /* Pointer to the contiguous buffer */ char* dataP; #else /* Number of paes in a maximum size directory block */ #define MAX_PAGES_PER_DIRBLOCK (MAX_DIRBLOCK_SIZE/DISCONTIG_PAGE_SIZE) /* Array of pointers to the pages that contain the buffer. This array contains the addresses at which the pages of the buffer can be accessed by the thread that called cxiMapDiscontiguousRW. */ char* userPagePointerArray[MAX_PAGES_PER_DIRBLOCK]; /* Array of pointers to OS-specific data structures needed to unmap each of the pages given by the array of pointers above. */ void* osPagePointerArray[MAX_PAGES_PER_DIRBLOCK]; #endif /* __CXI_BUFFERS_ARE_CONTIGUOUS */ }; /* Initialize a cxiDiscontiguousDirectoryBuffer_t */ static inline void InitDiscontiguousBuffer(struct cxiDiscontiguousDirectoryBuffer_t* ddbP) { /* Indicate buffer is invalid */ ddbP->mappedLen = -1; }; /* Convert an offset into a logical buffer into a pointer to the byte at that offset. The caller is assumed to know the length of the object beginning at the returned pointer, and is not supposed to access beyond the end of the page in which the pointer points */ static inline char* OffsetToPtr(const struct cxiDiscontiguousDirectoryBuffer_t* ddbP, int offset) { char* pageStartP; DBGASSERT(offset >= 0); DBGASSERT(offset < ddbP->mappedLen); #ifdef __CXI_BUFFERS_ARE_CONTIGUOUS return ddbP->dataP+offset; #else pageStartP = ddbP->userPagePointerArray[offset/DISCONTIG_PAGE_SIZE]; return pageStartP + offset%DISCONTIG_PAGE_SIZE; #endif /* __CXI_BUFFERS_ARE_CONTIGUOUS */ }; /* Given a contiguous buffer, set up a mapping as if it was discontiguous */ static inline void MapContiguousBuffer(char* bufP, int len, struct cxiDiscontiguousDirectoryBuffer_t* ddbP) { int i; ddbP->mappedLen = len; #ifdef __CXI_BUFFERS_ARE_CONTIGUOUS ddbP->dataP = bufP; #else /* Either we should start on a page boundary or our total buffer * should fit within the same page. */ DBGASSERT(((UIntPtr)bufP & (DISCONTIG_PAGE_SIZE-1)) == 0 || ((UIntPtr)bufP & ~(DISCONTIG_PAGE_SIZE-1)) == ((UIntPtr)(bufP + len - 1) & ~(DISCONTIG_PAGE_SIZE-1))); DBGASSERT(len <= MAX_PAGES_PER_DIRBLOCK*DISCONTIG_PAGE_SIZE); for (i=0 ; len>0 ; i++, bufP+=DISCONTIG_PAGE_SIZE, len-=DISCONTIG_PAGE_SIZE) { ddbP->userPagePointerArray[i] = bufP; ddbP->osPagePointerArray[i] = NULL; } #endif /* __CXI_BUFFERS_ARE_CONTIGUOUS */ }; #endif /* USED_IN_DAEMON_AND_KERNEL */ #endif /* _h_cxiIOBuffer */