/* * SPU local store allocation routines * * Copyright 2007 Benjamin Herrenschmidt, IBM Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #undef DEBUG #include #include #include #include #include #include #include "spufs.h" static int spu_alloc_lscsa_std(struct spu_state *csa) { struct spu_lscsa *lscsa; unsigned char *p; lscsa = vmalloc(sizeof(struct spu_lscsa)); if (!lscsa) return -ENOMEM; memset(lscsa, 0, sizeof(struct spu_lscsa)); csa->lscsa = lscsa; /* Set LS pages reserved to allow for user-space mapping. */ for (p = lscsa->ls; p < lscsa->ls + LS_SIZE; p += PAGE_SIZE) SetPageReserved(vmalloc_to_page(p)); return 0; } static void spu_free_lscsa_std(struct spu_state *csa) { /* Clear reserved bit before vfree. */ unsigned char *p; if (csa->lscsa == NULL) return; for (p = csa->lscsa->ls; p < csa->lscsa->ls + LS_SIZE; p += PAGE_SIZE) ClearPageReserved(vmalloc_to_page(p)); vfree(csa->lscsa); } #ifdef CONFIG_SPU_FS_64K_LS #define SPU_64K_PAGE_SHIFT 16 #define SPU_64K_PAGE_ORDER (SPU_64K_PAGE_SHIFT - PAGE_SHIFT) #define SPU_64K_PAGE_COUNT (1ul << SPU_64K_PAGE_ORDER) int spu_alloc_lscsa(struct spu_state *csa) { struct page **pgarray; unsigned char *p; int i, j, n_4k; /* Check availability of 64K pages */ if (!spu_64k_pages_available()) goto fail; csa->use_big_pages = 1; pr_debug("spu_alloc_lscsa(csa=0x%p), trying to allocate 64K pages\n", csa); /* First try to allocate our 64K pages. We need 5 of them * with the current implementation. In the future, we should try * to separate the lscsa with the actual local store image, thus * allowing us to require only 4 64K pages per context */ for (i = 0; i < SPU_LSCSA_NUM_BIG_PAGES; i++) { /* XXX This is likely to fail, we should use a special pool * similiar to what hugetlbfs does. */ csa->lscsa_pages[i] = alloc_pages(GFP_KERNEL, SPU_64K_PAGE_ORDER); if (csa->lscsa_pages[i] == NULL) goto fail; } pr_debug(" success ! creating vmap...\n"); /* Now we need to create a vmalloc mapping of these for the kernel * and SPU context switch code to use. Currently, we stick to a * normal kernel vmalloc mapping, which in our case will be 4K */ n_4k = SPU_64K_PAGE_COUNT * SPU_LSCSA_NUM_BIG_PAGES; pgarray = kmalloc(sizeof(struct page *) * n_4k, GFP_KERNEL); if (pgarray == NULL) goto fail; for (i = 0; i < SPU_LSCSA_NUM_BIG_PAGES; i++) for (j = 0; j < SPU_64K_PAGE_COUNT; j++) /* We assume all the struct page's are contiguous * which should be hopefully the case for an order 4 * allocation.. */ pgarray[i * SPU_64K_PAGE_COUNT + j] = csa->lscsa_pages[i] + j; csa->lscsa = vmap(pgarray, n_4k, VM_USERMAP, PAGE_KERNEL); kfree(pgarray); if (csa->lscsa == NULL) goto fail; memset(csa->lscsa, 0, sizeof(struct spu_lscsa)); /* Set LS pages reserved to allow for user-space mapping. * * XXX isn't that a bit obsolete ? I think we should just * make sure the page count is high enough. Anyway, won't harm * for now */ for (p = csa->lscsa->ls; p < csa->lscsa->ls + LS_SIZE; p += PAGE_SIZE) SetPageReserved(vmalloc_to_page(p)); pr_debug(" all good !\n"); return 0; fail: pr_debug("spufs: failed to allocate lscsa 64K pages, falling back\n"); spu_free_lscsa(csa); return spu_alloc_lscsa_std(csa); } void spu_free_lscsa(struct spu_state *csa) { unsigned char *p; int i; if (!csa->use_big_pages) { spu_free_lscsa_std(csa); return; } csa->use_big_pages = 0; if (csa->lscsa == NULL) goto free_pages; for (p = csa->lscsa->ls; p < csa->lscsa->ls + LS_SIZE; p += PAGE_SIZE) ClearPageReserved(vmalloc_to_page(p)); vunmap(csa->lscsa); csa->lscsa = NULL; free_pages: for (i = 0; i < SPU_LSCSA_NUM_BIG_PAGES; i++) if (csa->lscsa_pages[i]) __free_pages(csa->lscsa_pages[i], SPU_64K_PAGE_ORDER); } #else /* CONFIG_SPU_FS_64K_LS */ int spu_alloc_lscsa(struct spu_state *csa) { return spu_alloc_lscsa_std(csa); } void spu_free_lscsa(struct spu_state *csa) { spu_free_lscsa_std(csa); } #endif /* !defined(CONFIG_SPU_FS_64K_LS) */