提交 60458fba 编写于 作者: A Aneesh Kumar K.V 提交者: Michael Ellerman

powerpc/mm: Add helpers for accessing hash translation related variables

We want to switch to allocating them runtime only when hash translation is
enabled. Add helpers so that both book3s and nohash can be adapted to
upcoming change easily.
Signed-off-by: NAneesh Kumar K.V <aneesh.kumar@linux.ibm.com>
Signed-off-by: NMichael Ellerman <mpe@ellerman.id.au>
上级 4f40b15f
...@@ -657,8 +657,8 @@ extern void slb_set_size(u16 size); ...@@ -657,8 +657,8 @@ extern void slb_set_size(u16 size);
/* 4 bits per slice and we have one slice per 1TB */ /* 4 bits per slice and we have one slice per 1TB */
#define SLICE_ARRAY_SIZE (H_PGTABLE_RANGE >> 41) #define SLICE_ARRAY_SIZE (H_PGTABLE_RANGE >> 41)
#define TASK_SLICE_ARRAY_SZ(x) ((x)->context.slb_addr_limit >> 41) #define LOW_SLICE_ARRAY_SZ (BITS_PER_LONG / BITS_PER_BYTE)
#define TASK_SLICE_ARRAY_SZ(x) ((x)->slb_addr_limit >> 41)
#ifndef __ASSEMBLY__ #ifndef __ASSEMBLY__
#ifdef CONFIG_PPC_SUBPAGE_PROT #ifdef CONFIG_PPC_SUBPAGE_PROT
......
...@@ -139,7 +139,7 @@ typedef struct { ...@@ -139,7 +139,7 @@ typedef struct {
struct npu_context *npu_context; struct npu_context *npu_context;
/* SLB page size encodings*/ /* SLB page size encodings*/
unsigned char low_slices_psize[BITS_PER_LONG / BITS_PER_BYTE]; unsigned char low_slices_psize[LOW_SLICE_ARRAY_SZ];
unsigned char high_slices_psize[SLICE_ARRAY_SIZE]; unsigned char high_slices_psize[SLICE_ARRAY_SIZE];
unsigned long slb_addr_limit; unsigned long slb_addr_limit;
# ifdef CONFIG_PPC_64K_PAGES # ifdef CONFIG_PPC_64K_PAGES
...@@ -174,6 +174,67 @@ typedef struct { ...@@ -174,6 +174,67 @@ typedef struct {
#endif #endif
} mm_context_t; } mm_context_t;
static inline u16 mm_ctx_user_psize(mm_context_t *ctx)
{
return ctx->user_psize;
}
static inline void mm_ctx_set_user_psize(mm_context_t *ctx, u16 user_psize)
{
ctx->user_psize = user_psize;
}
static inline unsigned char *mm_ctx_low_slices(mm_context_t *ctx)
{
return ctx->low_slices_psize;
}
static inline unsigned char *mm_ctx_high_slices(mm_context_t *ctx)
{
return ctx->high_slices_psize;
}
static inline unsigned long mm_ctx_slb_addr_limit(mm_context_t *ctx)
{
return ctx->slb_addr_limit;
}
static inline void mm_ctx_set_slb_addr_limit(mm_context_t *ctx, unsigned long limit)
{
ctx->slb_addr_limit = limit;
}
#ifdef CONFIG_PPC_64K_PAGES
static inline struct slice_mask *mm_ctx_slice_mask_64k(mm_context_t *ctx)
{
return &ctx->mask_64k;
}
#endif
static inline struct slice_mask *mm_ctx_slice_mask_4k(mm_context_t *ctx)
{
return &ctx->mask_4k;
}
#ifdef CONFIG_HUGETLB_PAGE
static inline struct slice_mask *mm_ctx_slice_mask_16m(mm_context_t *ctx)
{
return &ctx->mask_16m;
}
static inline struct slice_mask *mm_ctx_slice_mask_16g(mm_context_t *ctx)
{
return &ctx->mask_16g;
}
#endif
#ifdef CONFIG_PPC_SUBPAGE_PROT
static inline struct subpage_prot_table *mm_ctx_subpage_prot(mm_context_t *ctx)
{
return &ctx->spt;
}
#endif
/* /*
* The current system page and segment sizes * The current system page and segment sizes
*/ */
......
...@@ -181,6 +181,7 @@ ...@@ -181,6 +181,7 @@
#ifdef CONFIG_PPC_MM_SLICES #ifdef CONFIG_PPC_MM_SLICES
#include <asm/nohash/32/slice.h> #include <asm/nohash/32/slice.h>
#define SLICE_ARRAY_SIZE (1 << (32 - SLICE_LOW_SHIFT - 1)) #define SLICE_ARRAY_SIZE (1 << (32 - SLICE_LOW_SHIFT - 1))
#define LOW_SLICE_ARRAY_SZ SLICE_ARRAY_SIZE
#endif #endif
#ifndef __ASSEMBLY__ #ifndef __ASSEMBLY__
...@@ -207,6 +208,55 @@ typedef struct { ...@@ -207,6 +208,55 @@ typedef struct {
void *pte_frag; void *pte_frag;
} mm_context_t; } mm_context_t;
#ifdef CONFIG_PPC_MM_SLICES
static inline u16 mm_ctx_user_psize(mm_context_t *ctx)
{
return ctx->user_psize;
}
static inline void mm_ctx_set_user_psize(mm_context_t *ctx, u16 user_psize)
{
ctx->user_psize = user_psize;
}
static inline unsigned char *mm_ctx_low_slices(mm_context_t *ctx)
{
return ctx->low_slices_psize;
}
static inline unsigned char *mm_ctx_high_slices(mm_context_t *ctx)
{
return ctx->high_slices_psize;
}
static inline unsigned long mm_ctx_slb_addr_limit(mm_context_t *ctx)
{
return ctx->slb_addr_limit;
}
static inline void mm_ctx_set_slb_addr_limit(mm_context_t *ctx, unsigned long limit)
{
ctx->slb_addr_limit = limit;
}
static inline struct slice_mask *mm_ctx_slice_mask_base(mm_context_t *ctx)
{
return &ctx->mask_base_psize;
}
#ifdef CONFIG_HUGETLB_PAGE
static inline struct slice_mask *mm_ctx_slice_mask_512k(mm_context_t *ctx)
{
return &ctx->mask_512k;
}
static inline struct slice_mask *mm_ctx_slice_mask_8m(mm_context_t *ctx)
{
return &ctx->mask_8m;
}
#endif
#endif /* CONFIG_PPC_MM_SLICE */
#define PHYS_IMMR_BASE (mfspr(SPRN_IMMR) & 0xfff80000) #define PHYS_IMMR_BASE (mfspr(SPRN_IMMR) & 0xfff80000)
#define VIRT_IMMR_BASE (__fix_to_virt(FIX_IMMR_BASE)) #define VIRT_IMMR_BASE (__fix_to_virt(FIX_IMMR_BASE))
......
...@@ -267,12 +267,12 @@ void copy_mm_to_paca(struct mm_struct *mm) ...@@ -267,12 +267,12 @@ void copy_mm_to_paca(struct mm_struct *mm)
get_paca()->mm_ctx_id = context->id; get_paca()->mm_ctx_id = context->id;
#ifdef CONFIG_PPC_MM_SLICES #ifdef CONFIG_PPC_MM_SLICES
VM_BUG_ON(!mm->context.slb_addr_limit); VM_BUG_ON(!mm_ctx_slb_addr_limit(context));
get_paca()->mm_ctx_slb_addr_limit = mm->context.slb_addr_limit; get_paca()->mm_ctx_slb_addr_limit = mm_ctx_slb_addr_limit(context);
memcpy(&get_paca()->mm_ctx_low_slices_psize, memcpy(&get_paca()->mm_ctx_low_slices_psize, mm_ctx_low_slices(context),
&context->low_slices_psize, sizeof(context->low_slices_psize)); LOW_SLICE_ARRAY_SZ);
memcpy(&get_paca()->mm_ctx_high_slices_psize, memcpy(&get_paca()->mm_ctx_high_slices_psize, mm_ctx_high_slices(context),
&context->high_slices_psize, TASK_SLICE_ARRAY_SZ(mm)); TASK_SLICE_ARRAY_SZ(context));
#else /* CONFIG_PPC_MM_SLICES */ #else /* CONFIG_PPC_MM_SLICES */
get_paca()->mm_ctx_user_psize = context->user_psize; get_paca()->mm_ctx_user_psize = context->user_psize;
get_paca()->mm_ctx_sllp = context->sllp; get_paca()->mm_ctx_sllp = context->sllp;
......
...@@ -1142,7 +1142,7 @@ void demote_segment_4k(struct mm_struct *mm, unsigned long addr) ...@@ -1142,7 +1142,7 @@ void demote_segment_4k(struct mm_struct *mm, unsigned long addr)
*/ */
static int subpage_protection(struct mm_struct *mm, unsigned long ea) static int subpage_protection(struct mm_struct *mm, unsigned long ea)
{ {
struct subpage_prot_table *spt = &mm->context.spt; struct subpage_prot_table *spt = mm_ctx_subpage_prot(&mm->context);
u32 spp = 0; u32 spp = 0;
u32 **sbpm, *sbpp; u32 **sbpm, *sbpp;
...@@ -1465,7 +1465,7 @@ static bool should_hash_preload(struct mm_struct *mm, unsigned long ea) ...@@ -1465,7 +1465,7 @@ static bool should_hash_preload(struct mm_struct *mm, unsigned long ea)
int psize = get_slice_psize(mm, ea); int psize = get_slice_psize(mm, ea);
/* We only prefault standard pages for now */ /* We only prefault standard pages for now */
if (unlikely(psize != mm->context.user_psize)) if (unlikely(psize != mm_ctx_user_psize(&mm->context)))
return false; return false;
/* /*
...@@ -1544,7 +1544,7 @@ void hash_preload(struct mm_struct *mm, unsigned long ea, ...@@ -1544,7 +1544,7 @@ void hash_preload(struct mm_struct *mm, unsigned long ea,
/* Hash it in */ /* Hash it in */
#ifdef CONFIG_PPC_64K_PAGES #ifdef CONFIG_PPC_64K_PAGES
if (mm->context.user_psize == MMU_PAGE_64K) if (mm_ctx_user_psize(&mm->context) == MMU_PAGE_64K)
rc = __hash_page_64K(ea, access, vsid, ptep, trap, rc = __hash_page_64K(ea, access, vsid, ptep, trap,
update_flags, ssize); update_flags, ssize);
else else
...@@ -1557,8 +1557,8 @@ void hash_preload(struct mm_struct *mm, unsigned long ea, ...@@ -1557,8 +1557,8 @@ void hash_preload(struct mm_struct *mm, unsigned long ea,
*/ */
if (rc == -1) if (rc == -1)
hash_failure_debug(ea, access, vsid, trap, ssize, hash_failure_debug(ea, access, vsid, trap, ssize,
mm->context.user_psize, mm_ctx_user_psize(&mm->context),
mm->context.user_psize, mm_ctx_user_psize(&mm->context),
pte_val(*ptep)); pte_val(*ptep));
out_exit: out_exit:
local_irq_restore(flags); local_irq_restore(flags);
......
...@@ -739,7 +739,7 @@ static long slb_allocate_user(struct mm_struct *mm, unsigned long ea) ...@@ -739,7 +739,7 @@ static long slb_allocate_user(struct mm_struct *mm, unsigned long ea)
* consider this as bad access if we take a SLB miss * consider this as bad access if we take a SLB miss
* on an address above addr limit. * on an address above addr limit.
*/ */
if (ea >= mm->context.slb_addr_limit) if (ea >= mm_ctx_slb_addr_limit(&mm->context))
return -EFAULT; return -EFAULT;
context = get_user_context(&mm->context, ea); context = get_user_context(&mm->context, ea);
......
...@@ -101,7 +101,7 @@ static int slice_area_is_free(struct mm_struct *mm, unsigned long addr, ...@@ -101,7 +101,7 @@ static int slice_area_is_free(struct mm_struct *mm, unsigned long addr,
{ {
struct vm_area_struct *vma; struct vm_area_struct *vma;
if ((mm->context.slb_addr_limit - len) < addr) if ((mm_ctx_slb_addr_limit(&mm->context) - len) < addr)
return 0; return 0;
vma = find_vma(mm, addr); vma = find_vma(mm, addr);
return (!vma || (addr + len) <= vm_start_gap(vma)); return (!vma || (addr + len) <= vm_start_gap(vma));
...@@ -155,15 +155,15 @@ static struct slice_mask *slice_mask_for_size(struct mm_struct *mm, int psize) ...@@ -155,15 +155,15 @@ static struct slice_mask *slice_mask_for_size(struct mm_struct *mm, int psize)
{ {
#ifdef CONFIG_PPC_64K_PAGES #ifdef CONFIG_PPC_64K_PAGES
if (psize == MMU_PAGE_64K) if (psize == MMU_PAGE_64K)
return &mm->context.mask_64k; return mm_ctx_slice_mask_64k(&mm->context);
#endif #endif
if (psize == MMU_PAGE_4K) if (psize == MMU_PAGE_4K)
return &mm->context.mask_4k; return mm_ctx_slice_mask_4k(&mm->context);
#ifdef CONFIG_HUGETLB_PAGE #ifdef CONFIG_HUGETLB_PAGE
if (psize == MMU_PAGE_16M) if (psize == MMU_PAGE_16M)
return &mm->context.mask_16m; return mm_ctx_slice_mask_16m(&mm->context);
if (psize == MMU_PAGE_16G) if (psize == MMU_PAGE_16G)
return &mm->context.mask_16g; return mm_ctx_slice_mask_16g(&mm->context);
#endif #endif
BUG(); BUG();
} }
...@@ -253,7 +253,7 @@ static void slice_convert(struct mm_struct *mm, ...@@ -253,7 +253,7 @@ static void slice_convert(struct mm_struct *mm,
*/ */
spin_lock_irqsave(&slice_convert_lock, flags); spin_lock_irqsave(&slice_convert_lock, flags);
lpsizes = mm->context.low_slices_psize; lpsizes = mm_ctx_low_slices(&mm->context);
for (i = 0; i < SLICE_NUM_LOW; i++) { for (i = 0; i < SLICE_NUM_LOW; i++) {
if (!(mask->low_slices & (1u << i))) if (!(mask->low_slices & (1u << i)))
continue; continue;
...@@ -272,8 +272,8 @@ static void slice_convert(struct mm_struct *mm, ...@@ -272,8 +272,8 @@ static void slice_convert(struct mm_struct *mm,
(((unsigned long)psize) << (mask_index * 4)); (((unsigned long)psize) << (mask_index * 4));
} }
hpsizes = mm->context.high_slices_psize; hpsizes = mm_ctx_high_slices(&mm->context);
for (i = 0; i < GET_HIGH_SLICE_INDEX(mm->context.slb_addr_limit); i++) { for (i = 0; i < GET_HIGH_SLICE_INDEX(mm_ctx_slb_addr_limit(&mm->context)); i++) {
if (!test_bit(i, mask->high_slices)) if (!test_bit(i, mask->high_slices))
continue; continue;
...@@ -292,8 +292,8 @@ static void slice_convert(struct mm_struct *mm, ...@@ -292,8 +292,8 @@ static void slice_convert(struct mm_struct *mm,
} }
slice_dbg(" lsps=%lx, hsps=%lx\n", slice_dbg(" lsps=%lx, hsps=%lx\n",
(unsigned long)mm->context.low_slices_psize, (unsigned long)mm_ctx_low_slices(&mm->context),
(unsigned long)mm->context.high_slices_psize); (unsigned long)mm_ctx_high_slices(&mm->context));
spin_unlock_irqrestore(&slice_convert_lock, flags); spin_unlock_irqrestore(&slice_convert_lock, flags);
...@@ -393,7 +393,7 @@ static unsigned long slice_find_area_topdown(struct mm_struct *mm, ...@@ -393,7 +393,7 @@ static unsigned long slice_find_area_topdown(struct mm_struct *mm,
* DEFAULT_MAP_WINDOW we should apply this. * DEFAULT_MAP_WINDOW we should apply this.
*/ */
if (high_limit > DEFAULT_MAP_WINDOW) if (high_limit > DEFAULT_MAP_WINDOW)
addr += mm->context.slb_addr_limit - DEFAULT_MAP_WINDOW; addr += mm_ctx_slb_addr_limit(&mm->context) - DEFAULT_MAP_WINDOW;
while (addr > min_addr) { while (addr > min_addr) {
info.high_limit = addr; info.high_limit = addr;
...@@ -505,20 +505,20 @@ unsigned long slice_get_unmapped_area(unsigned long addr, unsigned long len, ...@@ -505,20 +505,20 @@ unsigned long slice_get_unmapped_area(unsigned long addr, unsigned long len,
return -ENOMEM; return -ENOMEM;
} }
if (high_limit > mm->context.slb_addr_limit) { if (high_limit > mm_ctx_slb_addr_limit(&mm->context)) {
/* /*
* Increasing the slb_addr_limit does not require * Increasing the slb_addr_limit does not require
* slice mask cache to be recalculated because it should * slice mask cache to be recalculated because it should
* be already initialised beyond the old address limit. * be already initialised beyond the old address limit.
*/ */
mm->context.slb_addr_limit = high_limit; mm_ctx_set_slb_addr_limit(&mm->context, high_limit);
on_each_cpu(slice_flush_segments, mm, 1); on_each_cpu(slice_flush_segments, mm, 1);
} }
/* Sanity checks */ /* Sanity checks */
BUG_ON(mm->task_size == 0); BUG_ON(mm->task_size == 0);
BUG_ON(mm->context.slb_addr_limit == 0); BUG_ON(mm_ctx_slb_addr_limit(&mm->context) == 0);
VM_BUG_ON(radix_enabled()); VM_BUG_ON(radix_enabled());
slice_dbg("slice_get_unmapped_area(mm=%p, psize=%d...\n", mm, psize); slice_dbg("slice_get_unmapped_area(mm=%p, psize=%d...\n", mm, psize);
...@@ -696,7 +696,7 @@ unsigned long arch_get_unmapped_area(struct file *filp, ...@@ -696,7 +696,7 @@ unsigned long arch_get_unmapped_area(struct file *filp,
unsigned long flags) unsigned long flags)
{ {
return slice_get_unmapped_area(addr, len, flags, return slice_get_unmapped_area(addr, len, flags,
current->mm->context.user_psize, 0); mm_ctx_user_psize(&current->mm->context), 0);
} }
unsigned long arch_get_unmapped_area_topdown(struct file *filp, unsigned long arch_get_unmapped_area_topdown(struct file *filp,
...@@ -706,7 +706,7 @@ unsigned long arch_get_unmapped_area_topdown(struct file *filp, ...@@ -706,7 +706,7 @@ unsigned long arch_get_unmapped_area_topdown(struct file *filp,
const unsigned long flags) const unsigned long flags)
{ {
return slice_get_unmapped_area(addr0, len, flags, return slice_get_unmapped_area(addr0, len, flags,
current->mm->context.user_psize, 1); mm_ctx_user_psize(&current->mm->context), 1);
} }
unsigned int get_slice_psize(struct mm_struct *mm, unsigned long addr) unsigned int get_slice_psize(struct mm_struct *mm, unsigned long addr)
...@@ -717,10 +717,10 @@ unsigned int get_slice_psize(struct mm_struct *mm, unsigned long addr) ...@@ -717,10 +717,10 @@ unsigned int get_slice_psize(struct mm_struct *mm, unsigned long addr)
VM_BUG_ON(radix_enabled()); VM_BUG_ON(radix_enabled());
if (slice_addr_is_low(addr)) { if (slice_addr_is_low(addr)) {
psizes = mm->context.low_slices_psize; psizes = mm_ctx_low_slices(&mm->context);
index = GET_LOW_SLICE_INDEX(addr); index = GET_LOW_SLICE_INDEX(addr);
} else { } else {
psizes = mm->context.high_slices_psize; psizes = mm_ctx_high_slices(&mm->context);
index = GET_HIGH_SLICE_INDEX(addr); index = GET_HIGH_SLICE_INDEX(addr);
} }
mask_index = index & 0x1; mask_index = index & 0x1;
...@@ -742,20 +742,19 @@ void slice_init_new_context_exec(struct mm_struct *mm) ...@@ -742,20 +742,19 @@ void slice_init_new_context_exec(struct mm_struct *mm)
* duplicated. * duplicated.
*/ */
#ifdef CONFIG_PPC64 #ifdef CONFIG_PPC64
mm->context.slb_addr_limit = DEFAULT_MAP_WINDOW_USER64; mm_ctx_set_slb_addr_limit(&mm->context, DEFAULT_MAP_WINDOW_USER64);
#else #else
mm->context.slb_addr_limit = DEFAULT_MAP_WINDOW; mm->context.slb_addr_limit = DEFAULT_MAP_WINDOW;
#endif #endif
mm_ctx_set_user_psize(&mm->context, psize);
mm->context.user_psize = psize;
/* /*
* Set all slice psizes to the default. * Set all slice psizes to the default.
*/ */
lpsizes = mm->context.low_slices_psize; lpsizes = mm_ctx_low_slices(&mm->context);
memset(lpsizes, (psize << 4) | psize, SLICE_NUM_LOW >> 1); memset(lpsizes, (psize << 4) | psize, SLICE_NUM_LOW >> 1);
hpsizes = mm->context.high_slices_psize; hpsizes = mm_ctx_high_slices(&mm->context);
memset(hpsizes, (psize << 4) | psize, SLICE_NUM_HIGH >> 1); memset(hpsizes, (psize << 4) | psize, SLICE_NUM_HIGH >> 1);
/* /*
...@@ -777,7 +776,7 @@ void slice_setup_new_exec(void) ...@@ -777,7 +776,7 @@ void slice_setup_new_exec(void)
if (!is_32bit_task()) if (!is_32bit_task())
return; return;
mm->context.slb_addr_limit = DEFAULT_MAP_WINDOW; mm_ctx_set_slb_addr_limit(&mm->context, DEFAULT_MAP_WINDOW);
} }
#endif #endif
...@@ -816,7 +815,7 @@ int slice_is_hugepage_only_range(struct mm_struct *mm, unsigned long addr, ...@@ -816,7 +815,7 @@ int slice_is_hugepage_only_range(struct mm_struct *mm, unsigned long addr,
unsigned long len) unsigned long len)
{ {
const struct slice_mask *maskp; const struct slice_mask *maskp;
unsigned int psize = mm->context.user_psize; unsigned int psize = mm_ctx_user_psize(&mm->context);
VM_BUG_ON(radix_enabled()); VM_BUG_ON(radix_enabled());
......
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
*/ */
void subpage_prot_free(struct mm_struct *mm) void subpage_prot_free(struct mm_struct *mm)
{ {
struct subpage_prot_table *spt = &mm->context.spt; struct subpage_prot_table *spt = mm_ctx_subpage_prot(&mm->context);
unsigned long i, j, addr; unsigned long i, j, addr;
u32 **p; u32 **p;
...@@ -52,7 +52,7 @@ void subpage_prot_free(struct mm_struct *mm) ...@@ -52,7 +52,7 @@ void subpage_prot_free(struct mm_struct *mm)
void subpage_prot_init_new_context(struct mm_struct *mm) void subpage_prot_init_new_context(struct mm_struct *mm)
{ {
struct subpage_prot_table *spt = &mm->context.spt; struct subpage_prot_table *spt = mm_ctx_subpage_prot(&mm->context);
memset(spt, 0, sizeof(*spt)); memset(spt, 0, sizeof(*spt));
} }
...@@ -93,7 +93,7 @@ static void hpte_flush_range(struct mm_struct *mm, unsigned long addr, ...@@ -93,7 +93,7 @@ static void hpte_flush_range(struct mm_struct *mm, unsigned long addr,
static void subpage_prot_clear(unsigned long addr, unsigned long len) static void subpage_prot_clear(unsigned long addr, unsigned long len)
{ {
struct mm_struct *mm = current->mm; struct mm_struct *mm = current->mm;
struct subpage_prot_table *spt = &mm->context.spt; struct subpage_prot_table *spt = mm_ctx_subpage_prot(&mm->context);
u32 **spm, *spp; u32 **spm, *spp;
unsigned long i; unsigned long i;
size_t nw; size_t nw;
...@@ -189,7 +189,7 @@ SYSCALL_DEFINE3(subpage_prot, unsigned long, addr, ...@@ -189,7 +189,7 @@ SYSCALL_DEFINE3(subpage_prot, unsigned long, addr,
unsigned long, len, u32 __user *, map) unsigned long, len, u32 __user *, map)
{ {
struct mm_struct *mm = current->mm; struct mm_struct *mm = current->mm;
struct subpage_prot_table *spt = &mm->context.spt; struct subpage_prot_table *spt = mm_ctx_subpage_prot(&mm->context);
u32 **spm, *spp; u32 **spm, *spp;
unsigned long i; unsigned long i;
size_t nw; size_t nw;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册