blob: 598cb28fa55364d7469d43fa259ca4320f4f9b23 [file] [log] [blame]
lh758261d2023-07-13 05:52:04 -07001// SPDX-License-Identifier: GPL-2.0
2/*
3 * linux/ipc/shm_ctrl.c
4 * Copyright (C) 1992, 1993 Krishna Balasubramanian
5 * Replaced `struct shm_desc' by `struct vm_area_struct', July 1994.
6 * Fixed the shm swap deallocation (shm_unuse()), August 1998 Andrea Arcangeli.
7 *
8 * /proc/sysvipc/shm support (c) 1999 Dragos Acostachioaie <dragos@iname.com>
9 * Make shmmax, shmall, shmmni sysctl'able, Christoph Rohland <cr@sap.com>
10 * Move the mm functionality over to mm/shmem.c, Christoph Rohland <cr@sap.com>
11 *
12 * Better ipc lock (kern_ipc_perm.lock) handling
13 * Davidlohr Bueso <davidlohr.bueso@hp.com>, June 2013.
14 */
15#include <linux/mm.h>
16#include <asm/pgtable.h>
17#include "shm_ctrl.h"
18#include "../mm/internal.h"
19
20/**
21 * ºê¶¨Òå
22 */
23#define SHM_UNIT_BUFF_ORDER (12)
24#define SHM_KEYS_STATUS_LEN (4*1024)
25#define SHM_REMOTE_BUFF_LEN (128*1024)
26#define SHM_BUFF_BASE_PHY_ADDR (g_shm_phyAddr)
27
28#define SHM_UNIT_BUFF_SIZE (1UL<<SHM_UNIT_BUFF_ORDER) /*4KB*/
29#define SHM_UNIT_INDEX(addr) (((unsigned long)addr - SHM_BUFF_BASE_PHY_ADDR) >> SHM_UNIT_BUFF_ORDER)
30#define SHM_UNIT_PAGE_ADDR(index) ((void *)(SHM_BUFF_BASE_PHY_ADDR + ((unsigned long)index << SHM_UNIT_BUFF_ORDER)))
31#define SHM_UNIT_NUM_BITS (SHM_REMOTE_BUFF_LEN >> SHM_UNIT_BUFF_ORDER)
32#define SHM_CTRL_BITMAP_NUM (SHM_UNIT_NUM_BITS / SHM_CTRL_LONG_32BIT)
33
34struct shm_key_node {
35 key_t key;
36 unsigned int vma_count;
37 DECLARE_BITMAP(shm_inuse_index, SHM_UNIT_NUM_BITS);
38};
39
40struct shm_entity {
41 DECLARE_BITMAP(shm_regions_bitmap, SHM_UNIT_NUM_BITS);/*×ÜÄÚ´æ³Ø¹ÜÀíÐÅÏ¢*/
42 struct shm_key_node keys_info_head[SHM_UNIT_NUM_BITS]; /*ÿ¸öshmµÄkey¹ÜÀíÐÅÏ¢*/
43};
44
45/**
46 * È«¾Ö±äÁ¿¶¨Òå
47 */
48phys_addr_t g_shm_phyAddr = 0;
49void *g_shm_region = NULL;
50
51struct shm_entity *shm_remote_manager;
52
53/*******************************************************************************
54* ¹¦ÄÜÃèÊö: shm_quary_keyArray
55* ²ÎÊý˵Ã÷:
56* (´«Èë²ÎÊý) void
57* (´«³ö²ÎÊý) void
58* ·µ »Ø Öµ: SHM_CTRL_OK or SHM_CTRL_ERROR
59* ÆäËü˵Ã÷: This function is used for search a special key in array
60*******************************************************************************/
61static int shm_quary_keyArray(const key_t key)
62{
63 unsigned int index = 0;
64 struct shm_key_node *shm_data = NULL;
65
66 shm_data = shm_remote_manager->keys_info_head;
67
68 for (; index < SHM_UNIT_NUM_BITS; index++)
69 {
70 if (shm_data[index].key == key)
71 return index;
72 }
73 return SHM_CTRL_ERROR;
74}
75
76/*******************************************************************************
77* ¹¦ÄÜÃèÊö: shm_ctrl_pte_range
78* ²ÎÊý˵Ã÷:
79* (´«Èë²ÎÊý) void
80* (´«³ö²ÎÊý) void
81* ·µ »Ø Öµ: SHM_CTRL_OK or SHM_CTRL_ERROR
82* ÆäËü˵Ã÷: This function is used for clear the pagetable pte
83*******************************************************************************/
84unsigned long shm_ctrl_pte_range(struct mm_struct *mm,
85 struct vm_area_struct *vma, pmd_t *pmd,
86 unsigned long addr, unsigned long end)
87{
88 spinlock_t *ptl;
89 pte_t *start_pte;
90 pte_t *pte;
91
92 start_pte = pte_offset_map_lock(mm, pmd, addr, &ptl);
93 pte = start_pte;
94 arch_enter_lazy_mmu_mode();
95 do {
96 pte_t ptent = *pte;
97 if (pte_none(ptent)) {
98 continue;
99 }
100
101 if (pte_present(ptent)) {
102
103 ptent = ptep_get_and_clear(mm, addr, pte);
104 }
105 pte_clear_not_present_full(mm, addr, pte, 0);
106 } while (pte++, addr += PAGE_SIZE, addr != end);
107
108 arch_leave_lazy_mmu_mode();
109 pte_unmap_unlock(start_pte, ptl);
110
111 return addr;
112}
113
114/*******************************************************************************
115* ¹¦ÄÜÃèÊö: shm_ctrl_pmd_range
116* ²ÎÊý˵Ã÷:
117* (´«Èë²ÎÊý) mm: ÈÎÎñµÄÄÚ´æÃèÊö·û
118* (´«Èë²ÎÊý) vma£º¿çºË¹²ÏíÄÚ´æ½ø³ÌµØÖ·¿Õ¼ävma
119* (´«Èë²ÎÊý) pud£ºpudÒ³ÉϲãĿ¼
120* (´«Èë²ÎÊý) addr: ÐéÄâÆðʼµØÖ·
121* (´«Èë²ÎÊý) end: ÐéÄâ½áÊøµØÖ·
122* (´«³ö²ÎÊý) ¿Õ
123* ·µ »Ø Öµ: addr
124* ÆäËü˵Ã÷: This function is used for clear the pagetable pte
125*******************************************************************************/
126static inline unsigned long shm_ctrl_pmd_range(struct mm_struct *mm,
127 struct vm_area_struct *vma, pud_t *pud,
128 unsigned long addr, unsigned long end)
129{
130 pmd_t *pmd;
131 unsigned long next;
132
133 pmd = pmd_offset(pud, addr);
134 do {
135 next = pmd_addr_end(addr, end);
136 /*
137 * Here there can be other concurrent MADV_DONTNEED or
138 * trans huge page faults running, and if the pmd is
139 * none or trans huge it can change under us. This is
140 * because MADV_DONTNEED holds the mmap_sem in read
141 * mode.
142 */
143 if (pmd_none_or_trans_huge_or_clear_bad(pmd))
144 goto next;
145 next = shm_ctrl_pte_range(mm, vma, pmd, addr, next);
146next:
147 cond_resched();
148 } while (pmd++, addr = next, addr != end);
149
150 return addr;
151}
152/*******************************************************************************
153* ¹¦ÄÜÃèÊö: shm_ctrl_pud_range
154* ²ÎÊý˵Ã÷:
155* (´«Èë²ÎÊý) mm: ÈÎÎñµÄÄÚ´æÃèÊö·û
156* (´«Èë²ÎÊý) vma: ¿çºË¹²ÏíÄÚ´æ½ø³ÌµØÖ·¿Õ¼ävma
157* (´«Èë²ÎÊý) pgd: pgdҳĿ¼Ïî
158* (´«Èë²ÎÊý) addr: ÐéÄâÆðʼµØÖ·
159* (´«Èë²ÎÊý) end: ÐéÄâ½áÊøµØÖ·
160* (´«³ö²ÎÊý) ÎÞ
161* ·µ »Ø Öµ: SHM_CTRL_OK or SHM_CTRL_ERROR
162* ÆäËü˵Ã÷: This function is used for find pud
163*******************************************************************************/
164static inline unsigned long shm_ctrl_pud_range(struct mm_struct *mm,
165 struct vm_area_struct *vma, pgd_t *pgd,
166 unsigned long addr, unsigned long end)
167{
168 pud_t *pud;
169 unsigned long next;
170
171 pud = pud_offset(pgd, addr);
172 do {
173 next = pud_addr_end(addr, end);
174 if (pud_none_or_clear_bad(pud))
175 continue;
176 next = shm_ctrl_pmd_range(mm, vma, pud, addr, next);
177 } while (pud++, addr = next, addr != end);
178
179 return addr;
180}
181
182/*******************************************************************************
183* ¹¦ÄÜÃèÊö: shm_unmap_page_range
184* ²ÎÊý˵Ã÷:
185* (´«Èë²ÎÊý) mm: ÈÎÎñµÄÄÚ´æÃèÊö·û
186* (´«Èë²ÎÊý) vma ¿çºË¹²ÏíÄÚ´æ½ø³ÌµØÖ·¿Õ¼ävma
187* (´«Èë²ÎÊý) addr ÐéÄâÆðʼµØÖ·
188* (´«Èë²ÎÊý) end ÐéÄâ½áÊøµØÖ·
189* (´«³ö²ÎÊý) ¿Õ
190* ·µ »Ø Öµ: void
191* ÆäËü˵Ã÷: This function is used for unmap the shm memory
192*******************************************************************************/
193void shm_unmap_page_range(struct mm_struct *mm, struct vm_area_struct *vma,
194 unsigned long addr, unsigned long end)
195{
196 pgd_t *pgd;
197 unsigned long next;
198
199 BUG_ON(addr >= end);
200 pgd = pgd_offset(vma->vm_mm, addr);
201 do {
202 next = pgd_addr_end(addr, end);
203 if (pgd_none_or_clear_bad(pgd))
204 continue;
205 next = shm_ctrl_pud_range(mm, vma, pgd, addr, next);
206 } while (pgd++, addr = next, addr != end);
207}
208
209/*******************************************************************************
210* ¹¦ÄÜÃèÊö: shm_vma_write_pagetable
211* ²ÎÊý˵Ã÷:
212* (´«Èë²ÎÊý) void
213* (´«³ö²ÎÊý) void
214* ·µ »Ø Öµ: SHM_CTRL_OK or SHM_CTRL_ERROR
215* ÆäËü˵Ã÷: This function is used for create pagetable for shm mem region
216*******************************************************************************/
217static int shm_vma_write_pagetable(struct vm_area_struct *vma, unsigned long vm_addr,
218 phys_addr_t shmaddr_phy)
219{
220 pte_t *pte;
221 int retval = 0;
222 pte_t pte_val = 0;
223 spinlock_t *ptl;
224
225 if (vm_addr < vma->vm_start || vm_addr >= vma->vm_end)
226 return -EFAULT;
227
228 pte = get_locked_pte(vma->vm_mm, vm_addr, &ptl);
229 if ((!pte) || (!pte_none(*pte)))
230 return -EFAULT;
231
232 pte_val = __pte((phys_addr_t)(shmaddr_phy) | pgprot_val(vma->vm_page_prot));
233
234 set_pte_at(vma->vm_mm, vm_addr, pte, pte_val);
235 pte_unmap_unlock(pte, ptl);
236
237 return retval;
238}
239
240/*******************************************************************************
241* ¹¦ÄÜÃèÊö: shm_fill_keytable
242* ²ÎÊý˵Ã÷:
243* (´«Èë²ÎÊý) void
244* (´«³ö²ÎÊý) void
245* ·µ »Ø Öµ: SHM_CTRL_OK or SHM_CTRL_ERROR
246* ÆäËü˵Ã÷: This function is used for record the key and index relation
247*******************************************************************************/
248int shm_fill_keytable(struct shm_key_node *keydata)
249{
250 unsigned int key_index = 0;
251
252 if (keydata == NULL)
253 return SHM_CTRL_ERROR;
254
255 for(; key_index < SHM_UNIT_NUM_BITS; key_index++)
256 {
257 if(shm_remote_manager->keys_info_head[key_index].key == 0)
258 {
259 memcpy(&shm_remote_manager->keys_info_head[key_index], keydata, sizeof(struct shm_key_node));
260 return SHM_CTRL_OK;
261 }
262 }
263 return SHM_CTRL_ERROR;
264}
265
266/*******************************************************************************
267* ¹¦ÄÜÃèÊö: shm_remove_keynode
268* ²ÎÊý˵Ã÷:
269* (´«Èë²ÎÊý) void
270* (´«³ö²ÎÊý) void
271* ·µ »Ø Öµ: SHM_CTRL_OK or SHM_CTRL_ERROR
272* ÆäËü˵Ã÷: This function is used for remove the key and index relation
273*******************************************************************************/
274static void shm_remove_keynode(unsigned int key_index)
275{
276 memset(&shm_remote_manager->keys_info_head[key_index], 0, sizeof(struct shm_key_node));
277}
278
279/*******************************************************************************
280* ¹¦ÄÜÃèÊö: shm_alloc_new_page
281* ²ÎÊý˵Ã÷:
282* (´«Èë²ÎÊý) void
283* (´«³ö²ÎÊý) void
284* ·µ »Ø Öµ: SHM_CTRL_OK or SHM_CTRL_ERROR
285* ÆäËü˵Ã÷: This function is used for alloc page from shm mem region
286*******************************************************************************/
287int shm_alloc_new_page(struct vm_area_struct *vma, key_t key)
288{
289 unsigned long vm_addr = 0;
290 unsigned int region_index = 0;
291 void *new_page = NULL;
292 struct shm_key_node new_key = {0};
293
294 if((vma == NULL) || (g_shm_region == NULL))
295 {
296 printk("Shm region is not ready\n");
297 return SHM_CTRL_ERROR;
298 }
299
300 vm_addr = vma->vm_start;
301
302 for (; vm_addr < vma->vm_end; vm_addr += PAGE_SIZE)
303 {
304 region_index = find_first_zero_bit(shm_remote_manager->shm_regions_bitmap, SHM_UNIT_NUM_BITS);
305
306 if (region_index < SHM_UNIT_NUM_BITS)
307 {
308 set_bit(region_index, shm_remote_manager->shm_regions_bitmap);
309 new_page = SHM_UNIT_PAGE_ADDR(region_index);
310
311 if (shm_vma_write_pagetable(vma, vm_addr, new_page))
312 {
313 return SHM_CTRL_ERROR;
314 }
315 set_bit(region_index, new_key.shm_inuse_index);
316 }
317 else
318 {
319 return SHM_CTRL_ERROR;
320 }
321 }
322
323 if (!bitmap_empty(new_key.shm_inuse_index, SHM_UNIT_NUM_BITS))
324 {
325 new_key.key = key;
326 new_key.vma_count++;
327 shm_fill_keytable(&new_key);
328 }
329 return SHM_CTRL_OK;
330}
331
332/*******************************************************************************
333* ¹¦ÄÜÃèÊö: shm_do_remote_map_vma
334* ²ÎÊý˵Ã÷:
335* (´«Èë²ÎÊý) void
336* (´«³ö²ÎÊý) void
337* ·µ »Ø Öµ: SHM_CTRL_OK or SHM_CTRL_ERROR
338* ÆäËü˵Ã÷: This function is used for
339*
340/*²éѯkey,Èç¹ûÒÑ·ÖÅä¹ýʹÓÃkey¶ÔÓ¦µÄbitmap, ·ñÔò´Ó×ÜÄÚ´æ³Ø·ÖÅä
341*******************************************************************************/
342int shm_do_remote_map_vma(struct vm_area_struct *vma, key_t key)
343{
344 int ret = 0;
345 unsigned long vm_addr = 0;
346 unsigned int region_index = 0;
347 int key_index = 0;
348 void *new_page_phy = NULL;
349 struct shm_key_node *key_node = NULL;
350 DECLARE_BITMAP(shm_inuse_tmp, SHM_UNIT_NUM_BITS);
351
352 if((vma == NULL) || (g_shm_region == NULL))
353 {
354 printk("shm_do_remote_map_vma:Shm region is not ready\n");
355 return SHM_CTRL_ERROR;
356 }
357
358 /*Ó³ÉävmaΪ·ÇcacheÊôÐÔ*/
359 pgprot_noncached(vma->vm_page_prot);
360
361 soft_spin_lock(SHM_SFLOCK);
362
363 key_index = shm_quary_keyArray(key);
364
365 if (key_index < 0)
366 {
367 ret = shm_alloc_new_page(vma, key);
368 soft_spin_unlock(SHM_SFLOCK);
369 if (ret < 0)
370 panic("shm_alloc_new_page Fail\n");
371 return ret;
372 }
373
374 vm_addr = vma->vm_start;
375
376 if ((0 <= key_index) && (key_index < SHM_UNIT_NUM_BITS))
377 key_node = &shm_remote_manager->keys_info_head[key_index];
378 else
379 panic("key_index out of range: failed\n");
380
381 memcpy(shm_inuse_tmp, key_node->shm_inuse_index, sizeof(shm_inuse_tmp));
382
383 for (; vm_addr < vma->vm_end; vm_addr += PAGE_SIZE)
384 {
385 region_index = find_first_bit(shm_inuse_tmp, SHM_UNIT_NUM_BITS);
386 if (region_index < SHM_UNIT_NUM_BITS)
387 {
388 new_page_phy = SHM_UNIT_PAGE_ADDR(region_index);
389 if (shm_vma_write_pagetable(vma, vm_addr, new_page_phy))
390 {
391 soft_spin_unlock(SHM_SFLOCK);
392 panic("shm_do_remote_map_vma vm_insert_page failed\n");
393 return SHM_CTRL_ERROR;
394 }
395 clear_bit(region_index, shm_inuse_tmp);
396 }
397 else
398 {
399 return SHM_CTRL_ERROR;
400 }
401 }
402 key_node->vma_count++;
403
404 soft_spin_unlock(SHM_SFLOCK);
405 return SHM_CTRL_OK;
406}
407
408/*******************************************************************************
409* ¹¦ÄÜÃèÊö: shm_remote_free_pages
410* ²ÎÊý˵Ã÷:
411* (´«Èë²ÎÊý) void
412* (´«³ö²ÎÊý) void
413* ·µ »Ø Öµ: SHM_CTRL_OK or SHM_CTRL_ERROR
414* ÆäËü˵Ã÷: This function is used for
415*******************************************************************************/
416int shm_remote_free_pages(key_t key)
417{
418 int key_index = 0;
419 unsigned int region_index = 0;
420 struct shm_key_node *key_node = NULL;
421
422 if(g_shm_region == NULL)
423 {
424 printk("shm_remote_free_pages: Shm region is not ready\n");
425 return SHM_CTRL_ERROR;
426 }
427
428 soft_spin_lock(SHM_SFLOCK);
429
430 /*²éѯkey*/
431 key_index = shm_quary_keyArray(key);
432 if(key_index < 0 || key_index >= SHM_UNIT_NUM_BITS)
433 {
434 soft_spin_unlock(SHM_SFLOCK);
435 panic("error\n");
436 }
437
438 /*ÊôÓÚ¿çºËµÄ¾ÍÊͷŵ½³Ø×ÓÀï*/
439 key_node = &shm_remote_manager->keys_info_head[key_index];
440
441 key_node->vma_count--;
442
443 if(key_node->vma_count == 0)
444 {
445 for_each_set_bit(region_index, key_node->shm_inuse_index, SHM_UNIT_NUM_BITS)
446 {
447 clear_bit(region_index, shm_remote_manager->shm_regions_bitmap);
448 }
449 shm_remove_keynode(key_index);
450 }
451 soft_spin_unlock(SHM_SFLOCK);
452 return SHM_CTRL_OK;
453}
454
455/*******************************************************************************
456* ¹¦ÄÜÃèÊö: shm_rpcore_init
457* ²ÎÊý˵Ã÷:
458* (´«Èë²ÎÊý) void
459* (´«³ö²ÎÊý) void
460* ·µ »Ø Öµ:
461* ÆäËü˵Ã÷: This function is used for shm ctrl init
462*******************************************************************************/
463static int __init shm_rpcore_init(void)
464{
465 dma_addr_t dma_phys;
466 dma_addr_t shm_keyInfo_phys;
467 struct shm_pool_msg shm_msg = {0};
468
469 g_shm_region = dma_alloc_coherent(NULL,
470 (size_t)SHM_REMOTE_BUFF_LEN,
471 &dma_phys,
472 GFP_KERNEL);
473 if(!g_shm_region)
474 {
475 panic("g_shm_region NOMEM\n");
476 }
477
478 g_shm_phyAddr = dma_phys;
479
480 shm_remote_manager = dma_alloc_coherent(NULL,
481 (size_t)(SHM_KEYS_STATUS_LEN),
482 &shm_keyInfo_phys,
483 GFP_KERNEL);
484 if(!shm_remote_manager)
485 {
486 panic("shm_remote_manager NOMEM\n");
487 }
488
489 memset(shm_remote_manager, 0, sizeof(struct shm_entity));
490 shm_msg.shm_len = SHM_REMOTE_BUFF_LEN;
491 shm_msg.key_manage_len = SHM_KEYS_STATUS_LEN;
492 shm_msg.shm_memory_phy = g_shm_phyAddr;
493 shm_msg.key_manage_phy = shm_keyInfo_phys;
494
495 memcpy((void*)IRAM_BASE_ADDR_SHM_REMOTE_REGION, &shm_msg, sizeof(struct shm_pool_msg));
496
497 return SHM_CTRL_OK;
498
499}
500
501late_initcall(shm_rpcore_init);
502
503
504