| // 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_POSIX_HASH_CHARS      (26) | 
 | #define SHM_POSIX_HASH_BASE       (62) | 
 | #define SHM_POSIX_HASH_MASK       (0x7FF) | 
 |  | 
 | #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_hash_name_to_key | 
 | * ²ÎÊý˵Ã÷: | 
 | *    (´«Èë²ÎÊý) name: ¹²ÏíÄÚ´æÃû³Æ | 
 | *    (´«Èë²ÎÊý) len: ¹²ÏíÄÚ´æÃû³Æ³¤¶È | 
 | *    (´«³ö²ÎÊý) ÎÞ | 
 | * ·µ »Ø Öµ:      | 
 | * ÆäËü˵Ã÷:     This function is used for calc hash value of the name(-2048~-4096) | 
 | *******************************************************************************/ | 
 | key_t shm_hash_name_to_key(const char *name, int len) | 
 | { | 
 |     int   i        = 0; | 
 |     key_t tmp_key  = 0; | 
 |     key_t hash_key = 0; | 
 |     unsigned long long id = 0; | 
 |  | 
 |     for (; i < len; i++) | 
 |     { | 
 |         if (name[i] >= 'A' && name[i] <= 'Z') | 
 |             id = id*SHM_POSIX_HASH_BASE + name[i]-'A'; | 
 |         else if (name[i] >= 'a' && name[i] <= 'z') | 
 |             id = id*SHM_POSIX_HASH_BASE + name[i]-'a' + SHM_POSIX_HASH_CHARS; | 
 |         else if (name[i] >= '0' && name[i] <= '9') | 
 |             id = id*SHM_POSIX_HASH_BASE + name[i]-'0' + 2*SHM_POSIX_HASH_CHARS; | 
 |     } | 
 |     tmp_key =(id & SHM_POSIX_HASH_MASK) + (SHM_POSIX_HASH_MASK + 1); | 
 |     hash_key = ~tmp_key + 1;  | 
 |     return hash_key; | 
 | } | 
 |  | 
 | /******************************************************************************* | 
 | * ¹¦ÄÜÃèÊö:     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_newseg_check | 
 | * ²ÎÊý˵Ã÷:      | 
 | *    (´«Èë²ÎÊý)    void | 
 | *    (´«³ö²ÎÊý)    void | 
 | * ·µ »Ø Öµ:     SHM_CTRL_OK or SHM_CTRL_ERROR | 
 | * ÆäËü˵Ã÷:     This function is used for check key and len                     | 
 | *******************************************************************************/ | 
 | int shm_do_newseg_check(key_t key, unsigned long len) | 
 | { | 
 |     int                  ret        = 0; | 
 |     int                  key_index  = 0; | 
 |     unsigned int         shm_weight = 0; | 
 |     unsigned int         shm_pages  = 0; | 
 |     struct shm_key_node  *key_node  = NULL; | 
 |  | 
 |     if(g_shm_region == NULL) | 
 |     { | 
 |         printk("shm_do_newsg_check:Shm region is not ready\n"); | 
 |         return SHM_CTRL_ERROR; | 
 |     } | 
 |     soft_spin_lock(SHM_SFLOCK); | 
 |  | 
 |     key_index = shm_quary_keyArray(key); | 
 |  | 
 |     if (key_index < 0)  | 
 |     { | 
 |         soft_spin_unlock(SHM_SFLOCK);     | 
 |         return SHM_CTRL_OK; | 
 |     } | 
 |      | 
 |     if ((0 <= key_index) && (key_index < SHM_UNIT_NUM_BITS)) | 
 |     { | 
 |         key_node = &shm_remote_manager->keys_info_head[key_index]; | 
 |     } | 
 |     else | 
 |     { | 
 |         soft_spin_unlock(SHM_SFLOCK); | 
 |         panic("key_index out of range: failed\n"); | 
 |     } | 
 |  | 
 |     shm_pages  =  PAGE_ALIGN(len) >> PAGE_SHIFT; | 
 |     shm_weight = bitmap_weight(key_node->shm_inuse_index, SHM_UNIT_NUM_BITS); | 
 |     soft_spin_unlock(SHM_SFLOCK); | 
 |  | 
 |     /*APºÍCAP¹²ÏíÄÚ´æ´óСӦƥÅä*/ | 
 |     if(shm_weight != shm_pages)  | 
 |         return -EINVAL; | 
 |     else | 
 |         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 | 
 |     { | 
 |         soft_spin_unlock(SHM_SFLOCK); | 
 |         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); | 
 |  | 
 |  | 
 |  |