[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);
+
+
+