[Feature][ZXW-65]merged P49 base code
Change-Id: I3e09c0c3d47483bc645f02310380ecb7fc6f4041
diff --git a/ap/os/linux/linux-3.4.x/ipc/shm_ctrl.c b/ap/os/linux/linux-3.4.x/ipc/shm_ctrl.c
new file mode 100755
index 0000000..598cb28
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/ipc/shm_ctrl.c
@@ -0,0 +1,504 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * linux/ipc/shm_ctrl.c
+ * Copyright (C) 1992, 1993 Krishna Balasubramanian
+ * Replaced `struct shm_desc' by `struct vm_area_struct', July 1994.
+ * Fixed the shm swap deallocation (shm_unuse()), August 1998 Andrea Arcangeli.
+ *
+ * /proc/sysvipc/shm support (c) 1999 Dragos Acostachioaie <dragos@iname.com>
+ * Make shmmax, shmall, shmmni sysctl'able, Christoph Rohland <cr@sap.com>
+ * Move the mm functionality over to mm/shmem.c, Christoph Rohland <cr@sap.com>
+ *
+ * Better ipc lock (kern_ipc_perm.lock) handling
+ * Davidlohr Bueso <davidlohr.bueso@hp.com>, June 2013.
+ */
+#include <linux/mm.h>
+#include <asm/pgtable.h>
+#include "shm_ctrl.h"
+#include "../mm/internal.h"
+
+/**
+ * ºê¶¨Òå
+ */
+#define SHM_UNIT_BUFF_ORDER (12)
+#define SHM_KEYS_STATUS_LEN (4*1024)
+#define SHM_REMOTE_BUFF_LEN (128*1024)
+#define SHM_BUFF_BASE_PHY_ADDR (g_shm_phyAddr)
+
+#define SHM_UNIT_BUFF_SIZE (1UL<<SHM_UNIT_BUFF_ORDER) /*4KB*/
+#define SHM_UNIT_INDEX(addr) (((unsigned long)addr - SHM_BUFF_BASE_PHY_ADDR) >> SHM_UNIT_BUFF_ORDER)
+#define SHM_UNIT_PAGE_ADDR(index) ((void *)(SHM_BUFF_BASE_PHY_ADDR + ((unsigned long)index << SHM_UNIT_BUFF_ORDER)))
+#define SHM_UNIT_NUM_BITS (SHM_REMOTE_BUFF_LEN >> SHM_UNIT_BUFF_ORDER)
+#define SHM_CTRL_BITMAP_NUM (SHM_UNIT_NUM_BITS / SHM_CTRL_LONG_32BIT)
+
+struct shm_key_node {
+ key_t key;
+ unsigned int vma_count;
+ DECLARE_BITMAP(shm_inuse_index, SHM_UNIT_NUM_BITS);
+};
+
+struct shm_entity {
+ DECLARE_BITMAP(shm_regions_bitmap, SHM_UNIT_NUM_BITS);/*×ÜÄÚ´æ³Ø¹ÜÀíÐÅÏ¢*/
+ struct shm_key_node keys_info_head[SHM_UNIT_NUM_BITS]; /*ÿ¸öshmµÄkey¹ÜÀíÐÅÏ¢*/
+};
+
+/**
+ * È«¾Ö±äÁ¿¶¨Òå
+ */
+phys_addr_t g_shm_phyAddr = 0;
+void *g_shm_region = NULL;
+
+struct shm_entity *shm_remote_manager;
+
+/*******************************************************************************
+* ¹¦ÄÜÃèÊö: shm_quary_keyArray
+* ²ÎÊý˵Ã÷:
+* (´«Èë²ÎÊý) void
+* (´«³ö²ÎÊý) void
+* ·µ »Ø Öµ: SHM_CTRL_OK or SHM_CTRL_ERROR
+* ÆäËü˵Ã÷: This function is used for search a special key in array
+*******************************************************************************/
+static int shm_quary_keyArray(const key_t key)
+{
+ unsigned int index = 0;
+ struct shm_key_node *shm_data = NULL;
+
+ shm_data = shm_remote_manager->keys_info_head;
+
+ for (; index < SHM_UNIT_NUM_BITS; index++)
+ {
+ if (shm_data[index].key == key)
+ return index;
+ }
+ return SHM_CTRL_ERROR;
+}
+
+/*******************************************************************************
+* ¹¦ÄÜÃèÊö: shm_ctrl_pte_range
+* ²ÎÊý˵Ã÷:
+* (´«Èë²ÎÊý) void
+* (´«³ö²ÎÊý) void
+* ·µ »Ø Öµ: SHM_CTRL_OK or SHM_CTRL_ERROR
+* ÆäËü˵Ã÷: This function is used for clear the pagetable pte
+*******************************************************************************/
+unsigned long shm_ctrl_pte_range(struct mm_struct *mm,
+ struct vm_area_struct *vma, pmd_t *pmd,
+ unsigned long addr, unsigned long end)
+{
+ spinlock_t *ptl;
+ pte_t *start_pte;
+ pte_t *pte;
+
+ start_pte = pte_offset_map_lock(mm, pmd, addr, &ptl);
+ pte = start_pte;
+ arch_enter_lazy_mmu_mode();
+ do {
+ pte_t ptent = *pte;
+ if (pte_none(ptent)) {
+ continue;
+ }
+
+ if (pte_present(ptent)) {
+
+ ptent = ptep_get_and_clear(mm, addr, pte);
+ }
+ pte_clear_not_present_full(mm, addr, pte, 0);
+ } while (pte++, addr += PAGE_SIZE, addr != end);
+
+ arch_leave_lazy_mmu_mode();
+ pte_unmap_unlock(start_pte, ptl);
+
+ return addr;
+}
+
+/*******************************************************************************
+* ¹¦ÄÜÃèÊö: shm_ctrl_pmd_range
+* ²ÎÊý˵Ã÷:
+* (´«Èë²ÎÊý) mm: ÈÎÎñµÄÄÚ´æÃèÊö·û
+* (´«Èë²ÎÊý) vma£º¿çºË¹²ÏíÄÚ´æ½ø³ÌµØÖ·¿Õ¼ävma
+* (´«Èë²ÎÊý) pud£ºpudÒ³ÉϲãĿ¼
+* (´«Èë²ÎÊý) addr: ÐéÄâÆðʼµØÖ·
+* (´«Èë²ÎÊý) end: ÐéÄâ½áÊøµØÖ·
+* (´«³ö²ÎÊý) ¿Õ
+* ·µ »Ø Öµ: addr
+* ÆäËü˵Ã÷: This function is used for clear the pagetable pte
+*******************************************************************************/
+static inline unsigned long shm_ctrl_pmd_range(struct mm_struct *mm,
+ struct vm_area_struct *vma, pud_t *pud,
+ unsigned long addr, unsigned long end)
+{
+ pmd_t *pmd;
+ unsigned long next;
+
+ pmd = pmd_offset(pud, addr);
+ do {
+ next = pmd_addr_end(addr, end);
+ /*
+ * Here there can be other concurrent MADV_DONTNEED or
+ * trans huge page faults running, and if the pmd is
+ * none or trans huge it can change under us. This is
+ * because MADV_DONTNEED holds the mmap_sem in read
+ * mode.
+ */
+ if (pmd_none_or_trans_huge_or_clear_bad(pmd))
+ goto next;
+ next = shm_ctrl_pte_range(mm, vma, pmd, addr, next);
+next:
+ cond_resched();
+ } while (pmd++, addr = next, addr != end);
+
+ return addr;
+}
+/*******************************************************************************
+* ¹¦ÄÜÃèÊö: shm_ctrl_pud_range
+* ²ÎÊý˵Ã÷:
+* (´«Èë²ÎÊý) mm: ÈÎÎñµÄÄÚ´æÃèÊö·û
+* (´«Èë²ÎÊý) vma: ¿çºË¹²ÏíÄÚ´æ½ø³ÌµØÖ·¿Õ¼ävma
+* (´«Èë²ÎÊý) pgd: pgdҳĿ¼Ïî
+* (´«Èë²ÎÊý) addr: ÐéÄâÆðʼµØÖ·
+* (´«Èë²ÎÊý) end: ÐéÄâ½áÊøµØÖ·
+* (´«³ö²ÎÊý) ÎÞ
+* ·µ »Ø Öµ: SHM_CTRL_OK or SHM_CTRL_ERROR
+* ÆäËü˵Ã÷: This function is used for find pud
+*******************************************************************************/
+static inline unsigned long shm_ctrl_pud_range(struct mm_struct *mm,
+ struct vm_area_struct *vma, pgd_t *pgd,
+ unsigned long addr, unsigned long end)
+{
+ pud_t *pud;
+ unsigned long next;
+
+ pud = pud_offset(pgd, addr);
+ do {
+ next = pud_addr_end(addr, end);
+ if (pud_none_or_clear_bad(pud))
+ continue;
+ next = shm_ctrl_pmd_range(mm, vma, pud, addr, next);
+ } while (pud++, addr = next, addr != end);
+
+ return addr;
+}
+
+/*******************************************************************************
+* ¹¦ÄÜÃèÊö: shm_unmap_page_range
+* ²ÎÊý˵Ã÷:
+* (´«Èë²ÎÊý) mm: ÈÎÎñµÄÄÚ´æÃèÊö·û
+* (´«Èë²ÎÊý) vma ¿çºË¹²ÏíÄÚ´æ½ø³ÌµØÖ·¿Õ¼ävma
+* (´«Èë²ÎÊý) addr ÐéÄâÆðʼµØÖ·
+* (´«Èë²ÎÊý) end ÐéÄâ½áÊøµØÖ·
+* (´«³ö²ÎÊý) ¿Õ
+* ·µ »Ø Öµ: void
+* ÆäËü˵Ã÷: This function is used for unmap the shm memory
+*******************************************************************************/
+void shm_unmap_page_range(struct mm_struct *mm, struct vm_area_struct *vma,
+ unsigned long addr, unsigned long end)
+{
+ pgd_t *pgd;
+ unsigned long next;
+
+ BUG_ON(addr >= end);
+ pgd = pgd_offset(vma->vm_mm, addr);
+ do {
+ next = pgd_addr_end(addr, end);
+ if (pgd_none_or_clear_bad(pgd))
+ continue;
+ next = shm_ctrl_pud_range(mm, vma, pgd, addr, next);
+ } while (pgd++, addr = next, addr != end);
+}
+
+/*******************************************************************************
+* ¹¦ÄÜÃèÊö: shm_vma_write_pagetable
+* ²ÎÊý˵Ã÷:
+* (´«Èë²ÎÊý) void
+* (´«³ö²ÎÊý) void
+* ·µ »Ø Öµ: SHM_CTRL_OK or SHM_CTRL_ERROR
+* ÆäËü˵Ã÷: This function is used for create pagetable for shm mem region
+*******************************************************************************/
+static int shm_vma_write_pagetable(struct vm_area_struct *vma, unsigned long vm_addr,
+ phys_addr_t shmaddr_phy)
+{
+ pte_t *pte;
+ int retval = 0;
+ pte_t pte_val = 0;
+ spinlock_t *ptl;
+
+ if (vm_addr < vma->vm_start || vm_addr >= vma->vm_end)
+ return -EFAULT;
+
+ pte = get_locked_pte(vma->vm_mm, vm_addr, &ptl);
+ if ((!pte) || (!pte_none(*pte)))
+ return -EFAULT;
+
+ pte_val = __pte((phys_addr_t)(shmaddr_phy) | pgprot_val(vma->vm_page_prot));
+
+ set_pte_at(vma->vm_mm, vm_addr, pte, pte_val);
+ pte_unmap_unlock(pte, ptl);
+
+ return retval;
+}
+
+/*******************************************************************************
+* ¹¦ÄÜÃèÊö: shm_fill_keytable
+* ²ÎÊý˵Ã÷:
+* (´«Èë²ÎÊý) void
+* (´«³ö²ÎÊý) void
+* ·µ »Ø Öµ: SHM_CTRL_OK or SHM_CTRL_ERROR
+* ÆäËü˵Ã÷: This function is used for record the key and index relation
+*******************************************************************************/
+int shm_fill_keytable(struct shm_key_node *keydata)
+{
+ unsigned int key_index = 0;
+
+ if (keydata == NULL)
+ return SHM_CTRL_ERROR;
+
+ for(; key_index < SHM_UNIT_NUM_BITS; key_index++)
+ {
+ if(shm_remote_manager->keys_info_head[key_index].key == 0)
+ {
+ memcpy(&shm_remote_manager->keys_info_head[key_index], keydata, sizeof(struct shm_key_node));
+ return SHM_CTRL_OK;
+ }
+ }
+ return SHM_CTRL_ERROR;
+}
+
+/*******************************************************************************
+* ¹¦ÄÜÃèÊö: shm_remove_keynode
+* ²ÎÊý˵Ã÷:
+* (´«Èë²ÎÊý) void
+* (´«³ö²ÎÊý) void
+* ·µ »Ø Öµ: SHM_CTRL_OK or SHM_CTRL_ERROR
+* ÆäËü˵Ã÷: This function is used for remove the key and index relation
+*******************************************************************************/
+static void shm_remove_keynode(unsigned int key_index)
+{
+ memset(&shm_remote_manager->keys_info_head[key_index], 0, sizeof(struct shm_key_node));
+}
+
+/*******************************************************************************
+* ¹¦ÄÜÃèÊö: shm_alloc_new_page
+* ²ÎÊý˵Ã÷:
+* (´«Èë²ÎÊý) void
+* (´«³ö²ÎÊý) void
+* ·µ »Ø Öµ: SHM_CTRL_OK or SHM_CTRL_ERROR
+* ÆäËü˵Ã÷: This function is used for alloc page from shm mem region
+*******************************************************************************/
+int shm_alloc_new_page(struct vm_area_struct *vma, key_t key)
+{
+ unsigned long vm_addr = 0;
+ unsigned int region_index = 0;
+ void *new_page = NULL;
+ struct shm_key_node new_key = {0};
+
+ if((vma == NULL) || (g_shm_region == NULL))
+ {
+ printk("Shm region is not ready\n");
+ return SHM_CTRL_ERROR;
+ }
+
+ vm_addr = vma->vm_start;
+
+ for (; vm_addr < vma->vm_end; vm_addr += PAGE_SIZE)
+ {
+ region_index = find_first_zero_bit(shm_remote_manager->shm_regions_bitmap, SHM_UNIT_NUM_BITS);
+
+ if (region_index < SHM_UNIT_NUM_BITS)
+ {
+ set_bit(region_index, shm_remote_manager->shm_regions_bitmap);
+ new_page = SHM_UNIT_PAGE_ADDR(region_index);
+
+ if (shm_vma_write_pagetable(vma, vm_addr, new_page))
+ {
+ return SHM_CTRL_ERROR;
+ }
+ set_bit(region_index, new_key.shm_inuse_index);
+ }
+ else
+ {
+ return SHM_CTRL_ERROR;
+ }
+ }
+
+ if (!bitmap_empty(new_key.shm_inuse_index, SHM_UNIT_NUM_BITS))
+ {
+ new_key.key = key;
+ new_key.vma_count++;
+ shm_fill_keytable(&new_key);
+ }
+ return SHM_CTRL_OK;
+}
+
+/*******************************************************************************
+* ¹¦ÄÜÃèÊö: shm_do_remote_map_vma
+* ²ÎÊý˵Ã÷:
+* (´«Èë²ÎÊý) void
+* (´«³ö²ÎÊý) void
+* ·µ »Ø Öµ: SHM_CTRL_OK or SHM_CTRL_ERROR
+* ÆäËü˵Ã÷: This function is used for
+*
+/*²éѯkey,Èç¹ûÒÑ·ÖÅä¹ýʹÓÃkey¶ÔÓ¦µÄbitmap, ·ñÔò´Ó×ÜÄÚ´æ³Ø·ÖÅä
+*******************************************************************************/
+int shm_do_remote_map_vma(struct vm_area_struct *vma, key_t key)
+{
+ int ret = 0;
+ unsigned long vm_addr = 0;
+ unsigned int region_index = 0;
+ int key_index = 0;
+ void *new_page_phy = NULL;
+ struct shm_key_node *key_node = NULL;
+ DECLARE_BITMAP(shm_inuse_tmp, SHM_UNIT_NUM_BITS);
+
+ if((vma == NULL) || (g_shm_region == NULL))
+ {
+ printk("shm_do_remote_map_vma:Shm region is not ready\n");
+ return SHM_CTRL_ERROR;
+ }
+
+ /*Ó³ÉävmaΪ·ÇcacheÊôÐÔ*/
+ pgprot_noncached(vma->vm_page_prot);
+
+ soft_spin_lock(SHM_SFLOCK);
+
+ key_index = shm_quary_keyArray(key);
+
+ if (key_index < 0)
+ {
+ ret = shm_alloc_new_page(vma, key);
+ soft_spin_unlock(SHM_SFLOCK);
+ if (ret < 0)
+ panic("shm_alloc_new_page Fail\n");
+ return ret;
+ }
+
+ vm_addr = vma->vm_start;
+
+ if ((0 <= key_index) && (key_index < SHM_UNIT_NUM_BITS))
+ key_node = &shm_remote_manager->keys_info_head[key_index];
+ else
+ panic("key_index out of range: failed\n");
+
+ memcpy(shm_inuse_tmp, key_node->shm_inuse_index, sizeof(shm_inuse_tmp));
+
+ for (; vm_addr < vma->vm_end; vm_addr += PAGE_SIZE)
+ {
+ region_index = find_first_bit(shm_inuse_tmp, SHM_UNIT_NUM_BITS);
+ if (region_index < SHM_UNIT_NUM_BITS)
+ {
+ new_page_phy = SHM_UNIT_PAGE_ADDR(region_index);
+ if (shm_vma_write_pagetable(vma, vm_addr, new_page_phy))
+ {
+ soft_spin_unlock(SHM_SFLOCK);
+ panic("shm_do_remote_map_vma vm_insert_page failed\n");
+ return SHM_CTRL_ERROR;
+ }
+ clear_bit(region_index, shm_inuse_tmp);
+ }
+ else
+ {
+ return SHM_CTRL_ERROR;
+ }
+ }
+ key_node->vma_count++;
+
+ soft_spin_unlock(SHM_SFLOCK);
+ return SHM_CTRL_OK;
+}
+
+/*******************************************************************************
+* ¹¦ÄÜÃèÊö: shm_remote_free_pages
+* ²ÎÊý˵Ã÷:
+* (´«Èë²ÎÊý) void
+* (´«³ö²ÎÊý) void
+* ·µ »Ø Öµ: SHM_CTRL_OK or SHM_CTRL_ERROR
+* ÆäËü˵Ã÷: This function is used for
+*******************************************************************************/
+int shm_remote_free_pages(key_t key)
+{
+ int key_index = 0;
+ unsigned int region_index = 0;
+ struct shm_key_node *key_node = NULL;
+
+ if(g_shm_region == NULL)
+ {
+ printk("shm_remote_free_pages: Shm region is not ready\n");
+ return SHM_CTRL_ERROR;
+ }
+
+ soft_spin_lock(SHM_SFLOCK);
+
+ /*²éѯkey*/
+ key_index = shm_quary_keyArray(key);
+ if(key_index < 0 || key_index >= SHM_UNIT_NUM_BITS)
+ {
+ soft_spin_unlock(SHM_SFLOCK);
+ panic("error\n");
+ }
+
+ /*ÊôÓÚ¿çºËµÄ¾ÍÊͷŵ½³Ø×ÓÀï*/
+ key_node = &shm_remote_manager->keys_info_head[key_index];
+
+ key_node->vma_count--;
+
+ if(key_node->vma_count == 0)
+ {
+ for_each_set_bit(region_index, key_node->shm_inuse_index, SHM_UNIT_NUM_BITS)
+ {
+ clear_bit(region_index, shm_remote_manager->shm_regions_bitmap);
+ }
+ shm_remove_keynode(key_index);
+ }
+ soft_spin_unlock(SHM_SFLOCK);
+ return SHM_CTRL_OK;
+}
+
+/*******************************************************************************
+* ¹¦ÄÜÃèÊö: shm_rpcore_init
+* ²ÎÊý˵Ã÷:
+* (´«Èë²ÎÊý) void
+* (´«³ö²ÎÊý) void
+* ·µ »Ø Öµ:
+* ÆäËü˵Ã÷: This function is used for shm ctrl init
+*******************************************************************************/
+static int __init shm_rpcore_init(void)
+{
+ dma_addr_t dma_phys;
+ dma_addr_t shm_keyInfo_phys;
+ struct shm_pool_msg shm_msg = {0};
+
+ g_shm_region = dma_alloc_coherent(NULL,
+ (size_t)SHM_REMOTE_BUFF_LEN,
+ &dma_phys,
+ GFP_KERNEL);
+ if(!g_shm_region)
+ {
+ panic("g_shm_region NOMEM\n");
+ }
+
+ g_shm_phyAddr = dma_phys;
+
+ shm_remote_manager = dma_alloc_coherent(NULL,
+ (size_t)(SHM_KEYS_STATUS_LEN),
+ &shm_keyInfo_phys,
+ GFP_KERNEL);
+ if(!shm_remote_manager)
+ {
+ panic("shm_remote_manager NOMEM\n");
+ }
+
+ memset(shm_remote_manager, 0, sizeof(struct shm_entity));
+ shm_msg.shm_len = SHM_REMOTE_BUFF_LEN;
+ shm_msg.key_manage_len = SHM_KEYS_STATUS_LEN;
+ shm_msg.shm_memory_phy = g_shm_phyAddr;
+ shm_msg.key_manage_phy = shm_keyInfo_phys;
+
+ memcpy((void*)IRAM_BASE_ADDR_SHM_REMOTE_REGION, &shm_msg, sizeof(struct shm_pool_msg));
+
+ return SHM_CTRL_OK;
+
+}
+
+late_initcall(shm_rpcore_init);
+
+
+