Monday, December 26, 2016

ION Allocator, Revisited

4 years ago I wrote a LWN paper on ION Allocator.
I'm surprised to see ION still is changing, after 4 years of in staging.

Here's the latest ION interface in ion.h, but Laura is mainly proposing to adjust ION API so that it relies on the kernel's DMA abstraction layer, the DMA mapping API, to handle DMA cache synchronization and MMU mapping, by updating mainly ion_map_dma_buf() in  ion.c file, so that clients does not have to peek into ION objects to manage DMA operations.

It seems the new way would be to call dma_map_sg() directly using the ION object (or the dma_buf object associated with the ION object).

In comparison, the old way is shown in the call flow below.




1090 static struct dma_buf_ops dma_buf_ops = {
1091         .map_dma_buf = ion_map_dma_buf,
878 static struct sg_table *ion_map_dma_buf(struct dma_buf_attachment *attachment,
879                                         enum dma_data_direction direction)
75 /**
 76  * ion_client_create() -  allocate a client and returns it
 77  * @dev:                the global ion device
 78  * @name:               used for debugging
 79  */
 80 struct ion_client *ion_client_create(struct ion_device *dev,
 81                                      const char *name);
 82 
 83 /**
 84  * ion_client_destroy() -  free's a client and all it's handles
 85  * @client:     the client
 86  *
 87  * Free the provided client and all it's resources including
 88  * any handles it is holding.
 89  */
 90 void ion_client_destroy(struct ion_client *client);
 91 
 92 /**
 93  * ion_alloc - allocate ion memory
 94  * @client:             the client
 95  * @len:                size of the allocation
 96  * @align:              requested allocation alignment, lots of hardware blocks
 97  *                      have alignment requirements of some kind
 98  * @heap_id_mask:       mask of heaps to allocate from, if multiple bits are set
 99  *                      heaps will be tried in order from highest to lowest
100  *                      id
101  * @flags:              heap flags, the low 16 bits are consumed by ion, the
102  *                      high 16 bits are passed on to the respective heap and
103  *                      can be heap custom
104  *
105  * Allocate memory in one of the heaps provided in heap mask and return
106  * an opaque handle to it.
107  */
108 struct ion_handle *ion_alloc(struct ion_client *client, size_t len,
109                              size_t align, unsigned int heap_id_mask,
110                              unsigned int flags);
111 
112 /**
113  * ion_free - free a handle
114  * @client:     the client
115  * @handle:     the handle to free
116  *
117  * Free the provided handle.
118  */
119 void ion_free(struct ion_client *client, struct ion_handle *handle);
120 
121 /**
122  * ion_map_kernel - create mapping for the given handle
123  * @client:     the client
124  * @handle:     handle to map
125  *
126  * Map the given handle into the kernel and return a kernel address that
127  * can be used to access this address.
128  */
129 void *ion_map_kernel(struct ion_client *client, struct ion_handle *handle);
130 
131 /**
132  * ion_unmap_kernel() - destroy a kernel mapping for a handle
133  * @client:     the client
134  * @handle:     handle to unmap
135  */
136 void ion_unmap_kernel(struct ion_client *client, struct ion_handle *handle);
137 
138 /**
139  * ion_share_dma_buf() - share buffer as dma-buf
140  * @client:     the client
141  * @handle:     the handle
142  */
143 struct dma_buf *ion_share_dma_buf(struct ion_client *client,
144                                                 struct ion_handle *handle);
145 
146 /**
147  * ion_share_dma_buf_fd() - given an ion client, create a dma-buf fd
148  * @client:     the client
149  * @handle:     the handle
150  */
151 int ion_share_dma_buf_fd(struct ion_client *client, struct ion_handle *handle);
152 
153 /**
154  * ion_import_dma_buf() - get ion_handle from dma-buf
155  * @client:     the client
156  * @dmabuf:     the dma-buf
157  *
158  * Get the ion_buffer associated with the dma-buf and return the ion_handle.
159  * If no ion_handle exists for this buffer, return newly created ion_handle.
160  * If dma-buf from another exporter is passed, return ERR_PTR(-EINVAL)
161  */
162 struct ion_handle *ion_import_dma_buf(struct ion_client *client,
163                                       struct dma_buf *dmabuf);
164 
165 /**
166  * ion_import_dma_buf_fd() - given a dma-buf fd from the ion exporter get handle
167  * @client:     the client
168  * @fd:         the dma-buf fd
169  *
170  * Given an dma-buf fd that was allocated through ion via ion_share_dma_buf_fd,
171  * import that fd and return a handle representing it. If a dma-buf from
172  * another exporter is passed in this function will return ERR_PTR(-EINVAL)
173  */
174 struct ion_handle *ion_import_dma_buf_fd(struct ion_client *client, int fd);
175 
176 #endif /* _LINUX_ION_H */

Thursday, December 22, 2016

Advanced Cryptography, Number Theory and Algebra

Interesting link b/w public key cryptography taught by David Moneh of Stanford. He recommended math book by Victor Shoup of New York University:
  1. https://www.coursera.org/learn/crypto/supplement/wCCF4/more-background-on-number-theory
  2. http://shoup.net/ntb/ntb-v2.pdf

Wednesday, December 14, 2016

The public iommu and iommu_domain API interface in Linux: what is the concept of "dm: direct mapping"?

  1. iommu_domain
    /* iommu fault flags */
    #define IOMMU_FAULT_READ    0x0
    #define IOMMU_FAULT_WRITE   0x1

    typedef int (*iommu_fault_handler_t)(struct iommu_domain *,
                struct device *, unsigned long, int, void *);

    struct iommu_domain_geometry {
        dma_addr_t aperture_start; /* First address that can be mapped    */
        dma_addr_t aperture_end;   /* Last address that can be mapped     */
        bool force_aperture;       /* DMA only allowed in mappable range? */
    };

    /* Domain feature flags */
    #define __IOMMU_DOMAIN_PAGING   (1U << 0)  /* Support for iommu_map/unmap */
    #define __IOMMU_DOMAIN_DMA_API  (1U << 1)  /* Domain for use in DMA-API
                              implementation              */
    #define __IOMMU_DOMAIN_PT   (1U << 2)  /* Domain is identity mapped   */

    /*
     * This are the possible domain-types
     *
     *  IOMMU_DOMAIN_BLOCKED    - All DMA is blocked, can be used to isolate
     *                devices
     *  IOMMU_DOMAIN_IDENTITY   - DMA addresses are system physical addresses
     *  IOMMU_DOMAIN_UNMANAGED  - DMA mappings managed by IOMMU-API user, used
     *                for VMs
     *  IOMMU_DOMAIN_DMA    - Internally used for DMA-API implementations.
     *                This flag allows IOMMU drivers to implement
     *                certain optimizations for these domains
     */
    #define IOMMU_DOMAIN_BLOCKED    (0U)
    #define IOMMU_DOMAIN_IDENTITY   (__IOMMU_DOMAIN_PT)
    #define IOMMU_DOMAIN_UNMANAGED  (__IOMMU_DOMAIN_PAGING)
    #define IOMMU_DOMAIN_DMA    (__IOMMU_DOMAIN_PAGING |    \
                     __IOMMU_DOMAIN_DMA_API)

    struct iommu_domain {
        unsigned type;
        const struct iommu_ops *ops;
        iommu_fault_handler_t handler;
        void *handler_token;
        struct iommu_domain_geometry geometry;
        void *iova_cookie;
    };

                                                                                                    
     where IOMMU_DOMAIN_DMA has been integrated by the following:

    IOMMU_DOMAIN_DMA

    Defined as a preprocessor macro in: Referenced (in 8 files total) in:
  2. iommu operations:

/*
 * Following constraints are specifc to FSL_PAMUV1:
 *  -aperture must be power of 2, and naturally aligned
 *  -number of windows must be power of 2, and address space size
 *   of each window is determined by aperture size / # of windows
 *  -the actual size of the mapped region of a window must be power
 *   of 2 starting with 4KB and physical address must be naturally
 *   aligned.
 * DOMAIN_ATTR_FSL_PAMUV1 corresponds to the above mentioned contraints.
 * The caller can invoke iommu_domain_get_attr to check if the underlying
 * iommu implementation supports these constraints.
 */

enum iommu_attr {
    DOMAIN_ATTR_GEOMETRY,
    DOMAIN_ATTR_PAGING,
    DOMAIN_ATTR_WINDOWS,
    DOMAIN_ATTR_FSL_PAMU_STASH,
    DOMAIN_ATTR_FSL_PAMU_ENABLE,
    DOMAIN_ATTR_FSL_PAMUV1,
    DOMAIN_ATTR_NESTING,    /* two stages of translation */
    DOMAIN_ATTR_MAX,
};


/**
 * struct iommu_dm_region - descriptor for a direct mapped memory region
 * @list: Linked list pointers
 * @start: System physical start address of the region
 * @length: Length of the region in bytes
 * @prot: IOMMU Protection flags (READ/WRITE/...)
 */
struct iommu_dm_region {
    struct list_head    list;
    phys_addr_t        start;
    size_t            length;
    int            prot;
};

/**
 * struct iommu_ops - iommu ops and capabilities
 * @capable: check capability
 * @domain_alloc: allocate iommu domain
 * @domain_free: free iommu domain
 * @attach_dev: attach device to an iommu domain
 * @detach_dev: detach device from an iommu domain
 * @map: map a physically contiguous memory region to an iommu domain
 * @unmap: unmap a physically contiguous memory region from an iommu domain
 * @map_sg: map a scatter-gather list of physically contiguous memory chunks
 * to an iommu domain
 * @iova_to_phys: translate iova to physical address
 * @add_device: add device to iommu grouping
 * @remove_device: remove device from iommu grouping
 * @device_group: find iommu group for a particular device
 * @domain_get_attr: Query domain attributes
 * @domain_set_attr: Change domain attributes
 * @get_dm_regions: Request list of direct mapping requirements for a device
 * @put_dm_regions: Free list of direct mapping requirements for a device
 * @domain_window_enable: Configure and enable a particular window for a domain
 * @domain_window_disable: Disable a particular window for a domain
 * @domain_set_windows: Set the number of windows for a domain
 * @domain_get_windows: Return the number of windows for a domain
 * @of_xlate: add OF master IDs to iommu grouping
 * @pgsize_bitmap: bitmap of supported page sizes
 * @priv: per-instance data private to the iommu driver
 */

struct iommu_ops {
    bool (*capable)(enum iommu_cap);

    /* Domain allocation and freeing by the iommu driver */
    struct iommu_domain *(*domain_alloc)(unsigned iommu_domain_type);
    void (*domain_free)(struct iommu_domain *);

    int (*attach_dev)(struct iommu_domain *domain, struct device *dev);
    void (*detach_dev)(struct iommu_domain *domain, struct device *dev);
    int (*map)(struct iommu_domain *domain, unsigned long iova,
           phys_addr_t paddr, size_t size, int prot);
    size_t (*unmap)(struct iommu_domain *domain, unsigned long iova,
             size_t size);
    size_t (*map_sg)(struct iommu_domain *domain, unsigned long iova,
             struct scatterlist *sg, unsigned int nents, int prot);
    phys_addr_t (*iova_to_phys)(struct iommu_domain *domain, dma_addr_t iova);
    int (*add_device)(struct device *dev);
    void (*remove_device)(struct device *dev);
    struct iommu_group *(*device_group)(struct device *dev);
    int (*domain_get_attr)(struct iommu_domain *domain,
                   enum iommu_attr attr, void *data);
    int (*domain_set_attr)(struct iommu_domain *domain,
                   enum iommu_attr attr, void *data);

    /* Request/Free a list of direct mapping requirements for a device */
    void (*get_dm_regions)(struct device *dev, struct list_head *list);
    void (*put_dm_regions)(struct device *dev, struct list_head *list);

    /* Window handling functions */
    int (*domain_window_enable)(struct iommu_domain *domain, u32 wnd_nr,
                    phys_addr_t paddr, u64 size, int prot);
    void (*domain_window_disable)(struct iommu_domain *domain, u32 wnd_nr);
    /* Set the number of windows per domain */
    int (*domain_set_windows)(struct iommu_domain *domain, u32 w_count);
    /* Get the number of windows per domain */
    u32 (*domain_get_windows)(struct iommu_domain *domain);

#ifdef CONFIG_OF_IOMMU
    int (*of_xlate)(struct device *dev, struct of_phandle_args *args);
#endif

    unsigned long pgsize_bitmap;
    void *priv;
};

Gen TEE Driver SHM suballocator APIs

from linux/tee_drv.h (in optee branch until Gen Tee Driver is in upstream):

  1. Jens has done a good job to encapsulate "tee_shm" class.
  2. The public header is the linux/tee_drv.h, but the private interface is in drivers/tee/tee_private.h:
    /**
     * struct tee_shm - shared memory object
     * @teedev: device used to allocate the object
     * @ctx:    context using the object, if NULL the context is gone
     * @link    link element to traverse list of shm objects in a ctx
     * @paddr:  physical address of the shared memory
     * @kaddr:  virtual address of the shared memory
     * @size:   size of shared memory
     * @dmabuf: dmabuf used to for exporting to user space
     * @flags:  defined by TEE_SHM_* in tee_drv.h
     * @id:     unique id of a shared memory object on this device
     */

    struct tee_shm {
        struct tee_device *teedev;
        struct tee_context *ctx;
        struct list_head link;
        phys_addr_t paddr;
        void *kaddr;
        size_t size;
        struct dma_buf *dmabuf;
        u32 flags;
        int id;
    };
  3. Public API is defined as follows:
 /**
 * struct tee_shm_pool_mem_info - holds information needed to create a shared
 * memory pool
 * @vaddr:      Virtual address of start of pool
 * @paddr:      Physical address of start of pool
 * @size:       Size in bytes of the pool
 */

struct tee_shm_pool_mem_info {
        unsigned long vaddr;
        phys_addr_t paddr;
        size_t size;
};

/**
 * tee_shm_pool_alloc_res_mem() - Create a shared memory pool from reserved
 * memory range
 * @dev:         Device allocating the pool
 * @priv_info:   Information for driver private shared memory pool
 * @dmabuf_info: Information for dma-buf shared memory pool
 *
 * Start and end of pools will must be page aligned.
 *
 * Allocation with the flag TEE_SHM_DMA_BUF set will use the range supplied
 * in @dmabuf, others will use the range provided by @priv.
 *
 * @returns pointer to a 'struct tee_shm_pool' or an ERR_PTR on failure.
 */

struct tee_shm_pool *
tee_shm_pool_alloc_res_mem(struct device *dev,
                           struct tee_shm_pool_mem_info *priv_info,
                           struct tee_shm_pool_mem_info *dmabuf_info);


 /**
 * tee_shm_pool_free() - Free a shared memory pool
 * @pool:       The shared memory pool to free
 *
 * There must be no remaining shared memory allocated from this pool when
 * this function is called.
 */

void tee_shm_pool_free(struct tee_shm_pool *pool);
 /**
 * tee_shm_alloc() - Allocate shared memory
 * @ctx:        Context that allocates the shared memory
 * @size:       Requested size of shared memory
 * @flags:      Flags setting properties for the requested shared memory.
 *
 * Memory allocated as global shared memory is automatically freed when the
 * TEE file pointer is closed. The @flags field uses the bits defined by
 * TEE_SHM_* above. TEE_SHM_MAPPED must currently always be set. If
 * TEE_SHM_DMA_BUF global shared memory will be allocated and associated
 * with a dma-buf handle, else driver private memory.
 *
 * @returns a pointer to 'struct tee_shm'
 */

struct tee_shm *
      tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags);

/**
 * tee_shm_free() - Free shared memory
 * @shm:        Handle to shared memory to free
 */

void tee_shm_free(struct tee_shm *shm);

/**
 * tee_shm_put() - Decrease reference count on a shared memory handle
 * @shm:        Shared memory handle
 */

void tee_shm_put(struct tee_shm *shm);

/**
 * tee_shm_va2pa() - Get physical address of a virtual address
 * @shm:        Shared memory handle
 * @va:         Virtual address to tranlsate
 * @pa:         Returned physical address
 * @returns 0 on success and < 0 on failure
 */

int tee_shm_va2pa(struct tee_shm *shm, void *va, phys_addr_t *pa);

/**
 * tee_shm_pa2va() - Get virtual address of a physical address
 * @shm:        Shared memory handle
 * @pa:         Physical address to tranlsate
 * @va:         Returned virtual address
 * @returns 0 on success and < 0 on failure
 */

int tee_shm_pa2va(struct tee_shm *shm, phys_addr_t pa, void **va);

/**
 * tee_shm_get_va() - Get virtual address of a shared memory plus an offset
 * @shm:        Shared memory handle
 * @offs:       Offset from start of this shared memory
 * @returns virtual address of the shared memory + offs if offs is within
 *      the bounds of this shared memory, else an ERR_PTR
 */

void *tee_shm_get_va(struct tee_shm *shm, size_t offs);

/**
 * tee_shm_get_pa() - Get physical address of a shared memory plus an offset
 * @shm:        Shared memory handle
 * @offs:       Offset from start of this shared memory
 * @pa:         Physical address to return
 * @returns 0 if offs is within the bounds of this shared memory, else an
 *      error code.
 */

int tee_shm_get_pa(struct tee_shm *shm, size_t offs, phys_addr_t *pa);

/**
 * tee_shm_get_id() - Get id of a shared memory object
 * @shm:        Shared memory handle
 * @returns id
 */

int tee_shm_get_id(struct tee_shm *shm);

/**
 * tee_shm_get_from_id() - Find shared memory object and increase referece count
 * @ctx:        Context owning the shared memory
 * @id:         Id of shared memory object
 * @returns a pointer to 'struct tee_shm' on success or an ERR_PTR on failure
 */

struct tee_shm *tee_shm_get_from_id(struct tee_context *ctx, int id);

Bit Fields are Evil in Linux Kernel ... except linux/tcp.h has them!

but here is an example:

struct tcp_options_received {
/*  PAWS/RTTM data  */
    long    ts_recent_stamp;/* Time we stored ts_recent (for aging) */
    u32 ts_recent;  /* Time stamp to echo next      */
    u32 rcv_tsval;  /* Time stamp value                 */
    u32 rcv_tsecr;  /* Time stamp echo reply            */
    u16     saw_tstamp : 1, /* Saw TIMESTAMP on last packet     */
        tstamp_ok : 1,  /* TIMESTAMP seen on SYN packet     */
        dsack : 1,  /* D-SACK is scheduled          */
        wscale_ok : 1,  /* Wscale seen on SYN packet        */
        sack_ok : 4,    /* SACK seen on SYN packet      */
        snd_wscale : 4, /* Window scaling received from sender  */
        rcv_wscale : 4; /* Window scaling to send to receiver   */
    u8  num_sacks;  /* Number of SACK blocks        */
    u16 user_mss;   /* mss requested by user in ioctl   */
    u16 mss_clamp;  /* Maximal mss, negotiated at connection setup */
};