Android P 圖像顯示系統(二)GraphicBuffer和Gralloc分析

GraphicBuffer和Gralloc分析

BufferQueue中的Buffer對象,我們用的都是GraphicBuffer,那麼GraphicBuffer是怎麼來的呢?接下里我們具體來看這裏的流程。

Surface是Andorid窗口的描述,是ANativeWindow的實現;同樣GraphicBuffer是Android中圖形Buffer的描述,是ANativeWindowBuffer的實現。而一個窗口,可以有幾個Buffer。

GraphicBuffer定義

* frameworks/native/include/ui/GraphicBuffer.h

class GraphicBuffer
    : public ANativeObjectBase<ANativeWindowBuffer, GraphicBuffer, RefBase>,
      public Flattenable<GraphicBuffer>
{
    friend class Flattenable<GraphicBuffer>;
public:

其中ANativeObjectBase是一個模板類,定義如下:

* frameworks/native/include/ui/ANativeObjectBase.h

template <typename NATIVE_TYPE, typename TYPE, typename REF,
        typename NATIVE_BASE = android_native_base_t>
class ANativeObjectBase : public NATIVE_TYPE, public REF
{
public:
    // Disambiguate between the incStrong in REF and NATIVE_TYPE
    void incStrong(const void* id) const {
        REF::incStrong(id);
    }
    void decStrong(const void* id) const {
        REF::decStrong(id);
    }

這樣ANativeObjectBase繼承ANativeWindowBuffer和RefBase,GraphicBuffer繼承ANativeObjectBase和Flattenable。

這樣做的目的:

  • RefBase使GraphicBuffer支持引用計數控制
  • Flattenable使GraphicBuffer支持序列化。
    其中的關鍵類 ANativeWindowBuffer,它是一個結構體,是對Native Buffer的一個描述,其定義如下:
* frameworks/native/libs/nativebase/include/nativebase/nativebase.h

typedef struct ANativeWindowBuffer
{
#ifdef __cplusplus
    // 構造函數,decStrong和incStrong的實現;得初始化common
#endif

    struct android_native_base_t common;

    int width;
    int height;
    int stride;
    int format;
    int usage_deprecated;
    uintptr_t layerCount;

    void* reserved[1];

    const native_handle_t* handle;
    uint64_t usage;

    void* reserved_proc[8 - (sizeof(uint64_t) / sizeof(void*))];
} ANativeWindowBuffer_t;

typedef struct ANativeWindowBuffer ANativeWindowBuffer;

// Old typedef for backwards compatibility.
typedef ANativeWindowBuffer_t android_native_buffer_t;

ANativeWindowBuffer中,很多屬性前面我們介紹Surface時,已經介紹過了。這裏重點看看這個native_handle_t。

* system/core/libcutils/include/cutils/native_handle.h

typedef struct native_handle
{
    int version;        /* sizeof(native_handle_t) */
    int numFds;         /* number of file-descriptors at &data[0] */
    int numInts;        /* number of ints at &data[numFds] */
    ... ...
    int data[0];        /* numFds + numInts ints */
    ... ...
} native_handle_t;

typedef const native_handle_t* buffer_handle_t;

native_handle_t也就是具體Buffer的句柄,根據native_handle_t就能找到護體的Buffer。這裏是用文件描述符進行描述的。

GraphicBuffer,很多屬性都是繼承於父類的,GraphicBuffer自己的屬性比較少

* frameworks/native/include/ui/GraphicBuffer.h

    uint8_t mOwner;

    ... ...

    GraphicBufferMapper& mBufferMapper;
    ssize_t mInitCheck;

    // numbers of fds/ints in native_handle_t to flatten
    uint32_t mTransportNumFds;
    uint32_t mTransportNumInts;

    uint64_t mId;

    // Stores the generation number of this buffer. If this number does not
    // match the BufferQueue's internal generation number (set through
    // IGBP::setGenerationNumber), attempts to attach the buffer will fail.
    uint32_t mGenerationNumber;
};
  • mOwner
    表示該GraphicBuffer持有的只是handle,還是持有具體的數據
    enum {
        ownNone   = 0,
        ownHandle = 1,
        ownData   = 2,
    };

mOwner不一樣,釋放時,流程不一樣:

void GraphicBuffer::free_handle()
{
    if (mOwner == ownHandle) {
        mBufferMapper.freeBuffer(handle);
    } else if (mOwner == ownData) {
        GraphicBufferAllocator& allocator(GraphicBufferAllocator::get());
        allocator.free(handle);
    }
    handle = NULL;
}
  • GraphicBufferMapper
    GraphicBuffer實現Flattenable,可以將GraphicBuffer進行打包,在Binder中傳遞,但是傳遞只是Buffer的描述屬性,並不真正去拷貝Buffer的內容。怎麼實現的共享的,關鍵還是這裏的handle。GraphicBufferMapper會根據handle去在不同的進程中進map,map到同一塊物理內存。這裏先埋個伏筆,後續我們會講到。

  • mId
    GraphicBuffer的ID,這個ID在不同進程中都是一樣的

  • mGenerationNumber
    可以理解問題這個buffer被用多少次了。如果這個值和BufferQueue中的mGenerationNumber不一直,那麼是不能attach的。

餘下,GraphicBuffer的相關函數我們接下來具體來看~

分配一塊Buffer

Producer dequeueBuffer的時候,並不是 每一次都會去分配一塊Buffer。還記得什麼時候回去分配Buffer嗎?沒錯,設置了標識BUFFER_NEEDS_REALLOCATION時。

    if (returnFlags & BUFFER_NEEDS_REALLOCATION) {
        BQ_LOGV("dequeueBuffer: allocating a new buffer for slot %d", *outSlot);
        sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(
                width, height, format, BQ_LAYER_COUNT, usage,
                {mConsumerName.string(), mConsumerName.size()});

此時分配的Buffer,參數比較齊全,對應的構造函數爲:

* frameworks/native/libs/ui/GraphicBuffer.cpp

GraphicBuffer::GraphicBuffer(uint32_t inWidth, uint32_t inHeight,
        PixelFormat inFormat, uint32_t inLayerCount, uint64_t usage, std::string requestorName)
    : GraphicBuffer()
{
    mInitCheck = initWithSize(inWidth, inHeight, inFormat, inLayerCount,
            usage, std::move(requestorName));
}

在默認構造函數中,主要是做變量是初始化:

GraphicBuffer::GraphicBuffer()
    : BASE(), mOwner(ownData), mBufferMapper(GraphicBufferMapper::get()),
      mInitCheck(NO_ERROR), mId(getUniqueId()), mGenerationNumber(0)
{
    width  =
    height =
    stride =
    format =
    usage_deprecated = 0;
    usage  = 0;
    layerCount = 0;
    handle = NULL;
}

mOwner默認是ownData。GraphicBufferMapper是一個單例類,mBufferMapper在每個進程中只有一個實際對象。inLayerCount爲1,在BufferQueueProducer中是一個常量。

static constexpr uint32_t BQ_LAYER_COUNT = 1;
status_t GraphicBuffer::initWithSize(uint32_t inWidth, uint32_t inHeight,
        PixelFormat inFormat, uint32_t inLayerCount, uint64_t inUsage,
        std::string requestorName)
{
    GraphicBufferAllocator& allocator = GraphicBufferAllocator::get();
    uint32_t outStride = 0;
    status_t err = allocator.allocate(inWidth, inHeight, inFormat, inLayerCount,
            inUsage, &handle, &outStride, mId,
            std::move(requestorName));
    if (err == NO_ERROR) {
        mBufferMapper.getTransportSize(handle, &mTransportNumFds, &mTransportNumInts);

        width = static_cast<int>(inWidth);
        height = static_cast<int>(inHeight);
        format = inFormat;
        layerCount = inLayerCount;
        usage = inUsage;
        usage_deprecated = int(usage);
        stride = static_cast<int>(outStride);
    }
    return err;
}
  • GraphicBufferAllocator
    Buffer管理中,另外一個單例類,GraphicBufferAllocator把Buffer分配出來你,GraphicBufferMapper可以將其map到自己的進程。
    需要主要的是,BufferQueueProducer是跑在SurfaceFlinger進程中的,也就是說,絕大部分的應用,使用的Buffer,都是SurfaceFlinger進程分配出來的,所以,如果SurfaceFlinger出現內存泄露,FD泄露等問題,很有可能都是應用沒有釋放,SurfaceFlinger不會主動釋放,它只響應應用的請求。SurfaceFlinger是背鍋俠!

GraphicBufferAllocator定義如下:

* frameworks/native/include/ui/GraphicBufferAllocator.h

class GraphicBufferAllocator : public Singleton<GraphicBufferAllocator>
{
public:
    static inline GraphicBufferAllocator& get() { return getInstance(); }

    status_t allocate(uint32_t w, uint32_t h, PixelFormat format,
            uint32_t layerCount, uint64_t usage,
            buffer_handle_t* handle, uint32_t* stride, uint64_t graphicBufferId,
            std::string requestorName);

    status_t free(buffer_handle_t handle);

    void dump(String8& res) const;
    static void dumpToSystemLog();

private:
    struct alloc_rec_t {
        uint32_t width;
        uint32_t height;
        uint32_t stride;
        PixelFormat format;
        uint32_t layerCount;
        uint64_t usage;
        size_t size;
        std::string requestorName;
    };

    static Mutex sLock;
    static KeyedVector<buffer_handle_t, alloc_rec_t> sAllocList;

    friend class Singleton<GraphicBufferAllocator>;
    GraphicBufferAllocator();
    ~GraphicBufferAllocator();

    GraphicBufferMapper& mMapper;
    const std::unique_ptr<const Gralloc2::Allocator> mAllocator;
};
  • 兩個主要的方法,一個allocate用來分配Buffer,一個free用來釋放Buffe。
  • sAllocList,申請的Buffer,都保存下來,放到sAllocList中,並不是保存具體的Buffer,而是Buffer的描述alloc_rec_t。
  • mAllocator,Gralloc登場,gralloc採用版本化管理,用的是Gralloc2。

GraphicBufferAllocator的allocate函數如下:

status_t GraphicBufferAllocator::allocate(uint32_t width, uint32_t height,
        PixelFormat format, uint32_t layerCount, uint64_t usage,
        buffer_handle_t* handle, uint32_t* stride,
        uint64_t /*graphicBufferId*/, std::string requestorName)
{
    ATRACE_CALL();

    // make sure to not allocate a N x 0 or 0 x N buffer, since this is
    // allowed from an API stand-point allocate a 1x1 buffer instead.
    if (!width || !height)
        width = height = 1;

    // Ensure that layerCount is valid.
    if (layerCount < 1)
        layerCount = 1;

    Gralloc2::IMapper::BufferDescriptorInfo info = {};
    info.width = width;
    info.height = height;
    info.layerCount = layerCount;
    info.format = static_cast<Gralloc2::PixelFormat>(format);
    info.usage = usage;

    Gralloc2::Error error = mAllocator->allocate(info, stride, handle);
    if (error == Gralloc2::Error::NONE) {
        Mutex::Autolock _l(sLock);
        KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList);
        uint32_t bpp = bytesPerPixel(format);
        alloc_rec_t rec;
        rec.width = width;
        rec.height = height;
        rec.stride = *stride;
        rec.format = format;
        rec.layerCount = layerCount;
        rec.usage = usage;
        rec.size = static_cast<size_t>(height * (*stride) * bpp);
        rec.requestorName = std::move(requestorName);
        list.add(*handle, rec);

        return NO_ERROR;
    } else {
        ALOGE("Failed to allocate (%u x %u) layerCount %u format %d "
                "usage %" PRIx64 ": %d",
                width, height, layerCount, format, usage,
                error);
        return NO_MEMORY;
    }
}

在看allocate函數之前,我們先來看一下GraphicBuffer相關的類:
GraphicBuffer關係類圖

GraphicBuffer的左膀右臂,GraphicBufferAllocator和GraphicBufferMapper!從Android 8.0開始,Android 操作系統框架在架構方面的一項重大改變,提出了treble 項目。Vendor的實現和Androd的實現分開,Android和HAL,採用HwBinder進行通信,減少Android對HAL的直接依賴。這裏的Allocator和Mapper,就是對HAL結合的包裝;IAllocator,IMapper的HAL的接口。V2_1::IMapper是一個對Gralloc HAL的2.1版本。

回到allocate函數~

BufferDescriptorInfo,對Buffer的描述,在HAL層也通用。根據需要,生成BufferDescriptorInfo,再通過Gralloc2的Allocator進行allocate。allocate出來的Buffer 句柄,保存在sAllocList中。

Gralloc2 Allocator的allocate函數提供了很多形態,可以滿足我們不同的要求:

* frameworks/native/libs/ui/include/ui/Gralloc2.h

    /*
     * The returned buffers are already imported and must not be imported
     * again.  outBufferHandles must point to a space that can contain at
     * least "count" buffer_handle_t.
     */
    Error allocate(BufferDescriptor descriptor, uint32_t count,
            uint32_t* outStride, buffer_handle_t* outBufferHandles) const;

    Error allocate(BufferDescriptor descriptor,
            uint32_t* outStride, buffer_handle_t* outBufferHandle) const
    {
        return allocate(descriptor, 1, outStride, outBufferHandle);
    }

    Error allocate(const IMapper::BufferDescriptorInfo& descriptorInfo, uint32_t count,
            uint32_t* outStride, buffer_handle_t* outBufferHandles) const
    {
        BufferDescriptor descriptor;
        Error error = mMapper.createDescriptor(descriptorInfo, &descriptor);
        if (error == Error::NONE) {
            error = allocate(descriptor, count, outStride, outBufferHandles);
        }
        return error;
    }

    Error allocate(const IMapper::BufferDescriptorInfo& descriptorInfo,
            uint32_t* outStride, buffer_handle_t* outBufferHandle) const
    {
        return allocate(descriptorInfo, 1, outStride, outBufferHandle);
    }

我們傳的參數是BufferDescriptorInfo,首先要根據BufferDescriptorInfo,生成一個BufferDescriptor,這個是mapper的HAL層實現的,因爲這個BufferDescriptor最後也是要給到HAL層,HAL層根據BufferDescriptor去生成相應描述的Buffer。

最後,allocate的通用實現如下:

* frameworks/native/libs/ui/Gralloc2.cpp

Error Allocator::allocate(BufferDescriptor descriptor, uint32_t count,
        uint32_t* outStride, buffer_handle_t* outBufferHandles) const
{
    Error error;
    auto ret = mAllocator->allocate(descriptor, count,
            [&](const auto& tmpError, const auto& tmpStride,
                const auto& tmpBuffers) {
                error = tmpError;
                if (tmpError != Error::NONE) {
                    return;
                }

                // import buffers
                for (uint32_t i = 0; i < count; i++) {
                    error = mMapper.importBuffer(tmpBuffers[i],
                            &outBufferHandles[i]);
                    if (error != Error::NONE) {
                        for (uint32_t j = 0; j < i; j++) {
                            mMapper.freeBuffer(outBufferHandles[j]);
                            outBufferHandles[j] = nullptr;
                        }
                        return;
                    }
                }

                *outStride = tmpStride;
            });

    // make sure the kernel driver sees BC_FREE_BUFFER and closes the fds now
    hardware::IPCThreadState::self()->flushCommands();

    return (ret.isOk()) ? error : kTransactionError;
}

count,表示需要分配的Buffer個數,也就是說我們一次可以分配多個Buffer。

allocator分配完成後,再通過importBuffer函數,import到我們的handle中outBufferHandle。

* frameworks/native/libs/ui/Gralloc2.cpp

Error Mapper::importBuffer(const hardware::hidl_handle& rawHandle,
        buffer_handle_t* outBufferHandle) const
{
    Error error;
    auto ret = mMapper->importBuffer(rawHandle,
            [&](const auto& tmpError, const auto& tmpBuffer)
            {
                error = tmpError;
                if (error != Error::NONE) {
                    return;
                }

                *outBufferHandle = static_cast<buffer_handle_t>(tmpBuffer);
            });

    return (ret.isOk()) ? error : kTransactionError;
}

Gralloc1.0 接口介紹

Graphic相關的HAL的接口都在定義在hardware/interfaces/graphics/。allocator和mapper也是分開的。

IAllocator接口

* hardware/interfaces/graphics/allocator/2.0/IAllocator.hal

package android.hardware.graphics.allocator@2.0;

import android.hardware.graphics.mapper@2.0;

interface IAllocator {
    @entry
    @exit
    @callflow(next="*")
    dumpDebugInfo() generates (string debugInfo);

    @entry
    @exit
    @callflow(next="*")
    allocate(BufferDescriptor descriptor, uint32_t count)
        generates (Error error,
                   uint32_t stride,
                   vec<handle> buffers);
};

IAllocator主要兩個接口:

  • allocate
    根據Buffer Descriptor描述的屬性,分配對應的Buffer;count,分配的個數;返回值,stride,Buffer 步長,何爲步長?我們知道Buffer都有一個寬度,但是Buffer的內存中分配的時候,都是採用對齊後的大小。多少位對齊,每個硬件平臺不一樣。比如,我們在一個32對齊的平臺上,需要申請一塊60x60大小的Buffer。因爲要做對齊,所以實際分配的大小爲64x60。那麼對於這塊Buffer,stride就是64。這是因爲我們讀Buffer的時候,基本都是一行一行的讀的,我們要讀i行j列,也就是base + i*stride + j的位置。在有的場合下,高也會要求做對齊,那麼60x60的Buffer,實際分配的大小是64x64的。buffers這是分配的Buffer的handle了。

  • dumpDebugInfo
    dump函數,主要用來debug用

所以,IAllocator的接口主要就一個allocate。

IAllocator又是怎麼跟HAL模塊連接上的呢?其實一個hidl的接口,在編譯時會生成很多東西~

hidl_interface {
    name: "android.hardware.graphics.allocator@2.0",
    root: "android.hardware",
    vndk: {
        enabled: true,
    },
    srcs: [
        "IAllocator.hal",
    ],
    interfaces: [
        "android.hardware.graphics.common@1.0",
        "android.hardware.graphics.mapper@2.0",
        "android.hidl.base@1.0",
    ],
    gen_java: false,
}

IAllocator的目錄如下:

out/soong/.intermediates/hardware/interfaces/graphics/allocator/2.0

./android.hardware.graphics.allocator@2.0_genc++_headers/gen/android/hardware/graphics/allocator/2.0/IAllocator.h
./android.hardware.graphics.allocator@2.0_genc++/gen/android/hardware/graphics/allocator/2.0/AllocatorAll.cpp

Gralloc2的構造函數中,將首先建立和HAL層的HwBinder服務連接

* frameworks/native/libs/ui/Gralloc2.cpp

Allocator::Allocator(const Mapper& mapper)
    : mMapper(mapper)
{
    mAllocator = IAllocator::getService();
    if (mAllocator == nullptr) {
        LOG_ALWAYS_FATAL("gralloc-alloc is missing");
    }
}

IAllocator的getService函數,是.hal文件中是沒有定義的,但是編譯的中間結果中會生成。

* out/soong/.intermediates/hardware/interfaces/graphics/allocator/2.0/android.hardware.graphics.allocator@2.0_genc++_headers/gen/android/hardware/graphics/allocator/2.0/IAllocator.h

static ::android::sp<IAllocator> getService(const std::string &serviceName="default", bool getStub=false);

這裏用的是缺省構造函數,這裏其實和Binder是類似的:

* out/soong/.intermediates/hardware/interfaces/graphics/allocator/2.0/android.hardware.graphics.allocator@2.0_genc++/gen/android/hardware/graphics/allocator/2.0/AllocatorAll.cpp

// static
::android::sp<IAllocator> IAllocator::getService(const std::string &serviceName, const bool getStub) {
    return ::android::hardware::details::getServiceInternal<BpHwAllocator>(serviceName, true, getStub);
}

註冊的函數如下:

::android::status_t IAllocator::registerAsService(const std::string &serviceName) {
    ::android::hardware::details::onRegistration("[email protected]", "IAllocator", serviceName);

    const ::android::sp<::android::hidl::manager::V1_0::IServiceManager> sm
            = ::android::hardware::defaultServiceManager();
    if (sm == nullptr) {
        return ::android::INVALID_OPERATION;
    }
    ::android::hardware::Return<bool> ret = sm->add(serviceName.c_str(), this);
    return ret.isOk() && ret ? ::android::OK : ::android::UNKNOWN_ERROR;
}

IAllocator HAL服務是誰呢?默認的實現在這裏:

hardware/interfaces/graphics/allocator/2.0/default

默認服務起來的時候,將通過defaultPassthroughServiceImplementation去註冊IAllocator的HAL服務:

#define LOG_TAG "[email protected]"

#include <android/hardware/graphics/allocator/2.0/IAllocator.h>

#include <hidl/LegacySupport.h>

using android::hardware::graphics::allocator::V2_0::IAllocator;
using android::hardware::defaultPassthroughServiceImplementation;

int main() {
    return defaultPassthroughServiceImplementation<IAllocator>(4);
}

defaultPassthroughServiceImplementation的實現在LegacySupport.h

* system/libhidl/transport/include/hidl/LegacySupport.h

template<class Interface>
__attribute__((warn_unused_result))
status_t registerPassthroughServiceImplementation(
        std::string name = "default") {
    sp<Interface> service = Interface::getService(name, true /* getStub */);

    if (service == nullptr) {
        ALOGE("Could not get passthrough implementation for %s/%s.",
            Interface::descriptor, name.c_str());
        return EXIT_FAILURE;
    }

    LOG_FATAL_IF(service->isRemote(), "Implementation of %s/%s is remote!",
            Interface::descriptor, name.c_str());

    status_t status = service->registerAsService(name);

    if (status == OK) {
        ALOGI("Registration complete for %s/%s.",
            Interface::descriptor, name.c_str());
    } else {
        ALOGE("Could not register service %s/%s (%d).",
            Interface::descriptor, name.c_str(), status);
    }

    return status;
}

template<class Interface>
__attribute__((warn_unused_result))
status_t defaultPassthroughServiceImplementation(std::string name,
                                            size_t maxThreads = 1) {
    configureRpcThreadpool(maxThreads, true);
    status_t result = registerPassthroughServiceImplementation<Interface>(name);

    if (result != OK) {
        return result;
    }

    joinRpcThreadpool();
    return UNKNOWN_ERROR;
}
template<class Interface>
__attribute__((warn_unused_result))
status_t defaultPassthroughServiceImplementation(size_t maxThreads = 1) {
    return defaultPassthroughServiceImplementation<Interface>("default", maxThreads);
}

IAllocator被註冊爲Passthrough的Service。registerAsService,看看前面的函數,這個service將會被add到IServiceManager中,這這樣,get的時候,就能獲取到了。

獲取到service的時,將會調HIDL_FETCH_***的函數,我們這裏就是HIDL_FETCH_IAllocator,中間過程都是在system/libhidl中實現的。這裏就不細跟了。

HIDL_FETCH_IAllocator的函數實現如下:

* hardware/interfaces/graphics/allocator/2.0/default/Gralloc.cpp

IAllocator* HIDL_FETCH_IAllocator(const char* /* name */) {
    const hw_module_t* module = nullptr;
    int err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module);
    if (err) {
        ALOGE("failed to get gralloc module");
        return nullptr;
    }

    uint8_t major = (module->module_api_version >> 8) & 0xff;
    switch (major) {
        case 1:
            return new Gralloc1Allocator(module);
        case 0:
            return new Gralloc0Allocator(module);
        default:
            ALOGE("unknown gralloc module major version %d", major);
            return nullptr;
    }
}

HIDL_FETCH時,將會加載對應的HAL實現了。gralloc這邊的HAL實現GRALLOC_HARDWARE_MODULE_ID。major就是gralloc的API版本。Gralloc1Allocator是對1.0版本的適配,Gralloc0Allocator是對最初版本的適配。

Gralloc1 Allocator HAL層接口
大多數Hardware的接口都定義在hardware/libhardware/include/hardware,Gralloc也不例外。

Gralloc1,HAL的描述爲gralloc1_device_t

* hardware/libhardware/include/hardware/gralloc1.h

typedef struct gralloc1_device {
    /* Must be the first member of this struct, since a pointer to this struct
     * will be generated by casting from a hw_device_t* */
    struct hw_device_t common;

    // 獲取Devices支持的能力
    void (*getCapabilities)(struct gralloc1_device* device, uint32_t* outCount,
            int32_t* /*gralloc1_capability_t*/ outCapabilities);

    // 獲取對應功能的函數指針
    gralloc1_function_pointer_t (*getFunction)(struct gralloc1_device* device,
            int32_t /*gralloc1_function_descriptor_t*/ descriptor);
} gralloc1_device_t;

Gralloc1和前面的的實現有比較大的差別,接口都通過函數指針實現,不再採用原來的方式。

下面是Gralloc0的定義:

* hardware/libhardware/include/hardware/gralloc.h

typedef struct alloc_device_t {
    struct hw_device_t common;

    int (*alloc)(struct alloc_device_t* dev,
            int w, int h, int format, int usage,
            buffer_handle_t* handle, int* stride);

    int (*free)(struct alloc_device_t* dev,
            buffer_handle_t handle);

    void (*dump)(struct alloc_device_t *dev, char *buff, int buff_len);

    void* reserved_proc[7];
} alloc_device_t;

Gralloc0中,還是採用直接的函數調用。Gralloc1中,只是getCapabilities採用直接的函數調用。

Gralloc1時,走的Gralloc1Allocator,Gralloc0時,走的Gralloc0Allocator。我們主要來看一下Gralloc1Allocator。

* hardware/interfaces/graphics/allocator/2.0/default/Gralloc1Allocator.cpp

Gralloc1Allocator::Gralloc1Allocator(const hw_module_t* module)
    : mDevice(nullptr), mCapabilities(), mDispatch() {
    int result = gralloc1_open(module, &mDevice);
    if (result) {
        LOG_ALWAYS_FATAL("failed to open gralloc1 device: %s",
                         strerror(-result));
    }

    initCapabilities();
    initDispatch();
}

gralloc1_open,打開HAL層Gralloc1的具體實現。獲取到gralloc1_device_t設備mDevice。

通過initCapabilities函數,將Gralloc1的能力都讀出來,放到capabilities中

* hardware/interfaces/graphics/allocator/2.0/default/Gralloc1Allocator.cpp

void Gralloc1Allocator::initCapabilities() {
    uint32_t count = 0;
    mDevice->getCapabilities(mDevice, &count, nullptr);

    std::vector<int32_t> capabilities(count);
    mDevice->getCapabilities(mDevice, &count, capabilities.data());
    capabilities.resize(count);

    for (auto capability : capabilities) {
        if (capability == GRALLOC1_CAPABILITY_LAYERED_BUFFERS) {
            mCapabilities.layeredBuffers = true;
            break;
        }
    }
}

mDevice的getCapabilities函數調了兩次,這個在HAL實現中經常用到,第一次,主要是獲取大小,第二次纔去獲取具體的值。

initDispatch初始化函數指針,

* hardware/interfaces/graphics/allocator/2.0/default/Gralloc1Allocator.cpp

template <typename T>
void Gralloc1Allocator::initDispatch(gralloc1_function_descriptor_t desc,
                                     T* outPfn) {
    auto pfn = mDevice->getFunction(mDevice, desc);
    if (!pfn) {
        LOG_ALWAYS_FATAL("failed to get gralloc1 function %d", desc);
    }

    *outPfn = reinterpret_cast<T>(pfn);
}

void Gralloc1Allocator::initDispatch() {
    initDispatch(GRALLOC1_FUNCTION_DUMP, &mDispatch.dump);
    initDispatch(GRALLOC1_FUNCTION_CREATE_DESCRIPTOR,
                 &mDispatch.createDescriptor);
    initDispatch(GRALLOC1_FUNCTION_DESTROY_DESCRIPTOR,
                 &mDispatch.destroyDescriptor);
    initDispatch(GRALLOC1_FUNCTION_SET_DIMENSIONS, &mDispatch.setDimensions);
    initDispatch(GRALLOC1_FUNCTION_SET_FORMAT, &mDispatch.setFormat);
    if (mCapabilities.layeredBuffers) {
        initDispatch(GRALLOC1_FUNCTION_SET_LAYER_COUNT,
                     &mDispatch.setLayerCount);
    }
    initDispatch(GRALLOC1_FUNCTION_SET_CONSUMER_USAGE,
                 &mDispatch.setConsumerUsage);
    initDispatch(GRALLOC1_FUNCTION_SET_PRODUCER_USAGE,
                 &mDispatch.setProducerUsage);
    initDispatch(GRALLOC1_FUNCTION_GET_STRIDE, &mDispatch.getStride);
    initDispatch(GRALLOC1_FUNCTION_ALLOCATE, &mDispatch.allocate);
    initDispatch(GRALLOC1_FUNCTION_RELEASE, &mDispatch.release);
}

mDevice根據gralloc1_function_descriptor_t,去HAL的實現中去獲取對應的函數指針,初始化到mDispatch中。以後我們直接調mDispatch中的函數就訪問到HAL的實現。

Gralloc1的gralloc1_function_descriptor_t包括:

* hardware/libhardware/include/hardware/gralloc1.h

typedef enum {
    GRALLOC1_FUNCTION_INVALID = 0,
    GRALLOC1_FUNCTION_DUMP = 1,
    GRALLOC1_FUNCTION_CREATE_DESCRIPTOR = 2,
    GRALLOC1_FUNCTION_DESTROY_DESCRIPTOR = 3,
    GRALLOC1_FUNCTION_SET_CONSUMER_USAGE = 4,
    GRALLOC1_FUNCTION_SET_DIMENSIONS = 5,
    GRALLOC1_FUNCTION_SET_FORMAT = 6,
    GRALLOC1_FUNCTION_SET_PRODUCER_USAGE = 7,
    GRALLOC1_FUNCTION_GET_BACKING_STORE = 8,
    GRALLOC1_FUNCTION_GET_CONSUMER_USAGE = 9,
    GRALLOC1_FUNCTION_GET_DIMENSIONS = 10,
    GRALLOC1_FUNCTION_GET_FORMAT = 11,
    GRALLOC1_FUNCTION_GET_PRODUCER_USAGE = 12,
    GRALLOC1_FUNCTION_GET_STRIDE = 13,
    GRALLOC1_FUNCTION_ALLOCATE = 14,
    GRALLOC1_FUNCTION_RETAIN = 15,
    GRALLOC1_FUNCTION_RELEASE = 16,
    GRALLOC1_FUNCTION_GET_NUM_FLEX_PLANES = 17,
    GRALLOC1_FUNCTION_LOCK = 18,
    GRALLOC1_FUNCTION_LOCK_FLEX = 19,
    GRALLOC1_FUNCTION_UNLOCK = 20,
    GRALLOC1_FUNCTION_SET_LAYER_COUNT = 21,
    GRALLOC1_FUNCTION_GET_LAYER_COUNT = 22,
    GRALLOC1_LAST_FUNCTION = 22,
} gralloc1_function_descriptor_t;

IAllocator需要實現的gralloc1_function_descriptor_t包括:

* hardware/interfaces/graphics/allocator/2.0/default/Gralloc1Allocator.h
    struct {
        GRALLOC1_PFN_DUMP dump;
        GRALLOC1_PFN_CREATE_DESCRIPTOR createDescriptor;
        GRALLOC1_PFN_DESTROY_DESCRIPTOR destroyDescriptor;
        GRALLOC1_PFN_SET_DIMENSIONS setDimensions;
        GRALLOC1_PFN_SET_FORMAT setFormat;
        GRALLOC1_PFN_SET_LAYER_COUNT setLayerCount;
        GRALLOC1_PFN_SET_CONSUMER_USAGE setConsumerUsage;
        GRALLOC1_PFN_SET_PRODUCER_USAGE setProducerUsage;
        GRALLOC1_PFN_GET_STRIDE getStride;
        GRALLOC1_PFN_ALLOCATE allocate;
        GRALLOC1_PFN_RELEASE release;
    } mDispatch;

我們去實現Gralloc1的HAL時,allocator只去要實現getCapabilities和上面mDispatch中的gralloc1_function_descriptor_t就可以了。

IMapper接口

IMapper的接口有兩個版本2.0和2.1:

ls hardware/interfaces/graphics/mapper

2.0  2.1

2.1是可選的,暫時不嚴格要求支持:

* frameworks/native/libs/ui/Gralloc2.cpp

void Mapper::preload() {
    android::hardware::preloadPassthroughService<hardware::graphics::mapper::V2_0::IMapper>();
}

Mapper::Mapper()
{
    mMapper = IMapper::getService();
    if (mMapper == nullptr) {
        LOG_ALWAYS_FATAL("gralloc-mapper is missing");
    }
    if (mMapper->isRemote()) {
        LOG_ALWAYS_FATAL("gralloc-mapper must be in passthrough mode");
    }

    // IMapper 2.1 is optional
    mMapperV2_1 = hardware::graphics::mapper::V2_1::IMapper::castFrom(mMapper);
}

IMapper也是PassThrough的模式。

IMapper2.0的接口

* hardware/interfaces/graphics/mapper/2.0/IMapper.hal

package android.hardware.graphics.mapper@2.0;

import android.hardware.graphics.common@1.0;

interface IMapper {
    struct BufferDescriptorInfo {
        uint32_t width; // 寬,橫向的像素點數

        uint32_t height; //高,縱向的像素點數

       /**
        * The number of image layers that must be in the allocated buffer.
        */
        uint32_t layerCount;

        PixelFormat format; //像素點的格式

        bitfield<BufferUsage> usage; //用處
    };

    struct Rect {
        int32_t left;
        int32_t top;
        int32_t width;
        int32_t height;
    };

    /**
     * 創建一個Buffer的描述,這個描述在分配Buffer時使用
     * 如果成功,返回值爲NONE, 參數無效或衝突返回BAD_VALUE,沒有資源返回NO_RESOURCES,參數不支持返回UNSUPPORTED
     */
    @entry
    @callflow(next="*")
    createDescriptor(BufferDescriptorInfo descriptorInfo)
          generates (Error error,
                     BufferDescriptor descriptor);

    @entry
    @callflow(next="*")
    importBuffer(handle rawHandle) generates (Error error, pointer buffer);

    @exit
    @callflow(next="*")
    freeBuffer(pointer buffer) generates (Error error);

    @callflow(next="unlock")
    lock(pointer buffer,
         bitfield<BufferUsage> cpuUsage,
         Rect accessRegion,
         handle acquireFence)
        generates (Error error,
                   pointer data);

    @callflow(next="unlock")
    lockYCbCr(pointer buffer,
              bitfield<BufferUsage> cpuUsage,
              Rect accessRegion,
              handle acquireFence)
        generates (Error error,
                   YCbCrLayout layout);

    @callflow(next="*")
    unlock(pointer buffer)
        generates (Error error,
                   handle releaseFence);
};

IMapper的接口比IAllocator多,具體一些信息我寫在代碼中。

  • createDescriptor
    創建一個BufferDescriptor,分配Buffer時,根據Descriptor分配。

  • importBuffer
    Buffer被衝其他進程或HAL克隆出來時,這個Buffer是RAW狀態的Buffer,raw handle是不能直接訪問真正的Buffer的,我們需要把它imported到imported的handle中才能訪問。創建imported handle時,需要驗證raw handle的有效性,且raw handle需要能多少import創建多個imported handle。在passthrough HALs中,從HAL接收到的handle,可能已經被import到進程中,這個時候要能區分,將其當做raw handle處理,而不是返回BAD_BUFFER。

  • freeBuffer
    釋放Buffer handle,通過importBuffer返回的handle必現通過這個接口釋放。importBuffer時申請的所有資源必須一起釋放。比如 imported handle如果通過native_handle_create創建的,那麼必須調用native_handle_close和native_handle_delete

  • lock
    將Buffer鎖住,用來做制定的處理。多線程可以同事lock,但是不能同時寫。超出accessRegion區域的Buffer不能寫,超出的區域不受保護。Buffer的地址是指針buffer,是從left-top開始的,即使accessRegion不是left-top描述。

  • lockYCbCr
    這個lock很相似,只是返回值不一樣,這裏是YCbCrLayout。除非是Codec配置爲flexible-YUV-compatible的顏色格式,要不必現是PixelFormat::YCbCr_*_888格式的。

  • unlock
    表示CPU訪問Buffer已經完成

IMapper用到的數據類型定義在types.hal中

* hardware/interfaces/graphics/mapper/2.0/types.hal

package android.hardware.graphics.mapper@2.0;

enum Error : int32_t {
    NONE            = 0, /** no error */
    BAD_DESCRIPTOR  = 1, /** invalid BufferDescriptor */
    BAD_BUFFER      = 2, /** invalid buffer handle */
    BAD_VALUE       = 3, /** invalid width, height, etc. */
    /* 4 is reserved */
    NO_RESOURCES    = 5, /** temporary failure due to resource contention */
    /* 6 is reserved */
    UNSUPPORTED     = 7, /** permanent failure */
};

/**
 * A buffer descriptor is an implementation-defined opaque data returned by
 * createDescriptor. It describes the properties of a buffer and is consumed
 * by the allocator.
 */
typedef vec<uint32_t> BufferDescriptor;

/**
 * Structure for describing YCbCr formats for consumption by applications.
 * This is used with PixelFormat::YCBCR_*_888.
 *
 * Buffer chroma subsampling is defined in the format.
 * e.g. PixelFormat::YCBCR_420_888 has subsampling 4:2:0.
 *
 * Buffers must have a 8 bit depth.
 *
 * y, cb, and cr point to the first byte of their respective planes.
 *
 * Stride describes the distance in bytes from the first value of one row of
 * the image to the first value of the next row. It includes the width of the
 * image plus padding.
 * yStride is the stride of the luma plane.
 * cStride is the stride of the chroma planes.
 *
 * chromaStep is the distance in bytes from one chroma pixel value to the
 * next. This is 2 bytes for semiplanar (because chroma values are interleaved
 * and each chroma value is one byte) and 1 for planar.
 */
struct YCbCrLayout {
    pointer y;
    pointer cb;
    pointer cr;
    uint32_t yStride;
    uint32_t cStride;
    uint32_t chromaStep;
};

IMapper2.1的接口
IMapper2.1的接口繼承IMapper2.0的接口:

* hardware/interfaces/graphics/mapper/2.1/IMapper.hal

package android.hardware.graphics.mapper@2.1;

import android.hardware.graphics.mapper@2.0::Error;
import android.hardware.graphics.mapper@2.0::IMapper;

interface IMapper extends android.hardware.graphics.mapper@2.0::IMapper {

    validateBufferSize(pointer buffer,
                       BufferDescriptorInfo descriptorInfo,
                       uint32_t stride)
            generates (Error error);

    /**
     * Get the transport size of a buffer. An imported buffer handle is a raw
     * buffer handle with the process-local runtime data appended. This
     * function, for example, allows a caller to omit the process-local
     * runtime data at the tail when serializing the imported buffer handle.
     *
     * Note that a client might or might not omit the process-local runtime
     * data when sending an imported buffer handle. The mapper must support
     * both cases on the receiving end.
     *
     * @param buffer is the buffer to get the transport size from.
     * @return error is NONE upon success. Otherwise,
     *                  BAD_BUFFER when the buffer is invalid.
     * @return numFds is the number of file descriptors needed for transport.
     * @return numInts is the number of integers needed for transport.
     */
    getTransportSize(pointer buffer)
            generates (Error error,
                       uint32_t numFds,
                       uint32_t numInts);
};
  • validateBufferSize
    驗證,Buffer能不能被制定的描述信息和步長的訪問者訪問。

  • getTransportSize
    獲取Buffer傳輸的大小。一個Imported handle是一個raw handle再加上進程本地運行的數據,所以我們可以獲取到進程本地的數據。

IMapper的HIDL_FETCH_IMapper函數實現如下:

* hardware/interfaces/graphics/mapper/2.0/default/GrallocMapper.cpp

IMapper* HIDL_FETCH_IMapper(const char* /* name */) {
    const hw_module_t* module = nullptr;
    int err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module);
    if (err) {
        ALOGE("failed to get gralloc module");
        return nullptr;
    }

    uint8_t major = (module->module_api_version >> 8) & 0xff;
    switch (major) {
        case 1:
            return new Gralloc1Mapper(module);
        case 0:
            return new Gralloc0Mapper(module);
        default:
            ALOGE("unknown gralloc module major version %d", major);
            return nullptr;
    }
}

IMapper的HAL模塊ID爲GRALLOC_HARDWARE_MODULE_ID,和IAllocator類似,這裏也對應兩個mapper。Gralloc1Mapper和Gralloc0Mapper

前面IAllocator的時候,有個main函數,這裏爲什麼沒有?那麼,IMapper是怎麼找到的呢?

夏雨荷已死,還記得Gralloc2中的preload嗎?

* frameworks/native/libs/ui/Gralloc2.cpp

void Mapper::preload() {
    android::hardware::preloadPassthroughService<hardware::graphics::mapper::V2_0::IMapper>();
}

Gralloc1 Mapper HAL層接口
Mapper的接口也定義在gralloc1.h中,Gralloc1對應的Mapper爲Gralloc1Mapper

* hardware/interfaces/graphics/allocator/2.0/default/Gralloc1On0Adapter.cpp

Gralloc1Mapper::Gralloc1Mapper(const hw_module_t* module)
    : mDevice(nullptr), mDispatch() {
    int result = gralloc1_open(module, &mDevice);
    if (result) {
        LOG_ALWAYS_FATAL("failed to open gralloc1 device: %s",
                         strerror(-result));
    }

    initCapabilities();
    initDispatch();
}

mapper的initCapabilities函數,和allocator的initCapabilities函數類似,都是通過gralloc1_device_t的getCapabilities函數去獲取。只是這裏但是做了一個封裝了mCapabilities。

void Gralloc1Mapper::initCapabilities() {
    mCapabilities.highUsageBits = true;
    mCapabilities.layeredBuffers = false;
    mCapabilities.unregisterImplyDelete = false;

    uint32_t count = 0;
    mDevice->getCapabilities(mDevice, &count, nullptr);

    std::vector<int32_t> capabilities(count);
    mDevice->getCapabilities(mDevice, &count, capabilities.data());
    capabilities.resize(count);

    for (auto capability : capabilities) {
        switch (capability) {
            case GRALLOC1_CAPABILITY_LAYERED_BUFFERS:
                mCapabilities.layeredBuffers = true;
                break;
            case GRALLOC1_CAPABILITY_RELEASE_IMPLY_DELETE:
                mCapabilities.unregisterImplyDelete = true;
                break;
        }
    }
}

只是這裏但是做了一個封裝了mCapabilities。

    struct {
        bool highUsageBits;
        bool layeredBuffers;
        bool unregisterImplyDelete;
    } mCapabilities = {};

Gralloc1,中Mapper 對應的gralloc1_function_descriptor_t如下:

template <typename T>
void Gralloc1Mapper::initDispatch(gralloc1_function_descriptor_t desc,
                                  T* outPfn) {
    auto pfn = mDevice->getFunction(mDevice, desc);
    if (!pfn) {
        LOG_ALWAYS_FATAL("failed to get gralloc1 function %d", desc);
    }

    *outPfn = reinterpret_cast<T>(pfn);
}

void Gralloc1Mapper::initDispatch() {
    initDispatch(GRALLOC1_FUNCTION_RETAIN, &mDispatch.retain);
    initDispatch(GRALLOC1_FUNCTION_RELEASE, &mDispatch.release);
    initDispatch(GRALLOC1_FUNCTION_GET_NUM_FLEX_PLANES,
                 &mDispatch.getNumFlexPlanes);
    initDispatch(GRALLOC1_FUNCTION_LOCK, &mDispatch.lock);
    initDispatch(GRALLOC1_FUNCTION_LOCK_FLEX, &mDispatch.lockFlex);
    initDispatch(GRALLOC1_FUNCTION_UNLOCK, &mDispatch.unlock);
}

mapper的HAL實現,只要實現上述的gralloc1_function_descriptor_t。

我們來看一下Gralloc1的相關類似關係~
gralloc1關係類圖

最終,Gralloc1都是在gralloc1_device_t中去實現的。當然,如果HAL沒有實現對應地Gralloc1,而是Gralloc0。Android這邊也是提供適配的。對應的代碼在:

hardware/interfaces/graphics/allocator/2.0/default/gralloc1-adapter.cpp

下面,我們找一個具體平臺的實現來看看,gralloc的HAL層是怎麼實現的。

Qcom高通平臺Gralloc HAL實現

我們這裏拿到的代碼是AOSP的,和vendor從Qcom那裏拿到的估計有些區別。我們就看驍龍835吧~,msm8998的displayHAL相關的實現在hardware/qcom/display/msm8998

gralloc1整體架構

高通gralloc HAL的實現在libgralloc1中

* hardware/qcom/display/msm8998/libgralloc1/gr_device_impl.cpp

static struct hw_module_methods_t gralloc_module_methods = {.open = gralloc_device_open};

struct gralloc_module_t HAL_MODULE_INFO_SYM = {
  .common = {
    .tag = HARDWARE_MODULE_TAG,
    .module_api_version = GRALLOC_MODULE_API_VERSION_1_0,
    .hal_api_version    = HARDWARE_HAL_API_VERSION,
    .id = GRALLOC_HARDWARE_MODULE_ID,
    .name = "Graphics Memory Module",
    .author = "Code Aurora Forum",
    .methods = &gralloc_module_methods,
    .dso = 0,
    .reserved = {0},
  },
};

int gralloc_device_open(const struct hw_module_t *module, const char *name, hw_device_t **device) {
  int status = -EINVAL;
  if (!strcmp(name, GRALLOC_HARDWARE_MODULE_ID)) {
    gralloc1::GrallocImpl * /*gralloc1_device_t*/ dev = gralloc1::GrallocImpl::GetInstance(module);
    *device = reinterpret_cast<hw_device_t *>(dev);
    if (dev) {
      status = 0;
    } else {
      ALOGE("Fatal error opening gralloc1 device");
    }
  }
  return status;
}

Qcom 的gralloc是1.0版本~採用C++編寫,具體實現的類爲 GrallocImpl。GrallocImpl繼承gralloc1_device_t。

GrallocImpl::GrallocImpl(const hw_module_t *module) {
  common.tag = HARDWARE_DEVICE_TAG;
  common.version = GRALLOC_MODULE_API_VERSION_1_0;
  common.module = const_cast<hw_module_t *>(module);
  common.close = CloseDevice;
  getFunction = GetFunction;
  getCapabilities = GetCapabilities;

  initalized_ = Init();
}

gralloc1_device_t的getFunction初始化爲GetFunction,getCapabilities初始化爲GetCapabilities。

而在Init,申請了一個BufferManager。BufferManager是單例的用法。GrallocImpl也是單例的用法。

bool GrallocImpl::Init() {
  buf_mgr_ = BufferManager::GetInstance();
  return buf_mgr_ != nullptr;
}

Qcom的Gralloc1支持 Capabilities 有3種:

void GrallocImpl::GetCapabilities(struct gralloc1_device *device, uint32_t *out_count,
                                  int32_t  /*gralloc1_capability_t*/ *out_capabilities) {
  if (device != nullptr) {
    if (out_capabilities != nullptr && *out_count >= 3) {
      out_capabilities[0] = GRALLOC1_CAPABILITY_TEST_ALLOCATE;
      out_capabilities[1] = GRALLOC1_CAPABILITY_LAYERED_BUFFERS;
      out_capabilities[2] = GRALLOC1_CAPABILITY_RELEASE_IMPLY_DELETE;
    }
    *out_count = 3;
  }
  return;
}

從Android對Capabilities的定義來看,Qcom Gralloc1支持Android要求的所有能力。

* hardware/libhardware/include/hardware/gralloc1.h

typedef enum {
    GRALLOC1_CAPABILITY_INVALID = 0,

    /* If this capability is supported, then the outBuffers parameter to
     * allocate may be NULL, which instructs the device to report whether the
     * given allocation is possible or not. */
    GRALLOC1_CAPABILITY_TEST_ALLOCATE = 1,

    /* If this capability is supported, then the implementation supports
     * allocating buffers with more than one image layer. */
    GRALLOC1_CAPABILITY_LAYERED_BUFFERS = 2,

    /* If this capability is supported, then the implementation always closes
     * and deletes a buffer handle whenever the last reference is removed.
     *
     * Supporting this capability is strongly recommended.  It will become
     * mandatory in future releases. */
    GRALLOC1_CAPABILITY_RELEASE_IMPLY_DELETE = 3,

    GRALLOC1_LAST_CAPABILITY = 3,
} gralloc1_capability_t;

GetFunction函數,初始化函數指針,gralloc1_function_descriptor_t對應的指針實現如下:

gralloc1_function_pointer_t GrallocImpl::GetFunction(gralloc1_device_t *device, int32_t function) {
  if (!device) {
    return NULL;
  }

  switch (function) {
    case GRALLOC1_FUNCTION_DUMP:
      return reinterpret_cast<gralloc1_function_pointer_t>(Dump);
    case GRALLOC1_FUNCTION_CREATE_DESCRIPTOR:
      return reinterpret_cast<gralloc1_function_pointer_t>(CreateBufferDescriptor);
    case GRALLOC1_FUNCTION_DESTROY_DESCRIPTOR:
      return reinterpret_cast<gralloc1_function_pointer_t>(DestroyBufferDescriptor);
    case GRALLOC1_FUNCTION_SET_CONSUMER_USAGE:
      return reinterpret_cast<gralloc1_function_pointer_t>(SetConsumerUsage);
    case GRALLOC1_FUNCTION_SET_DIMENSIONS:
      return reinterpret_cast<gralloc1_function_pointer_t>(SetBufferDimensions);
    case GRALLOC1_FUNCTION_SET_FORMAT:
      return reinterpret_cast<gralloc1_function_pointer_t>(SetColorFormat);
    case GRALLOC1_FUNCTION_SET_LAYER_COUNT:
      return reinterpret_cast<gralloc1_function_pointer_t>(SetLayerCount);
    case GRALLOC1_FUNCTION_SET_PRODUCER_USAGE:
      return reinterpret_cast<gralloc1_function_pointer_t>(SetProducerUsage);
    case GRALLOC1_FUNCTION_GET_BACKING_STORE:
      return reinterpret_cast<gralloc1_function_pointer_t>(GetBackingStore);
    case GRALLOC1_FUNCTION_GET_CONSUMER_USAGE:
      return reinterpret_cast<gralloc1_function_pointer_t>(GetConsumerUsage);
    case GRALLOC1_FUNCTION_GET_DIMENSIONS:
      return reinterpret_cast<gralloc1_function_pointer_t>(GetBufferDimensions);
    case GRALLOC1_FUNCTION_GET_FORMAT:
      return reinterpret_cast<gralloc1_function_pointer_t>(GetColorFormat);
    case GRALLOC1_FUNCTION_GET_LAYER_COUNT:
      return reinterpret_cast<gralloc1_function_pointer_t>(GetLayerCount);
    case GRALLOC1_FUNCTION_GET_PRODUCER_USAGE:
      return reinterpret_cast<gralloc1_function_pointer_t>(GetProducerUsage);
    case GRALLOC1_FUNCTION_GET_STRIDE:
      return reinterpret_cast<gralloc1_function_pointer_t>(GetBufferStride);
    case GRALLOC1_FUNCTION_ALLOCATE:
      return reinterpret_cast<gralloc1_function_pointer_t>(AllocateBuffers);
    case GRALLOC1_FUNCTION_RETAIN:
      return reinterpret_cast<gralloc1_function_pointer_t>(RetainBuffer);
    case GRALLOC1_FUNCTION_RELEASE:
      return reinterpret_cast<gralloc1_function_pointer_t>(ReleaseBuffer);
    case GRALLOC1_FUNCTION_GET_NUM_FLEX_PLANES:
      return reinterpret_cast<gralloc1_function_pointer_t>(GetNumFlexPlanes);
    case GRALLOC1_FUNCTION_LOCK:
      return reinterpret_cast<gralloc1_function_pointer_t>(LockBuffer);
    case GRALLOC1_FUNCTION_LOCK_FLEX:
      return reinterpret_cast<gralloc1_function_pointer_t>(LockFlex);
    case GRALLOC1_FUNCTION_UNLOCK:
      return reinterpret_cast<gralloc1_function_pointer_t>(UnlockBuffer);
    case GRALLOC1_FUNCTION_PERFORM:
      return reinterpret_cast<gralloc1_function_pointer_t>(Gralloc1Perform);
    default:
      ALOGE("%s:Gralloc Error. Client Requested for unsupported function", __FUNCTION__);
      return NULL;
  }

  return NULL;
}

來看看Qcom Gralloc1的整體架構~
高通gralloc1關係類圖

  • GrallocImpl繼承gralloc1_device_t,這個Gralloc1具體的實現!
  • GrallocImpl採用一個BufferManager,管理Buffer,自己當領導!
  • BufferManager,抽像了一個Allocator,負責具體的Buffer分配!
  • Allocator說,我不具體幹活,這個活外包給IonAlloc幹,IonAlloc好好幹,幹不好,給就給別人來做了。
  • IonAlloc採用ion Buffer,負責具體的Buffer處理

總的來說,設計清晰,擴展方便~

allocate相關流程

我們來看下allocate Buffer的流程,代碼就不貼了,給個流程圖吧~
gralloc1的allocate流程

這個流程圖中,包括了release的流程~

Android中的importBuffer函數,在default中變爲registerBuffer,在HAL中變成retain,高通對應的實現爲RetainBuffer,IonAlloc中又變爲ImportBuffer。(…不止72變了…比孫悟空還厲害)

* hardware/qcom/display/msm8998/libgralloc1/gr_ion_alloc.cpp

int IonAlloc::ImportBuffer(int fd) {
  struct ion_fd_data fd_data;
  int err = 0;
  fd_data.fd = fd;
  if (ioctl(ion_dev_fd_, INT(ION_IOC_IMPORT), &fd_data)) {
    err = -errno;
    ALOGE("%s: ION_IOC_IMPORT failed with error - %s", __FUNCTION__, strerror(errno));
    return err;
  }
  return fd_data.handle;
}

import對應的ioctl爲ION_IOC_IMPORT。

ion相關的定義,Android有標準的要求,可以參考:

* system/core/libion/original-kernel-headers/linux/ion.h

#ifndef _UAPI_LINUX_ION_H
#define _UAPI_LINUX_ION_H

#include <linux/ioctl.h>
#include <linux/types.h>

typedef int ion_user_handle_t;

enum ion_heap_type {
    ION_HEAP_TYPE_SYSTEM,
    ION_HEAP_TYPE_SYSTEM_CONTIG,
    ION_HEAP_TYPE_CARVEOUT,
    ION_HEAP_TYPE_CHUNK,
    ION_HEAP_TYPE_DMA,
    ION_HEAP_TYPE_CUSTOM, /* must be last so device specific heaps always
                 are at the end of this enum */
    ION_NUM_HEAPS = 16,
};

#define ION_HEAP_SYSTEM_MASK        (1 << ION_HEAP_TYPE_SYSTEM)
#define ION_HEAP_SYSTEM_CONTIG_MASK (1 << ION_HEAP_TYPE_SYSTEM_CONTIG)
#define ION_HEAP_CARVEOUT_MASK      (1 << ION_HEAP_TYPE_CARVEOUT)
#define ION_HEAP_TYPE_DMA_MASK      (1 << ION_HEAP_TYPE_DMA)

#define ION_NUM_HEAP_IDS        sizeof(unsigned int) * 8

#define ION_FLAG_CACHED 1       /* mappings of this buffer should be
                       cached, ion will do cache
                       maintenance when the buffer is
                       mapped for dma */
#define ION_FLAG_CACHED_NEEDS_SYNC 2    /* mappings of this buffer will created
                       at mmap time, if this is set
                       caches must be managed manually */

struct ion_allocation_data {
    size_t len;
    size_t align;
    unsigned int heap_id_mask;
    unsigned int flags;
    ion_user_handle_t handle;
};

struct ion_fd_data {
    ion_user_handle_t handle;
    int fd;
};

struct ion_handle_data {
    ion_user_handle_t handle;
};

struct ion_custom_data {
    unsigned int cmd;
    unsigned long arg;
};

#define ION_IOC_MAGIC       'I'

#define ION_IOC_ALLOC       _IOWR(ION_IOC_MAGIC, 0, \
                      struct ion_allocation_data)

#define ION_IOC_FREE        _IOWR(ION_IOC_MAGIC, 1, struct ion_handle_data)

#define ION_IOC_MAP     _IOWR(ION_IOC_MAGIC, 2, struct ion_fd_data)

#define ION_IOC_SHARE       _IOWR(ION_IOC_MAGIC, 4, struct ion_fd_data)

#define ION_IOC_IMPORT      _IOWR(ION_IOC_MAGIC, 5, struct ion_fd_data)

#define ION_IOC_SYNC        _IOWR(ION_IOC_MAGIC, 7, struct ion_fd_data)

#define ION_IOC_CUSTOM      _IOWR(ION_IOC_MAGIC, 6, struct ion_custom_data)

#endif /* _UAPI_LINUX_ION_H */

Qcom對應的定義在msm_ion.h中,msm_ion.h在這裏就不看了。

Buffer的usage處理
usage分爲兩類,一個是Producer的,一個是Consumer的。在GetIonHeapInfo中對usage進行了處理。轉換爲ion對應的描述ion_heap_id,alloc_type以及ion_flags。這三個屬性上面的頭文件中有定義。usage的轉換如下:

* hardware/qcom/display/msm8998/libgralloc1/gr_allocator.cpp

void Allocator::GetIonHeapInfo(gralloc1_producer_usage_t prod_usage,
                               gralloc1_consumer_usage_t cons_usage, unsigned int *ion_heap_id,
                               unsigned int *alloc_type, unsigned int *ion_flags) {
  unsigned int heap_id = 0;
  unsigned int type = 0;
  uint32_t flags = 0;
  if (prod_usage & GRALLOC1_PRODUCER_USAGE_PROTECTED) {
    if (cons_usage & GRALLOC1_CONSUMER_USAGE_PRIVATE_SECURE_DISPLAY) {
      heap_id = ION_HEAP(SD_HEAP_ID);
      /*
       * There is currently no flag in ION for Secure Display
       * VM. Please add it to the define once available.
       */
      flags |= UINT(ION_SD_FLAGS);
    } else if (prod_usage & GRALLOC1_PRODUCER_USAGE_CAMERA) {
      heap_id = ION_HEAP(SD_HEAP_ID);
      if (cons_usage & GRALLOC1_CONSUMER_USAGE_HWCOMPOSER) {
        flags |= UINT(ION_SC_PREVIEW_FLAGS);
      } else {
        flags |= UINT(ION_SC_FLAGS);
      }
    } else {
      heap_id = ION_HEAP(CP_HEAP_ID);
      flags |= UINT(ION_CP_FLAGS);
    }
  } else if (prod_usage & GRALLOC1_PRODUCER_USAGE_PRIVATE_MM_HEAP) {
    // MM Heap is exclusively a secure heap.
    // If it is used for non secure cases, fallback to IOMMU heap
    ALOGW("MM_HEAP cannot be used as an insecure heap. Using system heap instead!!");
    heap_id |= ION_HEAP(ION_SYSTEM_HEAP_ID);
  }

  if (prod_usage & GRALLOC1_PRODUCER_USAGE_PRIVATE_CAMERA_HEAP) {
    heap_id |= ION_HEAP(ION_CAMERA_HEAP_ID);
  }

  if (prod_usage & GRALLOC1_PRODUCER_USAGE_PRIVATE_ADSP_HEAP ||
      prod_usage & GRALLOC1_PRODUCER_USAGE_SENSOR_DIRECT_DATA) {
    heap_id |= ION_HEAP(ION_ADSP_HEAP_ID);
  }

  if (flags & UINT(ION_SECURE)) {
    type |= private_handle_t::PRIV_FLAGS_SECURE_BUFFER;
  }

  // if no ion heap flags are set, default to system heap
  if (!heap_id) {
    heap_id = ION_HEAP(ION_SYSTEM_HEAP_ID);
  }

  *alloc_type = type;
  *ion_flags = flags;
  *ion_heap_id = heap_id;

  return;
}

Qcom的Gralloc1就不多介紹了,需要注意的是Qcom對很多數據結構進行封裝,增加了更多的信息,可以藉助Qcom的文檔進行理解。比如private_handle_t繼承native_handle_t,對native_handle_t進行了擴展。定義在下面的頭文件中。

hardware/qcom/display/msm8998/libgralloc1/gr_priv_handle.h

ION Buffer

Ion Buffer是一種內存分配器,是Android 4.0版本開始引入的,用以取代被詬病的PMEM,完美解決內存碎片管理。Ion管理着一個或多個內存池,其中有一些會在啓動的時候預先分配,供一些特殊的設備使用,比如GPU,Display。

IonAlloc初始化時,將會打開對應的ion驅動設備。

* hardware/qcom/display/msm8998/libgralloc1/gr_ion_alloc.cpp

bool IonAlloc::Init() {
  if (ion_dev_fd_ == FD_INIT) {
    ion_dev_fd_ = open(kIonDevice, O_RDONLY);
  }

  if (ion_dev_fd_ < 0) {
    ALOGE("%s: Failed to open ion device - %s", __FUNCTION__, strerror(errno));
    ion_dev_fd_ = FD_INIT;
    return false;
  }

  return true;
}

heap的類型

ION的驅動在kernel的驅動中,前面說到的system/core中的ion.h其實是自動生成的。我們基於這個開源的分支來看android-msm-wahoo-4.4-oreo-mr1

kernel/drivers/staging/android/ion

Ion定義了6種不同的heap類似,實現不同的分配策略:

* drivers/staging/android/uapi/ion.h

enum ion_heap_type {
    ION_HEAP_TYPE_SYSTEM,
    ION_HEAP_TYPE_SYSTEM_CONTIG,
    ION_HEAP_TYPE_CARVEOUT,
    ION_HEAP_TYPE_CHUNK,
    ION_HEAP_TYPE_DMA,
    ION_HEAP_TYPE_CUSTOM, 
    ION_NUM_HEAPS = 16,
};
  • ION_HEAP_TYPE_SYSTEM
    通過vmallc分配,vmalloc只保證內存在虛擬地址空間是連續的。

  • ION_HEAP_TYPE_SYSTEM_CONTIG
    通過kmalloc分配,kmalloc保證物理地址也是連續的。

  • ION_HEAP_TYPE_CARVEOUT
    從保留的carveout 中分配一個heap,分配的內存是物理連續的。

  • ION_HEAP_TYPE_CHUNK
    分配一快大內存

  • ION_HEAP_TYPE_DMA
    通過DMA API分配內存,DMA的Buffer

  • ION_HEAP_TYPE_CUSTOM
    由用戶自己定義,在enum中,必須是最後,這種heap比較特殊

Qcom msm8998中,實現的Ion type如下:

arch/arm/boot/dts/qcom/msm8998-ion.dtsi

&soc {
    qcom,ion {
        compatible = "qcom,msm-ion";
        #address-cells = <1>;
        #size-cells = <0>;

        system_heap: qcom,ion-heap@25 {
            reg = <25>;
            qcom,ion-heap-type = "SYSTEM";
        };

        qcom,ion-heap@22 { /* ADSP HEAP */
            reg = <22>;
            memory-region = <&adsp_mem>;
            qcom,ion-heap-type = "DMA";
        };

        qcom,ion-heap@27 { /* QSEECOM HEAP */
            reg = <27>;
            memory-region = <&qseecom_mem>;
            qcom,ion-heap-type = "DMA";
        };

        qcom,ion-heap@13 { /* SPSS HEAP */
            reg = <13>;
            memory-region = <&sp_mem>;
            qcom,ion-heap-type = "DMA";
        };

        qcom,ion-heap@10 { /* SECURE DISPLAY HEAP */
            reg = <10>;
            memory-region = <&secure_display_memory>;
            qcom,ion-heap-type = "HYP_CMA";
        };

        qcom,ion-heap@9 {
            reg = <9>;
            qcom,ion-heap-type = "SYSTEM_SECURE";
        };
    };
};

Qcom定義了更多的heap類型,做特殊之用。dts中的定義在驅動初始化時,將被讀出來,構建ion_platform_heap,用ion_platform_heap進行描述。

* drivers/staging/android/ion/msm/msm_ion.c

static struct ion_platform_data *msm_ion_parse_dt(struct platform_device *pdev)
{
    struct ion_platform_data *pdata = 0;
    struct ion_platform_heap *heaps = NULL;
    struct device_node *node;
    struct platform_device *new_dev = NULL;
    const struct device_node *dt_node = pdev->dev.of_node;
    uint32_t val = 0;
    int ret = 0;
    uint32_t num_heaps = 0;
    int idx = 0;

    for_each_available_child_of_node(dt_node, node)
        num_heaps++;

    if (!num_heaps)
        return ERR_PTR(-EINVAL);

    pdata = kzalloc(sizeof(struct ion_platform_data), GFP_KERNEL);
    if (!pdata)
        return ERR_PTR(-ENOMEM);

    heaps = kzalloc(sizeof(struct ion_platform_heap)*num_heaps, GFP_KERNEL);
    if (!heaps) {
        kfree(pdata);
        return ERR_PTR(-ENOMEM);
    }

    pdata->heaps = heaps;
    pdata->nr = num_heaps;

    for_each_available_child_of_node(dt_node, node) {
        new_dev = of_platform_device_create(node, NULL, &pdev->dev);
        if (!new_dev) {
            pr_err("Failed to create device %s\n", node->name);
            goto free_heaps;
        }

        pdata->heaps[idx].priv = &new_dev->dev;
        /**
         * TODO: Replace this with of_get_address() when this patch
         * gets merged: http://
         * permalink.gmane.org/gmane.linux.drivers.devicetree/18614
        */
        ret = of_property_read_u32(node, "reg", &val);
        if (ret) {
            pr_err("%s: Unable to find reg key", __func__);
            goto free_heaps;
        }
        pdata->heaps[idx].id = val;

        ret = msm_ion_populate_heap(node, &pdata->heaps[idx]);
        if (ret)
            goto free_heaps;

        msm_ion_get_heap_dt_data(node, &pdata->heaps[idx]);

        ++idx;
    }
    return pdata;

free_heaps:
    free_pdata(pdata);
    return ERR_PTR(ret);
}

ion_platform_heap定定義如下:

* drivers/staging/android/ion/ion.h

/**
 * struct ion_platform_heap - defines a heap in the given platform
 * @type:   type of the heap from ion_heap_type enum
 * @id:     unique identifier for heap.  When allocating higher numbers
 *      will be allocated from first.  At allocation these are passed
 *      as a bit mask and therefore can not exceed ION_NUM_HEAP_IDS.
 * @name:   used for debug purposes
 * @base:   base address of heap in physical memory if applicable
 * @size:   size of the heap in bytes if applicable
 * @has_outer_cache:    set to 1 if outer cache is used, 0 otherwise.
 * @extra_data: Extra data specific to each heap type
 * @priv:   heap private data
 * @align:  required alignment in physical memory if applicable
 * @priv:   private info passed from the board file
 *
 * Provided by the board file.
 */

struct ion_platform_heap {
    enum ion_heap_type type;
    unsigned int id;
    const char *name;
    ion_phys_addr_t base;
    size_t size;
    unsigned int has_outer_cache;
    void *extra_data;
    ion_phys_addr_t align;
    void *priv;
};

type,就是dts中的ion-heap-type再加上ION_HEAP_TYPE_的前綴。
id,唯一的,是dts中reg的值
base,是物理地址的起始地址
name 是heap的名字,主要用來debug,以對應的id的形式定義在ion_heap_meta中。

* drivers/staging/android/ion/msm/msm_ion.c

static struct ion_heap_desc ion_heap_meta[] = {
    {
        .id = ION_SYSTEM_HEAP_ID,
        .name   = ION_SYSTEM_HEAP_NAME,
    },
    {
        .id = ION_SYSTEM_CONTIG_HEAP_ID,
        .name   = ION_KMALLOC_HEAP_NAME,
    },
    {
        .id = ION_SECURE_HEAP_ID,
        .name   = ION_SECURE_HEAP_NAME,
    },
    {
        .id = ION_CP_MM_HEAP_ID,
        .name   = ION_MM_HEAP_NAME,
        .permission_type = IPT_TYPE_MM_CARVEOUT,
    },
    {
        .id = ION_MM_FIRMWARE_HEAP_ID,
        .name   = ION_MM_FIRMWARE_HEAP_NAME,
    },
    {
        .id = ION_GOOGLE_HEAP_ID,
        .name   = ION_GOOGLE_HEAP_NAME,
    },
    {
        .id = ION_CP_MFC_HEAP_ID,
        .name   = ION_MFC_HEAP_NAME,
        .permission_type = IPT_TYPE_MFC_SHAREDMEM,
    },
    {
        .id = ION_SF_HEAP_ID,
        .name   = ION_SF_HEAP_NAME,
    },
    {
        .id = ION_QSECOM_HEAP_ID,
        .name   = ION_QSECOM_HEAP_NAME,
    },
    {
        .id = ION_SPSS_HEAP_ID,
        .name   = ION_SPSS_HEAP_NAME,
    },
    {
        .id = ION_AUDIO_HEAP_ID,
        .name   = ION_AUDIO_HEAP_NAME,
    },
    {
        .id = ION_PIL1_HEAP_ID,
        .name   = ION_PIL1_HEAP_NAME,
    },
    {
        .id = ION_PIL2_HEAP_ID,
        .name   = ION_PIL2_HEAP_NAME,
    },
    {
        .id = ION_CP_WB_HEAP_ID,
        .name   = ION_WB_HEAP_NAME,
    },
    {
        .id = ION_CAMERA_HEAP_ID,
        .name   = ION_CAMERA_HEAP_NAME,
    },
    {
        .id = ION_ADSP_HEAP_ID,
        .name   = ION_ADSP_HEAP_NAME,
    },
    {
        .id = ION_SECURE_DISPLAY_HEAP_ID,
        .name   = ION_SECURE_DISPLAY_HEAP_NAME,
    }
};

解析完dts,ion_platform_heap被放在ion_platform_data的heaps中。

創建ion設備,通過ion_device_create函數:

static int msm_ion_probe(struct platform_device *pdev)
{
    ... ...

    new_dev = ion_device_create(compat_msm_ion_ioctl);

創建ion_device成功後,會根據解析出來的ion_platform_heap,通過msm_ion_heap_create創建對應的ion_heap。
msm_ion_allocate 函數如下:

* drivers/staging/android/ion/msm/msm_ion.c

static struct ion_heap *msm_ion_heap_create(struct ion_platform_heap *heap_data)
{
    struct ion_heap *heap = NULL;

    switch ((int)heap_data->type) {
#ifdef CONFIG_CMA
    case ION_HEAP_TYPE_SECURE_DMA:
        heap = ion_secure_cma_heap_create(heap_data);
        break;
#endif
    case ION_HEAP_TYPE_SYSTEM_SECURE:
        heap = ion_system_secure_heap_create(heap_data);
        break;
    case ION_HEAP_TYPE_HYP_CMA:
        heap = ion_cma_secure_heap_create(heap_data);
        break;
    default:
        heap = ion_heap_create(heap_data);
    }

    if (IS_ERR_OR_NULL(heap)) {
        pr_err("%s: error creating heap %s type %d base %pa size %zu\n",
               __func__, heap_data->name, heap_data->type,
               &heap_data->base, heap_data->size);
        return ERR_PTR(-EINVAL);
    }

    heap->name = heap_data->name;
    heap->id = heap_data->id;
    heap->priv = heap_data->priv;
    return heap;
}

實際分配的heap有4種:

類型 函數實現
ION_HEAP_TYPE_SECURE_DMA ion_secure_cma_heap_create
ION_HEAP_TYPE_SYSTEM_SECURE ion_system_secure_heap_create
ION_HEAP_TYPE_HYP_CMA ion_cma_secure_heap_create
ION_HEAP_TYPE_SYSTEM default ion_heap_create

創建的ion_heap通過ion_device_add_heap中的plist_add(&heap->node, &dev->heaps),把每個創建的ion_heap->node鏈接到ion_device->heaps。

* drivers/staging/android/ion/ion.c

void ion_device_add_heap(struct ion_device *dev, struct ion_heap *heap)
{
    struct dentry *debug_file;

    if (!heap->ops->allocate || !heap->ops->free || !heap->ops->map_dma ||
        !heap->ops->unmap_dma)
        pr_err("%s: can not add heap with invalid ops struct.\n",
               __func__);

    spin_lock_init(&heap->free_lock);
    heap->free_list_size = 0;

    if (heap->flags & ION_HEAP_FLAG_DEFER_FREE)
        ion_heap_init_deferred_free(heap);

    if ((heap->flags & ION_HEAP_FLAG_DEFER_FREE) || heap->ops->shrink)
        ion_heap_init_shrinker(heap);

    heap->dev = dev;
    down_write(&dev->lock);
    /*
     * use negative heap->id to reverse the priority -- when traversing
     * the list later attempt higher id numbers first
     */
    plist_node_init(&heap->node, -heap->id);
    plist_add(&heap->node, &dev->heaps);
    debug_file = debugfs_create_file(heap->name, 0664,
                    dev->heaps_debug_root, heap,
                    &debug_heap_fops);

    ... ...

    up_write(&dev->lock);
}
  • ION_HEAP_TYPE_SECURE_DMA
    SECURE_DMA類型相關的實現如下:
* drivers/staging/android/ion/ion_cma_secure_heap.c

struct ion_cma_secure_heap {
    struct device *dev;
    /*
     * Protects against races between threads allocating memory/adding to
     * pool at the same time. (e.g. thread 1 adds to pool, thread 2
     * allocates thread 1's memory before thread 1 knows it needs to
     * allocate more.
     * Admittedly this is fairly coarse grained right now but the chance for
     * contention on this lock is unlikely right now. This can be changed if
     * this ever changes in the future
     */
    struct mutex alloc_lock;
    /*
     * protects the list of memory chunks in this pool
     */
    struct mutex chunk_lock;
    struct ion_heap heap;
    /*
     * Bitmap for allocation. This contains the aggregate of all chunks. */
    unsigned long *bitmap;
    /*
     * List of all allocated chunks
     *
     * This is where things get 'clever'. Individual allocations from
     * dma_alloc_coherent must be allocated and freed in one chunk.
     * We don't just want to limit the allocations to those confined
     * within a single chunk (if clients allocate n small chunks we would
     * never be able to use the combined size). The bitmap allocator is
     * used to find the contiguous region and the parts of the chunks are
     * marked off as used. The chunks won't be freed in the shrinker until
     * the usage is actually zero.
     */
    struct list_head chunks;
    int npages;
    ion_phys_addr_t base;
    struct work_struct work;
    unsigned long last_alloc;
    struct shrinker shrinker;
    atomic_t total_allocated;
    atomic_t total_pool_size;
    atomic_t total_leaked;
    unsigned long heap_size;
    unsigned long default_prefetch_size;
};

ion_cma_secure_heap繼承ion_heap,又擴展了很多實現

SECURE_DMA對應的Ops如下:

static struct ion_heap_ops ion_secure_cma_ops = {
    .allocate = ion_secure_cma_allocate,
    .free = ion_secure_cma_free,
    .map_dma = ion_secure_cma_heap_map_dma,
    .unmap_dma = ion_secure_cma_heap_unmap_dma,
    .phys = ion_secure_cma_phys,
    .map_user = ion_secure_cma_mmap,
    .map_kernel = ion_secure_cma_map_kernel,
    .unmap_kernel = ion_secure_cma_unmap_kernel,
    .print_debug = ion_secure_cma_print_debug,
};

Ops就是ioctl下來的,相關cmd的實現。

  • ION_HEAP_TYPE_SYSTEM_SECURE
    SYSTEM_SECURE採用ion_system_secure_heap進行 描述,繼承ion_heap。
* drivers/staging/android/ion/ion_system_secure_heap.c

struct ion_system_secure_heap {
    struct ion_heap *sys_heap;
    struct ion_heap heap;

    /* Protects prefetch_list */
    spinlock_t work_lock;
    bool destroy_heap;
    struct list_head prefetch_list;
    struct delayed_work prefetch_work;
};

對應的接口實現爲system_secure_heap_ops

static struct ion_heap_ops system_secure_heap_ops = {
    .allocate = ion_system_secure_heap_allocate,
    .free = ion_system_secure_heap_free,
    .map_dma = ion_system_secure_heap_map_dma,
    .unmap_dma = ion_system_secure_heap_unmap_dma,
    .map_kernel = ion_system_secure_heap_map_kernel,
    .unmap_kernel = ion_system_secure_heap_unmap_kernel,
    .map_user = ion_system_secure_heap_map_user,
    .shrink = ion_system_secure_heap_shrink,
};
  • ION_HEAP_TYPE_HYP_CMA
    HYP_CMA主要用於Secure 顯示。HYP_CMA沒有做擴展,還是用ion_heap描述。但是採用Ops爲ion_secure_cma_ops。
* drivers/staging/android/ion/ion_cma_heap.c

static struct ion_heap_ops ion_secure_cma_ops = {
    .allocate = ion_secure_cma_allocate,
    .free = ion_secure_cma_free,
    .map_dma = ion_cma_heap_map_dma,
    .unmap_dma = ion_cma_heap_unmap_dma,
    .phys = ion_cma_phys,
    .map_user = ion_cma_mmap,
    .map_kernel = ion_cma_map_kernel,
    .unmap_kernel = ion_cma_unmap_kernel,
    .print_debug = ion_cma_print_debug,
};
  • 除上面3中類型外,其他的類型,通過ion_heap_create分配
    具體的對應關係如下:
* drivers/staging/android/ion/ion_heap.c

struct ion_heap *ion_heap_create(struct ion_platform_heap *heap_data)
{
    struct ion_heap *heap = NULL;

    switch (heap_data->type) {
    case ION_HEAP_TYPE_SYSTEM_CONTIG:
        pr_err("%s: Heap type is disabled: %d\n", __func__,
               heap_data->type);
        return ERR_PTR(-EINVAL);
    case ION_HEAP_TYPE_SYSTEM:
        heap = ion_system_heap_create(heap_data);
        break;
    case ION_HEAP_TYPE_CARVEOUT:
        heap = ion_carveout_heap_create(heap_data);
        break;
    case ION_HEAP_TYPE_CHUNK:
        heap = ion_chunk_heap_create(heap_data);
        break;
    case ION_HEAP_TYPE_DMA:
        heap = ion_cma_heap_create(heap_data);
        break;
    default:
        pr_err("%s: Invalid heap type %d\n", __func__,
               heap_data->type);
        return ERR_PTR(-EINVAL);
    }

    if (IS_ERR_OR_NULL(heap)) {
        pr_err("%s: error creating heap %s type %d base %pa size %zu\n",
               __func__, heap_data->name, heap_data->type,
               &heap_data->base, heap_data->size);
        return ERR_PTR(-EINVAL);
    }

    heap->name = heap_data->name;
    heap->id = heap_data->id;
    heap->priv = heap_data->priv;
    return heap;
}

比如,ion_system_heap_create類型,採用ion_system_heap描述,繼承ion_heap.

* drivers/staging/android/ion/ion_system_heap.c

struct ion_system_heap {
    struct ion_heap heap;
    struct ion_page_pool **uncached_pools;
    struct ion_page_pool **cached_pools;
    struct ion_page_pool **secure_pools[VMID_LAST];
    /* Prevents unnecessary page splitting */
    struct mutex split_page_mutex;
};

對應的Ops爲system_heap_ops:

static struct ion_heap_ops system_heap_ops = {
    .allocate = ion_system_heap_allocate,
    .free = ion_system_heap_free,
    .map_dma = ion_system_heap_map_dma,
    .unmap_dma = ion_system_heap_unmap_dma,
    .map_kernel = ion_heap_map_kernel,
    .unmap_kernel = ion_heap_unmap_kernel,
    .map_user = ion_heap_map_user,
    .shrink = ion_system_heap_shrink,
};

Ion API

在用戶空間,提供了系統調用ioctl,對應內存空間的API,內核空間的API對應具體類型的Heap的API。Heap的API用ion_heap_ops描述,前面我們已經說過了每種類型的API對應的ion_heap_ops。

除了Ion的標準API外,Qcom又定製了一些自己的ioctl,定製的ioctl實現爲compat_msm_ion_ioctl,在下面的代碼中

* drivers/staging/android/ion/compat_msm_ion.c

Ion標準的ioctl,在ion_ioctl中:

* drivers/staging/android/ion/ion.c

static const struct file_operations ion_fops = {
    .owner          = THIS_MODULE,
    .open           = ion_open,
    .release        = ion_release,
    .unlocked_ioctl = ion_ioctl,
    .compat_ioctl   = compat_ion_ioctl,
};

在我們的測試代碼中,Producer設置是usage爲GRALLOC_USAGE_SW_WRITE_OFTEN,Consumer中設置的usage爲USAGE_HW_COMPOSER,Layer創建的時候設置的。

uint32_t Layer::getEffectiveUsage(uint32_t usage) const {
    // TODO: should we do something special if mSecure is set?
    if (mProtectedByApp) {
        // need a hardware-protected path to external video sink
        usage |= GraphicBuffer::USAGE_PROTECTED;
    }
    if (mPotentialCursor) {
        usage |= GraphicBuffer::USAGE_CURSOR;
    }
    usage |= GraphicBuffer::USAGE_HW_COMPOSER;
    return usage;
}

GRALLOC_USAGE_SW_WRITE_OFTEN將被轉換爲 GRALLOC1_PRODUCER_USAGE_CPU_WRITE_OFTEN;USAGE_HW_COMPOSER將被轉換爲GRALLOC1_CONSUMER_USAGE_HWCOMPOSER;對應的heap id爲ION_SYSTEM_HEAP_ID。

下面,以ION_HEAP_TYPE_SYSTEM爲例,我們通過一個表格來描述他們直接的關係,和API的用途

用戶空間API 內核空間API Heap API 作用
open ion_client_create null 分配一個Ion的客戶端,客戶端負責和Ion設備進行通信
close ion_client_destroy null 釋放一個Ion的客戶端
ION_IOC_ALLOC ion_buffer_create ion_system_heap_allocate,map_dma 申請一塊Ion內存,返回Ion Handle
ION_IOC_FREE ion_free ion_free_nolock ion_system_heap_free 釋放Ion handle
ION_IOC_SHARE & ION_IOC_MAP ion_share_dma_buf_fd null 爲制定的Buffer創建DMA映射,返回DMA Buffer的FD
ION_IOC_IMPORT ion_import_dma_buf null 通過DMA的FD,返回Ion Buffer的Handle
mmap ion_mmap ion_heap_map_user map內存到user空間

Qcom定製的Ioctl,ION_IOC_CUSTOM還有

ION_IOC_CLEAN_CACHES
ION_IOC_INV_CACHES
ION_IOC_CLEAN_INV_CACHES
ION_IOC_PREFETCH
ION_IOC_DRAIN

ION是通過handle而非buffer地址來實現驅動間共享內存,用戶空間共享內存也是利用同樣原理,所以,map,import都是通過handle來完成。另外,Ion Buffer創建後,映射到 DMA Buffer,後續通過DMA Buffer來處理。

我們我們來看他們之間的關係類圖~
ION 關係類圖

Ion Debug

Ion 在/sys/kernel/debug/ion/ 提供一個debugfs 接口。

... /sys/kernel/debug/ion # ls
clients egl heaps 

每個heap都有自己的debugfs目錄,client內存使用狀況顯示在/sys/kernel/debug/ion/heaps/<>

... /sys/kernel/debug/ion/heaps # ls 
carveout_fb carveout_fb_shrink carveout_overlay carveout_overlay_shrink system system_shrink

比如這個system的分配情況:

... /sys/kernel/debug/ion/heaps # cat system
          client              pid             size
----------------------------------------------------
----------------------------------------------------
orphaned allocations (info is from last known client):
          client      pid             user user_pid             size  mcnt  rcnt
 [email protected]      257  [email protected]      258          7372800     0     1
 [email protected]      257  [email protected]      258           139264     0     1
 [email protected]      257  [email protected]      258           139264     0     1
 [email protected]      257  [email protected]      258          3686400     0     1
 [email protected]      257  [email protected]      258          3686400     0     1
 [email protected]      257  [email protected]      258          3686400     0     1
 [email protected]      257  [email protected]      258           139264     0     1
----------------------------------------------------
  total orphaned         18849792
          total          18849792
   deferred free                0
----------------------------------------------------
4 order 8 highmem pages                     in uncached pool = 4194304 total
2 order 8 lowmem pages              in uncached pool = 2097152 total
14 order 4 highmem pages                    in uncached pool = 917504 total
0 order 4 lowmem pages              in uncached pool = 0 total
0 order 0 highmem pages                     in uncached pool = 0 total
838 order 0 lowmem pages                in uncached pool = 3432448 total
0 order 8 highmem pages                     in cached pool = 0 total
0 order 8 lowmem pages                  in cached pool = 0 total
0 order 4 highmem pages                     in cached pool = 0 total
0 order 4 lowmem pages                  in cached pool = 0 total
0 order 0 highmem pages                     in cached pool = 0 total
0 order 0 lowmem pages                  in cached pool = 0 total

前面是ion Client的pid,這裏的[email protected]。然後是使用者pid,這裏是[email protected](大部分Buffer都是這個進分配的,用於顯示)。

小結

本章主要講述GraphicBuffer相關的流程,結合 Qcom的msm8998,講述了Gralloc1.0的接口實現,介紹了Ion使用及Ion驅動實現。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章