mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-09 21:59:12 -04:00
Merge "memshare: Add snapshot of memshare driver"
This commit is contained in:
commit
71b310c375
8 changed files with 1640 additions and 0 deletions
|
|
@ -294,6 +294,8 @@ config QCOM_QMI_HELPERS
|
|||
depends on ARCH_QCOM || COMPILE_TEST
|
||||
depends on NET
|
||||
|
||||
source "drivers/soc/qcom/memshare/Kconfig"
|
||||
|
||||
config QCOM_RMTFS_MEM
|
||||
tristate "Qualcomm Remote Filesystem memory driver"
|
||||
depends on ARCH_QCOM
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ obj-$(CONFIG_MSM_SERVICE_NOTIFIER) += service-notifier.o
|
|||
obj-$(CONFIG_MSM_SERVICE_LOCATOR) += service-locator.o
|
||||
obj-$(CONFIG_QCOM_QMI_HELPERS) += qmi_helpers.o
|
||||
qmi_helpers-y += qmi_encdec.o qmi_interface.o
|
||||
obj-$(CONFIG_MEM_SHARE_QMI_SERVICE) = memshare/
|
||||
obj-$(CONFIG_QCOM_RMTFS_MEM) += rmtfs_mem.o
|
||||
obj-$(CONFIG_QCOM_RPMH) += qcom_rpmh.o
|
||||
qcom_rpmh-y += rpmh-rsc.o
|
||||
|
|
|
|||
13
drivers/soc/qcom/memshare/Kconfig
Normal file
13
drivers/soc/qcom/memshare/Kconfig
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
# Shared Heap for external processors
|
||||
#
|
||||
config MEM_SHARE_QMI_SERVICE
|
||||
depends on QCOM_QMI_HELPERS
|
||||
bool "Shared Heap for external processors"
|
||||
help
|
||||
Memory Share Kernel QTI Messaging Interface Service
|
||||
receives requests from Modem Processor Sub System
|
||||
for heap alloc/free from Application Processor
|
||||
Sub System and send a response back to client with
|
||||
proper handle/address.
|
||||
2
drivers/soc/qcom/memshare/Makefile
Normal file
2
drivers/soc/qcom/memshare/Makefile
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
obj-$(CONFIG_MEM_SHARE_QMI_SERVICE) := heap_mem_ext_v01.o msm_memshare.o
|
||||
340
drivers/soc/qcom/memshare/heap_mem_ext_v01.c
Normal file
340
drivers/soc/qcom/memshare/heap_mem_ext_v01.c
Normal file
|
|
@ -0,0 +1,340 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/* Copyright (c) 2013-2015, 2017-2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/soc/qcom/qmi.h>
|
||||
#include "heap_mem_ext_v01.h"
|
||||
|
||||
struct qmi_elem_info dhms_mem_alloc_addr_info_type_v01_ei[] = {
|
||||
{
|
||||
.data_type = QMI_UNSIGNED_8_BYTE,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(uint64_t),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = QMI_COMMON_TLV_TYPE,
|
||||
.offset = offsetof(struct
|
||||
dhms_mem_alloc_addr_info_type_v01,
|
||||
phy_addr),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_UNSIGNED_4_BYTE,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(uint32_t),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = QMI_COMMON_TLV_TYPE,
|
||||
.offset = offsetof(struct
|
||||
dhms_mem_alloc_addr_info_type_v01,
|
||||
num_bytes),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_EOTI,
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = QMI_COMMON_TLV_TYPE,
|
||||
},
|
||||
};
|
||||
|
||||
struct qmi_elem_info mem_alloc_generic_req_msg_data_v01_ei[] = {
|
||||
{
|
||||
.data_type = QMI_UNSIGNED_4_BYTE,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(uint32_t),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x01,
|
||||
.offset = offsetof(struct mem_alloc_generic_req_msg_v01,
|
||||
num_bytes),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_UNSIGNED_4_BYTE,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(uint32_t),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x02,
|
||||
.offset = offsetof(struct mem_alloc_generic_req_msg_v01,
|
||||
client_id),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_UNSIGNED_4_BYTE,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(uint32_t),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x03,
|
||||
.offset = offsetof(struct mem_alloc_generic_req_msg_v01,
|
||||
proc_id),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_UNSIGNED_4_BYTE,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(uint32_t),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x04,
|
||||
.offset = offsetof(struct mem_alloc_generic_req_msg_v01,
|
||||
sequence_id),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_OPT_FLAG,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(uint8_t),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x10,
|
||||
.offset = offsetof(struct mem_alloc_generic_req_msg_v01,
|
||||
alloc_contiguous_valid),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_UNSIGNED_1_BYTE,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(uint8_t),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x10,
|
||||
.offset = offsetof(struct mem_alloc_generic_req_msg_v01,
|
||||
alloc_contiguous),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_OPT_FLAG,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(uint8_t),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x11,
|
||||
.offset = offsetof(struct mem_alloc_generic_req_msg_v01,
|
||||
block_alignment_valid),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_UNSIGNED_4_BYTE,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(uint32_t),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x11,
|
||||
.offset = offsetof(struct mem_alloc_generic_req_msg_v01,
|
||||
block_alignment),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_EOTI,
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = QMI_COMMON_TLV_TYPE,
|
||||
},
|
||||
};
|
||||
|
||||
struct qmi_elem_info mem_alloc_generic_resp_msg_data_v01_ei[] = {
|
||||
{
|
||||
.data_type = QMI_STRUCT,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(struct qmi_response_type_v01),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x02,
|
||||
.offset = offsetof(struct
|
||||
mem_alloc_generic_resp_msg_v01,
|
||||
resp),
|
||||
.ei_array = qmi_response_type_v01_ei,
|
||||
},
|
||||
{
|
||||
.data_type = QMI_OPT_FLAG,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(uint8_t),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x10,
|
||||
.offset = offsetof(struct
|
||||
mem_alloc_generic_resp_msg_v01,
|
||||
sequence_id_valid),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_UNSIGNED_4_BYTE,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(uint32_t),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x10,
|
||||
.offset = offsetof(struct
|
||||
mem_alloc_generic_resp_msg_v01,
|
||||
sequence_id),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_OPT_FLAG,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(uint8_t),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x11,
|
||||
.offset = offsetof(struct
|
||||
mem_alloc_generic_resp_msg_v01,
|
||||
dhms_mem_alloc_addr_info_valid),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_DATA_LEN,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(uint8_t),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x11,
|
||||
.offset = offsetof(struct
|
||||
mem_alloc_generic_resp_msg_v01,
|
||||
dhms_mem_alloc_addr_info_len),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_STRUCT,
|
||||
.elem_len = MAX_ARR_CNT_V01,
|
||||
.elem_size = sizeof(struct
|
||||
dhms_mem_alloc_addr_info_type_v01),
|
||||
.array_type = VAR_LEN_ARRAY,
|
||||
.tlv_type = 0x11,
|
||||
.offset = offsetof(struct
|
||||
mem_alloc_generic_resp_msg_v01,
|
||||
dhms_mem_alloc_addr_info),
|
||||
.ei_array = dhms_mem_alloc_addr_info_type_v01_ei,
|
||||
},
|
||||
{
|
||||
.data_type = QMI_EOTI,
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = QMI_COMMON_TLV_TYPE,
|
||||
},
|
||||
};
|
||||
|
||||
struct qmi_elem_info mem_free_generic_req_msg_data_v01_ei[] = {
|
||||
{
|
||||
.data_type = QMI_DATA_LEN,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(uint8_t),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x01,
|
||||
.offset = offsetof(struct mem_free_generic_req_msg_v01,
|
||||
dhms_mem_alloc_addr_info_len),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_STRUCT,
|
||||
.elem_len = MAX_ARR_CNT_V01,
|
||||
.elem_size = sizeof(struct
|
||||
dhms_mem_alloc_addr_info_type_v01),
|
||||
.array_type = VAR_LEN_ARRAY,
|
||||
.tlv_type = 0x01,
|
||||
.offset = offsetof(struct mem_free_generic_req_msg_v01,
|
||||
dhms_mem_alloc_addr_info),
|
||||
.ei_array = dhms_mem_alloc_addr_info_type_v01_ei,
|
||||
},
|
||||
{
|
||||
.data_type = QMI_OPT_FLAG,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(uint8_t),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x10,
|
||||
.offset = offsetof(struct mem_free_generic_req_msg_v01,
|
||||
client_id_valid),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_UNSIGNED_4_BYTE,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(uint32_t),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x10,
|
||||
.offset = offsetof(struct mem_free_generic_req_msg_v01,
|
||||
client_id),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_OPT_FLAG,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(uint8_t),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x11,
|
||||
.offset = offsetof(struct mem_free_generic_req_msg_v01,
|
||||
proc_id_valid),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_UNSIGNED_4_BYTE,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(uint32_t),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x11,
|
||||
.offset = offsetof(struct mem_free_generic_req_msg_v01,
|
||||
proc_id),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_EOTI,
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = QMI_COMMON_TLV_TYPE,
|
||||
},
|
||||
};
|
||||
|
||||
struct qmi_elem_info mem_free_generic_resp_msg_data_v01_ei[] = {
|
||||
{
|
||||
.data_type = QMI_STRUCT,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(struct qmi_response_type_v01),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x02,
|
||||
.offset = offsetof(struct
|
||||
mem_free_generic_resp_msg_v01,
|
||||
resp),
|
||||
.ei_array = qmi_response_type_v01_ei,
|
||||
},
|
||||
{
|
||||
.data_type = QMI_EOTI,
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = QMI_COMMON_TLV_TYPE,
|
||||
},
|
||||
};
|
||||
|
||||
struct qmi_elem_info mem_query_size_req_msg_data_v01_ei[] = {
|
||||
{
|
||||
.data_type = QMI_UNSIGNED_4_BYTE,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(uint32_t),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x01,
|
||||
.offset = offsetof(struct mem_query_size_req_msg_v01,
|
||||
client_id),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_OPT_FLAG,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(uint8_t),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x10,
|
||||
.offset = offsetof(struct mem_query_size_req_msg_v01,
|
||||
proc_id_valid),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_UNSIGNED_4_BYTE,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(uint32_t),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x10,
|
||||
.offset = offsetof(struct mem_query_size_req_msg_v01,
|
||||
proc_id),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_EOTI,
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = QMI_COMMON_TLV_TYPE,
|
||||
},
|
||||
};
|
||||
|
||||
struct qmi_elem_info mem_query_size_resp_msg_data_v01_ei[] = {
|
||||
{
|
||||
.data_type = QMI_STRUCT,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(struct qmi_response_type_v01),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x02,
|
||||
.offset = offsetof(struct
|
||||
mem_query_size_rsp_msg_v01,
|
||||
resp),
|
||||
.ei_array = qmi_response_type_v01_ei,
|
||||
},
|
||||
{
|
||||
.data_type = QMI_OPT_FLAG,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(uint8_t),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x10,
|
||||
.offset = offsetof(struct mem_query_size_rsp_msg_v01,
|
||||
size_valid),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_UNSIGNED_4_BYTE,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(uint32_t),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x10,
|
||||
.offset = offsetof(struct mem_query_size_rsp_msg_v01,
|
||||
size),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_EOTI,
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = QMI_COMMON_TLV_TYPE,
|
||||
},
|
||||
};
|
||||
263
drivers/soc/qcom/memshare/heap_mem_ext_v01.h
Normal file
263
drivers/soc/qcom/memshare/heap_mem_ext_v01.h
Normal file
|
|
@ -0,0 +1,263 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* Copyright (c) 2013-2015, 2017-2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef HEAP_MEM_EXT_SERVICE_01_H
|
||||
#define HEAP_MEM_EXT_SERVICE_01_H
|
||||
|
||||
#include <linux/soc/qcom/qmi.h>
|
||||
|
||||
#define MEM_ALLOC_REQ_MAX_MSG_LEN_V01 255
|
||||
#define MEM_FREE_REQ_MAX_MSG_LEN_V01 255
|
||||
#define MEM_QUERY_MAX_MSG_LEN_V01 255
|
||||
#define MAX_ARR_CNT_V01 64
|
||||
|
||||
struct dhms_mem_alloc_addr_info_type_v01 {
|
||||
uint64_t phy_addr;
|
||||
uint32_t num_bytes;
|
||||
};
|
||||
|
||||
enum dhms_mem_proc_id_v01 {
|
||||
/* To force a 32 bit signed enum. Do not change or use */
|
||||
DHMS_MEM_PROC_ID_MIN_ENUM_VAL_V01 = -2147483647,
|
||||
/* Request from MPSS processor */
|
||||
DHMS_MEM_PROC_MPSS_V01 = 0,
|
||||
/* Request from ADSP processor */
|
||||
DHMS_MEM_PROC_ADSP_V01 = 1,
|
||||
/* Request from WCNSS processor */
|
||||
DHMS_MEM_PROC_WCNSS_V01 = 2,
|
||||
/* To force a 32 bit signed enum. Do not change or use */
|
||||
DHMS_MEM_PROC_ID_MAX_ENUM_VAL_V01 = 2147483647
|
||||
};
|
||||
|
||||
enum dhms_mem_client_id_v01 {
|
||||
/*To force a 32 bit signed enum. Do not change or use*/
|
||||
DHMS_MEM_CLIENT_ID_MIN_ENUM_VAL_V01 = -2147483647,
|
||||
/* Request from GPS Client */
|
||||
DHMS_MEM_CLIENT_GPS_V01 = 0,
|
||||
/* Invalid Client */
|
||||
DHMS_MEM_CLIENT_INVALID = 1000,
|
||||
/* To force a 32 bit signed enum. Do not change or use */
|
||||
DHMS_MEM_CLIENT_ID_MAX_ENUM_VAL_V01 = 2147483647
|
||||
};
|
||||
|
||||
enum dhms_mem_block_align_enum_v01 {
|
||||
/* To force a 32 bit signed enum. Do not change or use
|
||||
*/
|
||||
DHMS_MEM_BLOCK_ALIGN_ENUM_MIN_ENUM_VAL_V01 = -2147483647,
|
||||
/* Align allocated memory by 2 bytes */
|
||||
DHMS_MEM_BLOCK_ALIGN_2_V01 = 0,
|
||||
/* Align allocated memory by 4 bytes */
|
||||
DHMS_MEM_BLOCK_ALIGN_4_V01 = 1,
|
||||
/**< Align allocated memory by 8 bytes */
|
||||
DHMS_MEM_BLOCK_ALIGN_8_V01 = 2,
|
||||
/**< Align allocated memory by 16 bytes */
|
||||
DHMS_MEM_BLOCK_ALIGN_16_V01 = 3,
|
||||
/**< Align allocated memory by 32 bytes */
|
||||
DHMS_MEM_BLOCK_ALIGN_32_V01 = 4,
|
||||
/**< Align allocated memory by 64 bytes */
|
||||
DHMS_MEM_BLOCK_ALIGN_64_V01 = 5,
|
||||
/**< Align allocated memory by 128 bytes */
|
||||
DHMS_MEM_BLOCK_ALIGN_128_V01 = 6,
|
||||
/**< Align allocated memory by 256 bytes */
|
||||
DHMS_MEM_BLOCK_ALIGN_256_V01 = 7,
|
||||
/**< Align allocated memory by 512 bytes */
|
||||
DHMS_MEM_BLOCK_ALIGN_512_V01 = 8,
|
||||
/**< Align allocated memory by 1024 bytes */
|
||||
DHMS_MEM_BLOCK_ALIGN_1K_V01 = 9,
|
||||
/**< Align allocated memory by 2048 bytes */
|
||||
DHMS_MEM_BLOCK_ALIGN_2K_V01 = 10,
|
||||
/**< Align allocated memory by 4096 bytes */
|
||||
DHMS_MEM_BLOCK_ALIGN_4K_V01 = 11,
|
||||
DHMS_MEM_BLOCK_ALIGN_ENUM_MAX_ENUM_VAL_V01 = 2147483647
|
||||
/* To force a 32 bit signed enum. Do not change or use
|
||||
*/
|
||||
};
|
||||
|
||||
/* Request Message; This command is used for getting
|
||||
* the multiple physically contiguous
|
||||
* memory blocks from the server memory subsystem
|
||||
*/
|
||||
struct mem_alloc_generic_req_msg_v01 {
|
||||
|
||||
/* Mandatory */
|
||||
/*requested size*/
|
||||
uint32_t num_bytes;
|
||||
|
||||
/* Mandatory */
|
||||
/* client id */
|
||||
enum dhms_mem_client_id_v01 client_id;
|
||||
|
||||
/* Mandatory */
|
||||
/* Peripheral Id*/
|
||||
enum dhms_mem_proc_id_v01 proc_id;
|
||||
|
||||
/* Mandatory */
|
||||
/* Sequence id */
|
||||
uint32_t sequence_id;
|
||||
|
||||
/* Optional */
|
||||
/* alloc_contiguous */
|
||||
/* Must be set to true if alloc_contiguous is being passed */
|
||||
uint8_t alloc_contiguous_valid;
|
||||
|
||||
/* Alloc_contiguous is used to identify that clients are requesting
|
||||
* for contiguous or non contiguous memory, default is contiguous
|
||||
* 0 = non contiguous else contiguous
|
||||
*/
|
||||
uint8_t alloc_contiguous;
|
||||
|
||||
/* Optional */
|
||||
/* Must be set to true if block_alignment
|
||||
* is being passed
|
||||
*/
|
||||
uint8_t block_alignment_valid;
|
||||
|
||||
/* The block alignment for the memory block to be allocated
|
||||
*/
|
||||
enum dhms_mem_block_align_enum_v01 block_alignment;
|
||||
|
||||
}; /* Message */
|
||||
|
||||
/* Response Message; This command is used for getting
|
||||
* the multiple physically contiguous memory blocks
|
||||
* from the server memory subsystem
|
||||
*/
|
||||
struct mem_alloc_generic_resp_msg_v01 {
|
||||
|
||||
/* Mandatory */
|
||||
/* Result Code */
|
||||
/* The result of the requested memory operation
|
||||
*/
|
||||
struct qmi_response_type_v01 resp;
|
||||
|
||||
/* Optional */
|
||||
/* Sequence ID */
|
||||
/* Must be set to true if sequence_id is being passed */
|
||||
uint8_t sequence_id_valid;
|
||||
|
||||
|
||||
/* Mandatory */
|
||||
/* Sequence id */
|
||||
uint32_t sequence_id;
|
||||
|
||||
/* Optional */
|
||||
/* Memory Block Handle
|
||||
*/
|
||||
/* Must be set to true if handle is being passed
|
||||
*/
|
||||
uint8_t dhms_mem_alloc_addr_info_valid;
|
||||
|
||||
/* Optional */
|
||||
/* Handle Size */
|
||||
uint32_t dhms_mem_alloc_addr_info_len;
|
||||
|
||||
/* Optional */
|
||||
/* The physical address of the memory allocated on the HLOS
|
||||
*/
|
||||
struct dhms_mem_alloc_addr_info_type_v01
|
||||
dhms_mem_alloc_addr_info[MAX_ARR_CNT_V01];
|
||||
|
||||
}; /* Message */
|
||||
|
||||
/* Request Message; This command is used for releasing
|
||||
* the multiple physically contiguous
|
||||
* memory blocks to the server memory subsystem
|
||||
*/
|
||||
struct mem_free_generic_req_msg_v01 {
|
||||
|
||||
/* Mandatory */
|
||||
/* Must be set to # of elments in array*/
|
||||
uint32_t dhms_mem_alloc_addr_info_len;
|
||||
|
||||
/* Mandatory */
|
||||
/* Physical address and size of the memory allocated
|
||||
* on the HLOS to be freed.
|
||||
*/
|
||||
struct dhms_mem_alloc_addr_info_type_v01
|
||||
dhms_mem_alloc_addr_info[MAX_ARR_CNT_V01];
|
||||
|
||||
/* Optional */
|
||||
/* Client ID */
|
||||
/* Must be set to true if client_id is being passed */
|
||||
uint8_t client_id_valid;
|
||||
|
||||
/* Optional */
|
||||
/* Client Id */
|
||||
enum dhms_mem_client_id_v01 client_id;
|
||||
|
||||
/* Optional */
|
||||
/* Proc ID */
|
||||
/* Must be set to true if proc_id is being passed */
|
||||
uint8_t proc_id_valid;
|
||||
|
||||
/* Optional */
|
||||
/* Peripheral */
|
||||
enum dhms_mem_proc_id_v01 proc_id;
|
||||
|
||||
}; /* Message */
|
||||
|
||||
/* Response Message; This command is used for releasing
|
||||
* the multiple physically contiguous
|
||||
* memory blocks to the server memory subsystem
|
||||
*/
|
||||
struct mem_free_generic_resp_msg_v01 {
|
||||
|
||||
/*
|
||||
* Mandatory
|
||||
* Result of the requested memory operation, todo,
|
||||
* need to check the async operation for free
|
||||
*/
|
||||
struct qmi_response_type_v01 resp;
|
||||
|
||||
}; /* Message */
|
||||
|
||||
struct mem_query_size_req_msg_v01 {
|
||||
|
||||
/* Mandatory */
|
||||
enum dhms_mem_client_id_v01 client_id;
|
||||
|
||||
/*
|
||||
* Optional
|
||||
* Proc ID
|
||||
* proc_id_valid must be set to true if proc_id is being passed
|
||||
*/
|
||||
uint8_t proc_id_valid;
|
||||
|
||||
enum dhms_mem_proc_id_v01 proc_id;
|
||||
}; /* Message */
|
||||
|
||||
struct mem_query_size_rsp_msg_v01 {
|
||||
|
||||
/*
|
||||
* Mandatory
|
||||
* Result Code
|
||||
*/
|
||||
struct qmi_response_type_v01 resp;
|
||||
|
||||
/*
|
||||
* Optional
|
||||
* size_valid must be set to true if size is being passed
|
||||
*/
|
||||
uint8_t size_valid;
|
||||
|
||||
uint32_t size;
|
||||
}; /* Message */
|
||||
|
||||
|
||||
extern struct qmi_elem_info mem_alloc_generic_req_msg_data_v01_ei[];
|
||||
extern struct qmi_elem_info mem_alloc_generic_resp_msg_data_v01_ei[];
|
||||
extern struct qmi_elem_info mem_free_generic_req_msg_data_v01_ei[];
|
||||
extern struct qmi_elem_info mem_free_generic_resp_msg_data_v01_ei[];
|
||||
extern struct qmi_elem_info mem_query_size_req_msg_data_v01_ei[];
|
||||
extern struct qmi_elem_info mem_query_size_resp_msg_data_v01_ei[];
|
||||
|
||||
/*Service Message Definition*/
|
||||
#define MEM_ALLOC_GENERIC_REQ_MSG_V01 0x0022
|
||||
#define MEM_ALLOC_GENERIC_RESP_MSG_V01 0x0022
|
||||
#define MEM_FREE_GENERIC_REQ_MSG_V01 0x0023
|
||||
#define MEM_FREE_GENERIC_RESP_MSG_V01 0x0023
|
||||
#define MEM_QUERY_SIZE_REQ_MSG_V01 0x0024
|
||||
#define MEM_QUERY_SIZE_RESP_MSG_V01 0x0024
|
||||
|
||||
#endif
|
||||
955
drivers/soc/qcom/memshare/msm_memshare.c
Normal file
955
drivers/soc/qcom/memshare/msm_memshare.c
Normal file
|
|
@ -0,0 +1,955 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/* Copyright (c) 2013-2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/soc/qcom/qmi.h>
|
||||
#include <soc/qcom/subsystem_restart.h>
|
||||
#include <soc/qcom/subsystem_notif.h>
|
||||
#include "msm_memshare.h"
|
||||
#include "heap_mem_ext_v01.h"
|
||||
|
||||
#include <soc/qcom/secure_buffer.h>
|
||||
#include <soc/qcom/ramdump.h>
|
||||
|
||||
/* Macros */
|
||||
#define MEMSHARE_DEV_NAME "memshare"
|
||||
#define MEMSHARE_CHILD_DEV_NAME "memshare_child"
|
||||
static unsigned long(attrs);
|
||||
|
||||
static struct qmi_handle *mem_share_svc_handle;
|
||||
static struct workqueue_struct *mem_share_svc_workqueue;
|
||||
static uint64_t bootup_request;
|
||||
static bool ramdump_event;
|
||||
static void *memshare_ramdump_dev[MAX_CLIENTS];
|
||||
static struct device *memshare_dev[MAX_CLIENTS];
|
||||
|
||||
/* Memshare Driver Structure */
|
||||
struct memshare_driver {
|
||||
struct device *dev;
|
||||
struct mutex mem_share;
|
||||
struct mutex mem_free;
|
||||
struct work_struct memshare_init_work;
|
||||
};
|
||||
|
||||
struct memshare_child {
|
||||
struct device *dev;
|
||||
};
|
||||
|
||||
static struct memshare_driver *memsh_drv;
|
||||
static struct memshare_child *memsh_child;
|
||||
static struct mem_blocks memblock[MAX_CLIENTS];
|
||||
static uint32_t num_clients;
|
||||
|
||||
/*
|
||||
* This API creates ramdump dev handlers
|
||||
* for each of the memshare clients.
|
||||
* These dev handlers will be used for
|
||||
* extracting the ramdump for loaned memory
|
||||
* segments.
|
||||
*/
|
||||
|
||||
static int mem_share_configure_ramdump(int client)
|
||||
{
|
||||
char client_name[18];
|
||||
const char *clnt = NULL;
|
||||
|
||||
switch (client) {
|
||||
case 0:
|
||||
clnt = "GPS";
|
||||
break;
|
||||
case 1:
|
||||
clnt = "FTM";
|
||||
break;
|
||||
case 2:
|
||||
clnt = "DIAG";
|
||||
break;
|
||||
default:
|
||||
dev_err(memsh_child->dev, "memshare: no memshare clients registered\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
snprintf(client_name, sizeof(client_name),
|
||||
"memshare_%s", clnt);
|
||||
if (memshare_dev[client]) {
|
||||
memshare_ramdump_dev[client] =
|
||||
create_ramdump_device(client_name,
|
||||
memshare_dev[client]);
|
||||
} else {
|
||||
dev_err(memsh_child->dev,
|
||||
"memshare: invalid memshare device for creating ramdump device\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
if (IS_ERR_OR_NULL(memshare_ramdump_dev[client])) {
|
||||
dev_err(memsh_child->dev,
|
||||
"memshare: unable to create memshare ramdump device\n");
|
||||
memshare_ramdump_dev[client] = NULL;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int check_client(int client_id, int proc, int request)
|
||||
{
|
||||
int i = 0, rc;
|
||||
int found = DHMS_MEM_CLIENT_INVALID;
|
||||
|
||||
for (i = 0; i < MAX_CLIENTS; i++) {
|
||||
if (memblock[i].client_id == client_id &&
|
||||
memblock[i].peripheral == proc) {
|
||||
found = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ((found == DHMS_MEM_CLIENT_INVALID) && !request) {
|
||||
dev_dbg(memsh_child->dev,
|
||||
"memshare: No registered client for the client_id: %d, adding a new client\n",
|
||||
client_id);
|
||||
/* Add a new client */
|
||||
for (i = 0; i < MAX_CLIENTS; i++) {
|
||||
if (memblock[i].client_id == DHMS_MEM_CLIENT_INVALID) {
|
||||
memblock[i].client_id = client_id;
|
||||
memblock[i].allotted = 0;
|
||||
memblock[i].guarantee = 0;
|
||||
memblock[i].peripheral = proc;
|
||||
found = i;
|
||||
|
||||
if (!memblock[i].file_created) {
|
||||
rc = mem_share_configure_ramdump(i);
|
||||
if (rc)
|
||||
dev_err(memsh_child->dev,
|
||||
"memshare_check_client: cannot create ramdump for client with id: %d\n",
|
||||
client_id);
|
||||
else
|
||||
memblock[i].file_created = 1;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
static void free_client(int id)
|
||||
{
|
||||
memblock[id].phy_addr = 0;
|
||||
memblock[id].virtual_addr = 0;
|
||||
memblock[id].allotted = 0;
|
||||
memblock[id].guarantee = 0;
|
||||
memblock[id].sequence_id = -1;
|
||||
memblock[id].memory_type = MEMORY_CMA;
|
||||
|
||||
}
|
||||
|
||||
static void fill_alloc_response(struct mem_alloc_generic_resp_msg_v01 *resp,
|
||||
int id, int *flag)
|
||||
{
|
||||
resp->sequence_id_valid = 1;
|
||||
resp->sequence_id = memblock[id].sequence_id;
|
||||
resp->dhms_mem_alloc_addr_info_valid = 1;
|
||||
resp->dhms_mem_alloc_addr_info_len = 1;
|
||||
resp->dhms_mem_alloc_addr_info[0].phy_addr = memblock[id].phy_addr;
|
||||
resp->dhms_mem_alloc_addr_info[0].num_bytes = memblock[id].size;
|
||||
if (!*flag) {
|
||||
resp->resp.result = QMI_RESULT_SUCCESS_V01;
|
||||
resp->resp.error = QMI_ERR_NONE_V01;
|
||||
} else {
|
||||
resp->resp.result = QMI_RESULT_FAILURE_V01;
|
||||
resp->resp.error = QMI_ERR_NO_MEMORY_V01;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void initialize_client(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_CLIENTS; i++) {
|
||||
memblock[i].allotted = 0;
|
||||
memblock[i].size = 0;
|
||||
memblock[i].guarantee = 0;
|
||||
memblock[i].phy_addr = 0;
|
||||
memblock[i].virtual_addr = 0;
|
||||
memblock[i].client_id = DHMS_MEM_CLIENT_INVALID;
|
||||
memblock[i].peripheral = -1;
|
||||
memblock[i].sequence_id = -1;
|
||||
memblock[i].memory_type = MEMORY_CMA;
|
||||
memblock[i].free_memory = 0;
|
||||
memblock[i].hyp_mapping = 0;
|
||||
memblock[i].file_created = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* mem_share_do_ramdump() function initializes the
|
||||
* ramdump segments with the physical address and
|
||||
* size of the memshared clients. Extraction of ramdump
|
||||
* is skipped if memshare client is not allotted
|
||||
* This calls the ramdump api in extracting the
|
||||
* ramdump in elf format.
|
||||
*/
|
||||
|
||||
static int mem_share_do_ramdump(void)
|
||||
{
|
||||
int i = 0, ret;
|
||||
char *client_name = NULL;
|
||||
u32 source_vmlist[1] = {VMID_MSS_MSA};
|
||||
int dest_vmids[1] = {VMID_HLOS};
|
||||
int dest_perms[1] = {PERM_READ|PERM_WRITE|PERM_EXEC};
|
||||
|
||||
for (i = 0; i < num_clients; i++) {
|
||||
|
||||
struct ramdump_segment *ramdump_segments_tmp = NULL;
|
||||
|
||||
switch (i) {
|
||||
case 0:
|
||||
client_name = "GPS";
|
||||
break;
|
||||
case 1:
|
||||
client_name = "FTM";
|
||||
break;
|
||||
case 2:
|
||||
client_name = "DIAG";
|
||||
break;
|
||||
default:
|
||||
dev_err(memsh_child->dev,
|
||||
"memshare: no memshare clients registered for client_id: %d\n",
|
||||
i);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!memblock[i].allotted) {
|
||||
dev_err(memsh_child->dev, "memshare: %s: memblock is not allotted\n",
|
||||
client_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (memblock[i].hyp_mapping &&
|
||||
memblock[i].peripheral ==
|
||||
DHMS_MEM_PROC_MPSS_V01) {
|
||||
dev_dbg(memsh_child->dev,
|
||||
"memshare: %s: hypervisor unmapping for client before elf dump\n",
|
||||
client_name);
|
||||
if (memblock[i].alloc_request)
|
||||
continue;
|
||||
ret = hyp_assign_phys(
|
||||
memblock[i].phy_addr,
|
||||
memblock[i].size,
|
||||
source_vmlist,
|
||||
1, dest_vmids,
|
||||
dest_perms, 1);
|
||||
if (ret) {
|
||||
/*
|
||||
* This is an error case as hyp
|
||||
* mapping was successful
|
||||
* earlier but during unmap
|
||||
* it lead to failure.
|
||||
*/
|
||||
dev_err(memsh_child->dev,
|
||||
"memshare: %s: failed to map the memory region to APPS\n",
|
||||
client_name);
|
||||
continue;
|
||||
} else {
|
||||
memblock[i].hyp_mapping = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!memblock[i].hyp_mapping) {
|
||||
ramdump_segments_tmp = kcalloc(1,
|
||||
sizeof(struct ramdump_segment),
|
||||
GFP_KERNEL);
|
||||
if (!ramdump_segments_tmp)
|
||||
return -ENOMEM;
|
||||
|
||||
ramdump_segments_tmp[0].size = memblock[i].size;
|
||||
ramdump_segments_tmp[0].v_address =
|
||||
memblock[i].virtual_addr;
|
||||
|
||||
dev_dbg(memsh_child->dev, "memshare: %s: Begin elf dump for size = %d\n",
|
||||
client_name, memblock[i].size);
|
||||
|
||||
ret = do_elf_ramdump(memshare_ramdump_dev[i],
|
||||
ramdump_segments_tmp, 1);
|
||||
kfree(ramdump_segments_tmp);
|
||||
if (ret < 0) {
|
||||
dev_err(memsh_child->dev,
|
||||
"memshare: %s: Unable to elf dump with failure: %d\n",
|
||||
client_name, ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int modem_notifier_cb(struct notifier_block *this, unsigned long code,
|
||||
void *_cmd)
|
||||
{
|
||||
int i, ret, size = 0;
|
||||
u32 source_vmlist[1] = {VMID_MSS_MSA};
|
||||
int dest_vmids[1] = {VMID_HLOS};
|
||||
int dest_perms[1] = {PERM_READ|PERM_WRITE|PERM_EXEC};
|
||||
struct notif_data *notifdata = NULL;
|
||||
|
||||
mutex_lock(&memsh_drv->mem_share);
|
||||
|
||||
switch (code) {
|
||||
|
||||
case SUBSYS_BEFORE_SHUTDOWN:
|
||||
bootup_request++;
|
||||
for (i = 0; i < MAX_CLIENTS; i++)
|
||||
memblock[i].alloc_request = 0;
|
||||
break;
|
||||
|
||||
case SUBSYS_RAMDUMP_NOTIFICATION:
|
||||
ramdump_event = true;
|
||||
break;
|
||||
|
||||
case SUBSYS_BEFORE_POWERUP:
|
||||
if (_cmd) {
|
||||
notifdata = (struct notif_data *) _cmd;
|
||||
} else {
|
||||
ramdump_event = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (notifdata->enable_ramdump && ramdump_event) {
|
||||
dev_info(memsh_child->dev, "memshare: Ramdump collection is enabled\n");
|
||||
ret = mem_share_do_ramdump();
|
||||
if (ret)
|
||||
dev_err(memsh_child->dev, "memshare: Ramdump collection failed\n");
|
||||
ramdump_event = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case SUBSYS_AFTER_POWERUP:
|
||||
dev_dbg(memsh_child->dev, "memshare: Modem has booted up\n");
|
||||
for (i = 0; i < MAX_CLIENTS; i++) {
|
||||
size = memblock[i].size;
|
||||
if (memblock[i].free_memory > 0 &&
|
||||
bootup_request >= 2) {
|
||||
memblock[i].free_memory -= 1;
|
||||
dev_dbg(memsh_child->dev, "memshare: free_memory count: %d for client id: %d\n",
|
||||
memblock[i].free_memory,
|
||||
memblock[i].client_id);
|
||||
}
|
||||
|
||||
if (memblock[i].free_memory == 0 &&
|
||||
memblock[i].peripheral ==
|
||||
DHMS_MEM_PROC_MPSS_V01 &&
|
||||
!memblock[i].guarantee &&
|
||||
!memblock[i].client_request &&
|
||||
memblock[i].allotted &&
|
||||
!memblock[i].alloc_request) {
|
||||
dev_info(memsh_child->dev,
|
||||
"memshare: hypervisor unmapping for allocated memory with client id: %d\n",
|
||||
memblock[i].client_id);
|
||||
if (memblock[i].hyp_mapping) {
|
||||
ret = hyp_assign_phys(
|
||||
memblock[i].phy_addr,
|
||||
memblock[i].size,
|
||||
source_vmlist,
|
||||
1, dest_vmids,
|
||||
dest_perms, 1);
|
||||
if (ret &&
|
||||
memblock[i].hyp_mapping == 1) {
|
||||
/*
|
||||
* This is an error case as hyp
|
||||
* mapping was successful
|
||||
* earlier but during unmap
|
||||
* it lead to failure.
|
||||
*/
|
||||
dev_err(memsh_child->dev,
|
||||
"memshare: failed to hypervisor unmap the memory region for client id: %d\n",
|
||||
memblock[i].client_id);
|
||||
} else {
|
||||
memblock[i].hyp_mapping = 0;
|
||||
}
|
||||
}
|
||||
if (memblock[i].guard_band) {
|
||||
/*
|
||||
* Check if the client required guard band
|
||||
* support so the memory region of client's
|
||||
* size + guard bytes of 4K can be freed.
|
||||
*/
|
||||
size += MEMSHARE_GUARD_BYTES;
|
||||
}
|
||||
dma_free_attrs(memsh_drv->dev,
|
||||
size, memblock[i].virtual_addr,
|
||||
memblock[i].phy_addr,
|
||||
attrs);
|
||||
free_client(i);
|
||||
}
|
||||
}
|
||||
bootup_request++;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
mutex_unlock(&memsh_drv->mem_share);
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static struct notifier_block nb = {
|
||||
.notifier_call = modem_notifier_cb,
|
||||
};
|
||||
|
||||
static void shared_hyp_mapping(int client_id)
|
||||
{
|
||||
int ret;
|
||||
u32 source_vmlist[1] = {VMID_HLOS};
|
||||
int dest_vmids[1] = {VMID_MSS_MSA};
|
||||
int dest_perms[1] = {PERM_READ|PERM_WRITE};
|
||||
|
||||
if (client_id == DHMS_MEM_CLIENT_INVALID) {
|
||||
dev_err(memsh_child->dev,
|
||||
"memshare: hypervisor mapping failure for invalid client\n");
|
||||
return;
|
||||
}
|
||||
|
||||
ret = hyp_assign_phys(memblock[client_id].phy_addr,
|
||||
memblock[client_id].size,
|
||||
source_vmlist, 1, dest_vmids,
|
||||
dest_perms, 1);
|
||||
|
||||
if (ret != 0) {
|
||||
dev_err(memsh_child->dev, "memshare: hyp_assign_phys failed size=%u err=%d\n",
|
||||
memblock[client_id].size, ret);
|
||||
return;
|
||||
}
|
||||
memblock[client_id].hyp_mapping = 1;
|
||||
}
|
||||
|
||||
static void handle_alloc_generic_req(struct qmi_handle *handle,
|
||||
struct sockaddr_qrtr *sq, struct qmi_txn *txn, const void *decoded_msg)
|
||||
{
|
||||
struct mem_alloc_generic_req_msg_v01 *alloc_req;
|
||||
struct mem_alloc_generic_resp_msg_v01 *alloc_resp;
|
||||
int rc, resp = 0;
|
||||
int client_id;
|
||||
uint32_t size = 0;
|
||||
|
||||
mutex_lock(&memsh_drv->mem_share);
|
||||
alloc_req = (struct mem_alloc_generic_req_msg_v01 *)decoded_msg;
|
||||
dev_info(memsh_child->dev,
|
||||
"memshare_alloc: memory alloc request received for client id: %d, proc_id: %d, request size: %d\n",
|
||||
alloc_req->client_id, alloc_req->proc_id, alloc_req->num_bytes);
|
||||
alloc_resp = kzalloc(sizeof(*alloc_resp),
|
||||
GFP_KERNEL);
|
||||
if (!alloc_resp) {
|
||||
mutex_unlock(&memsh_drv->mem_share);
|
||||
return;
|
||||
}
|
||||
alloc_resp->resp.result = QMI_RESULT_FAILURE_V01;
|
||||
alloc_resp->resp.error = QMI_ERR_NO_MEMORY_V01;
|
||||
client_id = check_client(alloc_req->client_id, alloc_req->proc_id,
|
||||
CHECK);
|
||||
|
||||
if (client_id >= MAX_CLIENTS) {
|
||||
dev_err(memsh_child->dev,
|
||||
"memshare_alloc: client not found, requested client: %d, proc_id: %d\n",
|
||||
alloc_req->client_id, alloc_req->proc_id);
|
||||
kfree(alloc_resp);
|
||||
alloc_resp = NULL;
|
||||
mutex_unlock(&memsh_drv->mem_share);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!memblock[client_id].allotted) {
|
||||
if (memblock[client_id].guard_band && alloc_req->num_bytes > 0)
|
||||
size = alloc_req->num_bytes + MEMSHARE_GUARD_BYTES;
|
||||
else
|
||||
size = alloc_req->num_bytes;
|
||||
rc = memshare_alloc(memsh_drv->dev, size,
|
||||
&memblock[client_id]);
|
||||
if (rc) {
|
||||
dev_err(memsh_child->dev,
|
||||
"memshare_alloc: unable to allocate memory of size: %d for requested client\n",
|
||||
size);
|
||||
resp = 1;
|
||||
}
|
||||
if (!resp) {
|
||||
memblock[client_id].free_memory += 1;
|
||||
memblock[client_id].allotted = 1;
|
||||
memblock[client_id].size = alloc_req->num_bytes;
|
||||
memblock[client_id].peripheral = alloc_req->proc_id;
|
||||
}
|
||||
}
|
||||
dev_dbg(memsh_child->dev,
|
||||
"memshare_alloc: free memory count for client id: %d = %d\n",
|
||||
memblock[client_id].client_id, memblock[client_id].free_memory);
|
||||
|
||||
memblock[client_id].sequence_id = alloc_req->sequence_id;
|
||||
memblock[client_id].alloc_request = 1;
|
||||
|
||||
fill_alloc_response(alloc_resp, client_id, &resp);
|
||||
/*
|
||||
* Perform the Hypervisor mapping in order to avoid XPU viloation
|
||||
* to the allocated region for Modem Clients
|
||||
*/
|
||||
if (!memblock[client_id].hyp_mapping &&
|
||||
memblock[client_id].allotted)
|
||||
shared_hyp_mapping(client_id);
|
||||
mutex_unlock(&memsh_drv->mem_share);
|
||||
dev_info(memsh_child->dev,
|
||||
"memshare_alloc: client_id: %d, alloc_resp.num_bytes: %d, alloc_resp.resp.result: %lx\n",
|
||||
alloc_req->client_id,
|
||||
alloc_resp->dhms_mem_alloc_addr_info[0].num_bytes,
|
||||
(unsigned long)alloc_resp->resp.result);
|
||||
|
||||
rc = qmi_send_response(mem_share_svc_handle, sq, txn,
|
||||
MEM_ALLOC_GENERIC_RESP_MSG_V01,
|
||||
sizeof(struct mem_alloc_generic_resp_msg_v01),
|
||||
mem_alloc_generic_resp_msg_data_v01_ei, alloc_resp);
|
||||
if (rc < 0)
|
||||
dev_err(memsh_child->dev,
|
||||
"memshare_alloc: Error sending the alloc response: %d\n",
|
||||
rc);
|
||||
|
||||
kfree(alloc_resp);
|
||||
alloc_resp = NULL;
|
||||
}
|
||||
|
||||
static void handle_free_generic_req(struct qmi_handle *handle,
|
||||
struct sockaddr_qrtr *sq, struct qmi_txn *txn, const void *decoded_msg)
|
||||
{
|
||||
struct mem_free_generic_req_msg_v01 *free_req;
|
||||
struct mem_free_generic_resp_msg_v01 free_resp;
|
||||
int rc, flag = 0, ret = 0, size = 0;
|
||||
uint32_t client_id;
|
||||
u32 source_vmlist[1] = {VMID_MSS_MSA};
|
||||
int dest_vmids[1] = {VMID_HLOS};
|
||||
int dest_perms[1] = {PERM_READ|PERM_WRITE|PERM_EXEC};
|
||||
|
||||
mutex_lock(&memsh_drv->mem_free);
|
||||
free_req = (struct mem_free_generic_req_msg_v01 *)decoded_msg;
|
||||
memset(&free_resp, 0, sizeof(free_resp));
|
||||
free_resp.resp.error = QMI_ERR_INTERNAL_V01;
|
||||
free_resp.resp.result = QMI_RESULT_FAILURE_V01;
|
||||
dev_info(memsh_child->dev,
|
||||
"memshare_free: handling memory free request with client id: %d, proc_id: %d\n",
|
||||
free_req->client_id, free_req->proc_id);
|
||||
client_id = check_client(free_req->client_id, free_req->proc_id, FREE);
|
||||
if (client_id == DHMS_MEM_CLIENT_INVALID) {
|
||||
dev_err(memsh_child->dev, "memshare_free: invalid client request to free memory\n");
|
||||
flag = 1;
|
||||
} else if (!memblock[client_id].guarantee &&
|
||||
!memblock[client_id].client_request &&
|
||||
memblock[client_id].allotted) {
|
||||
dev_dbg(memsh_child->dev,
|
||||
"memshare_free: hypervisor unmapping for client_id:%d - size: %d\n",
|
||||
client_id, memblock[client_id].size);
|
||||
ret = hyp_assign_phys(memblock[client_id].phy_addr,
|
||||
memblock[client_id].size, source_vmlist, 1,
|
||||
dest_vmids, dest_perms, 1);
|
||||
if (ret && memblock[client_id].hyp_mapping == 1) {
|
||||
/*
|
||||
* This is an error case as hyp mapping was successful
|
||||
* earlier but during unmap it lead to failure.
|
||||
*/
|
||||
dev_err(memsh_child->dev,
|
||||
"memshare_free: failed to unmap the region for client id:%d\n",
|
||||
client_id);
|
||||
}
|
||||
size = memblock[client_id].size;
|
||||
if (memblock[client_id].guard_band) {
|
||||
/*
|
||||
* Check if the client required guard band support so
|
||||
* the memory region of client's size + guard
|
||||
* bytes of 4K can be freed
|
||||
*/
|
||||
size += MEMSHARE_GUARD_BYTES;
|
||||
}
|
||||
dma_free_attrs(memsh_drv->dev, size,
|
||||
memblock[client_id].virtual_addr,
|
||||
memblock[client_id].phy_addr,
|
||||
attrs);
|
||||
free_client(client_id);
|
||||
} else {
|
||||
dev_err(memsh_child->dev,
|
||||
"memshare_free: cannot free the memory for a guaranteed client (client_id: %d)\n",
|
||||
client_id);
|
||||
}
|
||||
|
||||
if (flag) {
|
||||
free_resp.resp.result = QMI_RESULT_FAILURE_V01;
|
||||
free_resp.resp.error = QMI_ERR_INVALID_ID_V01;
|
||||
} else {
|
||||
free_resp.resp.result = QMI_RESULT_SUCCESS_V01;
|
||||
free_resp.resp.error = QMI_ERR_NONE_V01;
|
||||
}
|
||||
|
||||
mutex_unlock(&memsh_drv->mem_free);
|
||||
rc = qmi_send_response(mem_share_svc_handle, sq, txn,
|
||||
MEM_FREE_GENERIC_RESP_MSG_V01,
|
||||
MEM_FREE_REQ_MAX_MSG_LEN_V01,
|
||||
mem_free_generic_resp_msg_data_v01_ei, &free_resp);
|
||||
if (rc < 0)
|
||||
dev_err(memsh_child->dev,
|
||||
"memshare_free: error sending the free response: %d\n", rc);
|
||||
|
||||
}
|
||||
|
||||
static void handle_query_size_req(struct qmi_handle *handle,
|
||||
struct sockaddr_qrtr *sq, struct qmi_txn *txn, const void *decoded_msg)
|
||||
{
|
||||
int rc, client_id;
|
||||
struct mem_query_size_req_msg_v01 *query_req;
|
||||
struct mem_query_size_rsp_msg_v01 *query_resp;
|
||||
|
||||
mutex_lock(&memsh_drv->mem_share);
|
||||
query_req = (struct mem_query_size_req_msg_v01 *)decoded_msg;
|
||||
query_resp = kzalloc(sizeof(*query_resp),
|
||||
GFP_KERNEL);
|
||||
if (!query_resp) {
|
||||
mutex_unlock(&memsh_drv->mem_share);
|
||||
return;
|
||||
}
|
||||
dev_dbg(memsh_child->dev,
|
||||
"memshare_query: query on availalbe memory size for client id: %d, proc_id: %d\n",
|
||||
query_req->client_id, query_req->proc_id);
|
||||
client_id = check_client(query_req->client_id, query_req->proc_id,
|
||||
CHECK);
|
||||
|
||||
if (client_id >= MAX_CLIENTS) {
|
||||
dev_err(memsh_child->dev,
|
||||
"memshare_query: client not found, requested client: %d, proc_id: %d\n",
|
||||
query_req->client_id, query_req->proc_id);
|
||||
kfree(query_resp);
|
||||
query_resp = NULL;
|
||||
mutex_unlock(&memsh_drv->mem_share);
|
||||
return;
|
||||
}
|
||||
|
||||
if (memblock[client_id].init_size) {
|
||||
query_resp->size_valid = 1;
|
||||
query_resp->size = memblock[client_id].init_size;
|
||||
} else {
|
||||
query_resp->size_valid = 1;
|
||||
query_resp->size = 0;
|
||||
}
|
||||
query_resp->resp.result = QMI_RESULT_SUCCESS_V01;
|
||||
query_resp->resp.error = QMI_ERR_NONE_V01;
|
||||
mutex_unlock(&memsh_drv->mem_share);
|
||||
|
||||
dev_info(memsh_child->dev,
|
||||
"memshare_query: client_id : %d, query_resp.size :%d, query_resp.resp.result :%lx\n",
|
||||
query_req->client_id, query_resp->size,
|
||||
(unsigned long)query_resp->resp.result);
|
||||
rc = qmi_send_response(mem_share_svc_handle, sq, txn,
|
||||
MEM_QUERY_SIZE_RESP_MSG_V01,
|
||||
MEM_QUERY_MAX_MSG_LEN_V01,
|
||||
mem_query_size_resp_msg_data_v01_ei, query_resp);
|
||||
if (rc < 0)
|
||||
dev_err(memsh_child->dev,
|
||||
"memshare_query: Error sending the query response: %d\n", rc);
|
||||
|
||||
kfree(query_resp);
|
||||
query_resp = NULL;
|
||||
}
|
||||
|
||||
static void mem_share_svc_disconnect_cb(struct qmi_handle *qmi,
|
||||
unsigned int node, unsigned int port)
|
||||
{
|
||||
}
|
||||
|
||||
static struct qmi_ops server_ops = {
|
||||
.del_client = mem_share_svc_disconnect_cb,
|
||||
};
|
||||
|
||||
static struct qmi_msg_handler qmi_memshare_handlers[] = {
|
||||
{
|
||||
.type = QMI_REQUEST,
|
||||
.msg_id = MEM_ALLOC_GENERIC_REQ_MSG_V01,
|
||||
.ei = mem_alloc_generic_req_msg_data_v01_ei,
|
||||
.decoded_size = MEM_ALLOC_REQ_MAX_MSG_LEN_V01,
|
||||
.fn = handle_alloc_generic_req,
|
||||
},
|
||||
{
|
||||
.type = QMI_REQUEST,
|
||||
.msg_id = MEM_FREE_GENERIC_REQ_MSG_V01,
|
||||
.ei = mem_free_generic_req_msg_data_v01_ei,
|
||||
.decoded_size = MEM_FREE_REQ_MAX_MSG_LEN_V01,
|
||||
.fn = handle_free_generic_req,
|
||||
},
|
||||
{
|
||||
.type = QMI_REQUEST,
|
||||
.msg_id = MEM_QUERY_SIZE_REQ_MSG_V01,
|
||||
.ei = mem_query_size_req_msg_data_v01_ei,
|
||||
.decoded_size = MEM_QUERY_MAX_MSG_LEN_V01,
|
||||
.fn = handle_query_size_req,
|
||||
},
|
||||
};
|
||||
|
||||
int memshare_alloc(struct device *dev,
|
||||
unsigned int block_size,
|
||||
struct mem_blocks *pblk)
|
||||
{
|
||||
dev_dbg(memsh_child->dev,
|
||||
"memshare: allocation request for size: %d\n", block_size);
|
||||
|
||||
if (!pblk) {
|
||||
dev_err(memsh_child->dev,
|
||||
"memshare: Failed memory block allocation\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
pblk->virtual_addr = dma_alloc_attrs(dev, block_size,
|
||||
&pblk->phy_addr, GFP_KERNEL,
|
||||
attrs);
|
||||
if (pblk->virtual_addr == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void memshare_init_worker(struct work_struct *work)
|
||||
{
|
||||
int rc;
|
||||
|
||||
mem_share_svc_workqueue =
|
||||
create_singlethread_workqueue("mem_share_svc");
|
||||
if (!mem_share_svc_workqueue)
|
||||
return;
|
||||
|
||||
mem_share_svc_handle = kzalloc(sizeof(struct qmi_handle),
|
||||
GFP_KERNEL);
|
||||
if (!mem_share_svc_handle) {
|
||||
destroy_workqueue(mem_share_svc_workqueue);
|
||||
return;
|
||||
}
|
||||
|
||||
rc = qmi_handle_init(mem_share_svc_handle,
|
||||
sizeof(struct qmi_elem_info),
|
||||
&server_ops, qmi_memshare_handlers);
|
||||
if (rc < 0) {
|
||||
dev_err(memsh_child->dev,
|
||||
"memshare: Creating mem_share_svc qmi handle failed\n");
|
||||
kfree(mem_share_svc_handle);
|
||||
destroy_workqueue(mem_share_svc_workqueue);
|
||||
return;
|
||||
}
|
||||
rc = qmi_add_server(mem_share_svc_handle, MEM_SHARE_SERVICE_SVC_ID,
|
||||
MEM_SHARE_SERVICE_VERS, MEM_SHARE_SERVICE_INS_ID);
|
||||
if (rc < 0) {
|
||||
dev_err(memsh_child->dev,
|
||||
"memshare: Registering mem share svc failed %d\n", rc);
|
||||
qmi_handle_release(mem_share_svc_handle);
|
||||
kfree(mem_share_svc_handle);
|
||||
destroy_workqueue(mem_share_svc_workqueue);
|
||||
return;
|
||||
}
|
||||
dev_dbg(memsh_child->dev, "memshare: memshare_init successful\n");
|
||||
}
|
||||
|
||||
static int memshare_child_probe(struct platform_device *pdev)
|
||||
{
|
||||
int rc;
|
||||
uint32_t size, client_id;
|
||||
const char *name;
|
||||
struct memshare_child *drv;
|
||||
|
||||
drv = devm_kzalloc(&pdev->dev, sizeof(struct memshare_child),
|
||||
GFP_KERNEL);
|
||||
|
||||
if (!drv)
|
||||
return -ENOMEM;
|
||||
|
||||
drv->dev = &pdev->dev;
|
||||
memsh_child = drv;
|
||||
platform_set_drvdata(pdev, memsh_child);
|
||||
|
||||
rc = of_property_read_u32(pdev->dev.of_node, "qcom,peripheral-size",
|
||||
&size);
|
||||
if (rc) {
|
||||
dev_err(memsh_child->dev, "memshare: Error reading size of clients, rc: %d\n",
|
||||
rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = of_property_read_u32(pdev->dev.of_node, "qcom,client-id",
|
||||
&client_id);
|
||||
if (rc) {
|
||||
dev_err(memsh_child->dev, "memshare: Error reading client id, rc: %d\n",
|
||||
rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
memblock[num_clients].guarantee = of_property_read_bool(
|
||||
pdev->dev.of_node,
|
||||
"qcom,allocate-boot-time");
|
||||
|
||||
memblock[num_clients].client_request = of_property_read_bool(
|
||||
pdev->dev.of_node,
|
||||
"qcom,allocate-on-request");
|
||||
|
||||
memblock[num_clients].guard_band = of_property_read_bool(
|
||||
pdev->dev.of_node,
|
||||
"qcom,guard-band");
|
||||
|
||||
rc = of_property_read_string(pdev->dev.of_node, "label",
|
||||
&name);
|
||||
if (rc) {
|
||||
dev_err(memsh_child->dev, "memshare: Error reading peripheral info for client, rc: %d\n",
|
||||
rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (strcmp(name, "modem") == 0)
|
||||
memblock[num_clients].peripheral = DHMS_MEM_PROC_MPSS_V01;
|
||||
else if (strcmp(name, "adsp") == 0)
|
||||
memblock[num_clients].peripheral = DHMS_MEM_PROC_ADSP_V01;
|
||||
else if (strcmp(name, "wcnss") == 0)
|
||||
memblock[num_clients].peripheral = DHMS_MEM_PROC_WCNSS_V01;
|
||||
|
||||
memblock[num_clients].init_size = size;
|
||||
memblock[num_clients].client_id = client_id;
|
||||
|
||||
/*
|
||||
* Memshare allocation for guaranteed clients
|
||||
*/
|
||||
if (memblock[num_clients].guarantee && size > 0) {
|
||||
if (memblock[num_clients].guard_band)
|
||||
size += MEMSHARE_GUARD_BYTES;
|
||||
rc = memshare_alloc(memsh_child->dev,
|
||||
size,
|
||||
&memblock[num_clients]);
|
||||
if (rc) {
|
||||
dev_err(memsh_child->dev,
|
||||
"memshare_child: Unable to allocate memory for guaranteed clients, rc: %d\n",
|
||||
rc);
|
||||
return rc;
|
||||
}
|
||||
memblock[num_clients].size = size;
|
||||
memblock[num_clients].allotted = 1;
|
||||
shared_hyp_mapping(num_clients);
|
||||
}
|
||||
|
||||
/*
|
||||
* call for creating ramdump dev handlers for
|
||||
* memshare clients
|
||||
*/
|
||||
|
||||
memshare_dev[num_clients] = &pdev->dev;
|
||||
|
||||
if (!memblock[num_clients].file_created) {
|
||||
rc = mem_share_configure_ramdump(num_clients);
|
||||
if (rc)
|
||||
dev_err(memsh_child->dev,
|
||||
"memshare_child: cannot create ramdump for client with id: %d\n",
|
||||
memblock[num_clients].client_id);
|
||||
else
|
||||
memblock[num_clients].file_created = 1;
|
||||
}
|
||||
|
||||
num_clients++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int memshare_probe(struct platform_device *pdev)
|
||||
{
|
||||
int rc;
|
||||
struct memshare_driver *drv;
|
||||
|
||||
drv = devm_kzalloc(&pdev->dev, sizeof(struct memshare_driver),
|
||||
GFP_KERNEL);
|
||||
|
||||
if (!drv)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Memory allocation has been done successfully */
|
||||
mutex_init(&drv->mem_free);
|
||||
mutex_init(&drv->mem_share);
|
||||
|
||||
INIT_WORK(&drv->memshare_init_work, memshare_init_worker);
|
||||
schedule_work(&drv->memshare_init_work);
|
||||
|
||||
drv->dev = &pdev->dev;
|
||||
memsh_drv = drv;
|
||||
platform_set_drvdata(pdev, memsh_drv);
|
||||
initialize_client();
|
||||
num_clients = 0;
|
||||
|
||||
rc = of_platform_populate(pdev->dev.of_node, NULL, NULL,
|
||||
&pdev->dev);
|
||||
|
||||
if (rc) {
|
||||
dev_err(memsh_child->dev,
|
||||
"memshare: error populating the devices\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
subsys_notif_register_notifier("modem", &nb);
|
||||
dev_dbg(memsh_child->dev, "memshare: Memshare inited\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int memshare_remove(struct platform_device *pdev)
|
||||
{
|
||||
if (!memsh_drv)
|
||||
return 0;
|
||||
|
||||
flush_workqueue(mem_share_svc_workqueue);
|
||||
qmi_handle_release(mem_share_svc_handle);
|
||||
kfree(mem_share_svc_handle);
|
||||
destroy_workqueue(mem_share_svc_workqueue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int memshare_child_remove(struct platform_device *pdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id memshare_match_table[] = {
|
||||
{
|
||||
.compatible = "qcom,memshare",
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct of_device_id memshare_match_table1[] = {
|
||||
{
|
||||
.compatible = "qcom,memshare-peripheral",
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
static struct platform_driver memshare_pdriver = {
|
||||
.probe = memshare_probe,
|
||||
.remove = memshare_remove,
|
||||
.driver = {
|
||||
.name = MEMSHARE_DEV_NAME,
|
||||
.of_match_table = memshare_match_table,
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_driver memshare_pchild = {
|
||||
.probe = memshare_child_probe,
|
||||
.remove = memshare_child_remove,
|
||||
.driver = {
|
||||
.name = MEMSHARE_CHILD_DEV_NAME,
|
||||
.of_match_table = memshare_match_table1,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(memshare_pdriver);
|
||||
module_platform_driver(memshare_pchild);
|
||||
|
||||
MODULE_DESCRIPTION("Mem Share QMI Service Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
64
drivers/soc/qcom/memshare/msm_memshare.h
Normal file
64
drivers/soc/qcom/memshare/msm_memshare.h
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* Copyright (c) 2013-2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_MEM_SHARE_H
|
||||
#define _LINUX_MEM_SHARE_H
|
||||
|
||||
#define MEM_SHARE_SERVICE_SVC_ID 0x00000034
|
||||
#define MEM_SHARE_SERVICE_INS_ID 1
|
||||
#define MEM_SHARE_SERVICE_VERS 1
|
||||
|
||||
#define MEMORY_CMA 1
|
||||
#define MEMORY_NON_CMA 0
|
||||
#define MAX_CLIENTS 10
|
||||
#define GPS 0
|
||||
#define CHECK 0
|
||||
#define FREE 1
|
||||
#define MEMSHARE_GUARD_BYTES (4*1024)
|
||||
|
||||
struct mem_blocks {
|
||||
/* Client Id information */
|
||||
uint32_t client_id;
|
||||
/* Peripheral associated with client */
|
||||
uint32_t peripheral;
|
||||
/* Sequence Id */
|
||||
uint32_t sequence_id;
|
||||
/* CMA or Non-CMA region */
|
||||
uint32_t memory_type;
|
||||
/* Guaranteed Memory */
|
||||
uint32_t guarantee;
|
||||
/* Memory alloted or not */
|
||||
uint32_t allotted;
|
||||
/* Memory allocation request received or not */
|
||||
uint32_t alloc_request;
|
||||
/* Allocation on request from a client*/
|
||||
uint32_t client_request;
|
||||
/* Guard band around the allotted region*/
|
||||
uint32_t guard_band;
|
||||
/* Size required for client */
|
||||
uint32_t size;
|
||||
/* Available memory size for client */
|
||||
uint32_t init_size;
|
||||
/*
|
||||
* start address of the memory block reserved by server memory
|
||||
* subsystem to client
|
||||
*/
|
||||
phys_addr_t phy_addr;
|
||||
/* Virtual address for the physical address allocated */
|
||||
void *virtual_addr;
|
||||
/* Release memory only when XPU is released*/
|
||||
uint8_t free_memory;
|
||||
/* Need Hypervisor mapping*/
|
||||
uint8_t hyp_mapping;
|
||||
/* Status flag which checks if ramdump file is created*/
|
||||
int file_created;
|
||||
|
||||
};
|
||||
|
||||
int memshare_alloc(struct device *dev,
|
||||
unsigned int block_size,
|
||||
struct mem_blocks *pblk);
|
||||
void memshare_free(unsigned int block_size,
|
||||
struct mem_blocks *pblk);
|
||||
#endif /* _LINUX_MEM_SHARE_H */
|
||||
Loading…
Reference in a new issue