From patchwork Mon May 19 06:51:07 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexandre Courbot X-Patchwork-Id: 4199841 Return-Path: X-Original-To: patchwork-dri-devel@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork1.web.kernel.org (Postfix) with ESMTP id AFCE99F32A for ; Mon, 19 May 2014 06:51:36 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id D14082021B for ; Mon, 19 May 2014 06:51:35 +0000 (UTC) Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by mail.kernel.org (Postfix) with ESMTP id EFE612025A for ; Mon, 19 May 2014 06:51:34 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 43FC56E32F; Sun, 18 May 2014 23:51:34 -0700 (PDT) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from hqemgate14.nvidia.com (hqemgate14.nvidia.com [216.228.121.143]) by gabe.freedesktop.org (Postfix) with ESMTP id 8D3486E325; Sun, 18 May 2014 23:51:32 -0700 (PDT) Received: from hqnvupgp07.nvidia.com (Not Verified[216.228.121.13]) by hqemgate14.nvidia.com id ; Sun, 18 May 2014 23:52:42 -0700 Received: from hqemhub01.nvidia.com ([172.20.12.94]) by hqnvupgp07.nvidia.com (PGP Universal service); Sun, 18 May 2014 23:45:46 -0700 X-PGP-Universal: processed; by hqnvupgp07.nvidia.com on Sun, 18 May 2014 23:45:46 -0700 Received: from percival.nvidia.com (172.20.144.16) by hqemhub01.nvidia.com (172.20.150.30) with Microsoft SMTP Server (TLS) id 8.3.342.0; Sun, 18 May 2014 23:51:31 -0700 From: Alexandre Courbot To: Ben Skeggs Subject: [PATCH 1/2] drm/gk20a/fb: fix huge memory leak Date: Mon, 19 May 2014 15:51:07 +0900 Message-ID: <1400482268-4971-2-git-send-email-acourbot@nvidia.com> X-Mailer: git-send-email 1.9.2 In-Reply-To: <1400482268-4971-1-git-send-email-acourbot@nvidia.com> References: <1400482268-4971-1-git-send-email-acourbot@nvidia.com> X-NVConfidentiality: public MIME-Version: 1.0 Cc: gnurou@gmail.com, nouveau@lists.freedesktop.org, linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-tegra@vger.kernel.org X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" X-Spam-Status: No, score=-4.8 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP CMA-allocated memory must be freed by an exact mirror call to dma_release_from_contiguous(). It cannot be freed page-by-page as was previously believed without severe memory leakage. This page records the address and size of every allocated memory chunk so they can be properly freed when needed. Signed-off-by: Alexandre Courbot --- drivers/gpu/drm/nouveau/core/subdev/fb/ramgk20a.c | 74 ++++++++++++++--------- 1 file changed, 46 insertions(+), 28 deletions(-) diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramgk20a.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramgk20a.c index 7effd1a63458..5904af52e6d6 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/ramgk20a.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/ramgk20a.c @@ -28,28 +28,34 @@ #include #include +struct gk20a_mem_chunk { + struct list_head list; + struct page *pages; + u32 npages; +}; + +struct gk20a_mem { + struct nouveau_mem base; + struct list_head head; +}; + static void gk20a_ram_put(struct nouveau_fb *pfb, struct nouveau_mem **pmem) { struct device *dev = nv_device_base(nv_device(pfb)); - struct nouveau_mem *mem = *pmem; - int i; + struct gk20a_mem *mem = container_of(*pmem, struct gk20a_mem, base); + struct gk20a_mem_chunk *chunk, *n; *pmem = NULL; if (unlikely(mem == NULL)) return; - for (i = 0; i < mem->size; i++) { - struct page *page; - - if (mem->pages[i] == 0) - break; - - page = pfn_to_page(mem->pages[i] >> PAGE_SHIFT); - dma_release_from_contiguous(dev, page, 1); + list_for_each_entry_safe(chunk, n, &mem->head, list) { + dma_release_from_contiguous(dev, chunk->pages, chunk->npages); + kfree(chunk); } - kfree(mem->pages); + kfree(mem->base.pages); kfree(mem); } @@ -58,9 +64,8 @@ gk20a_ram_get(struct nouveau_fb *pfb, u64 size, u32 align, u32 ncmin, u32 memtype, struct nouveau_mem **pmem) { struct device *dev = nv_device_base(nv_device(pfb)); - struct nouveau_mem *mem; + struct gk20a_mem *mem; int type = memtype & 0xff; - dma_addr_t dma_addr; int npages; int order; int i; @@ -95,44 +100,57 @@ gk20a_ram_get(struct nouveau_fb *pfb, u64 size, u32 align, u32 ncmin, if (!mem) return -ENOMEM; - mem->size = npages; - mem->memtype = type; + mem->base.size = npages; + mem->base.memtype = type; - mem->pages = kzalloc(sizeof(dma_addr_t) * npages, GFP_KERNEL); - if (!mem) { + mem->base.pages = kzalloc(sizeof(dma_addr_t) * npages, GFP_KERNEL); + if (!mem->base.pages) { kfree(mem); return -ENOMEM; } + INIT_LIST_HEAD(&mem->head); + + *pmem = &mem->base; + while (npages) { - struct page *pages; + struct gk20a_mem_chunk *chunk; + dma_addr_t addr; int pos = 0; /* don't overflow in case size is not a multiple of ncmin */ if (ncmin > npages) ncmin = npages; - pages = dma_alloc_from_contiguous(dev, ncmin, order); - if (!pages) { - gk20a_ram_put(pfb, &mem); + chunk = kzalloc(sizeof(*chunk), GFP_KERNEL); + if (!chunk) { + gk20a_ram_put(pfb, pmem); return -ENOMEM; } - dma_addr = (dma_addr_t)(page_to_pfn(pages) << PAGE_SHIFT); + chunk->pages = dma_alloc_from_contiguous(dev, ncmin, order); + if (!chunk->pages) { + kfree(chunk); + gk20a_ram_put(pfb, pmem); + return -ENOMEM; + } - nv_debug(pfb, " alloc count: %x, order: %x, addr: %pad\n", ncmin, - order, &dma_addr); + chunk->npages = ncmin; + list_add_tail(&chunk->list, &mem->head); + + addr = (dma_addr_t)(page_to_pfn(chunk->pages) << PAGE_SHIFT); + + nv_debug(pfb, " alloc count: %x, order: %x, addr: %pad\n", + ncmin, order, &addr); for (i = 0; i < ncmin; i++) - mem->pages[pos + i] = dma_addr + (PAGE_SIZE * i); + mem->base.pages[pos + i] = addr + (PAGE_SIZE * i); pos += ncmin; npages -= ncmin; } - mem->offset = (u64)mem->pages[0]; - - *pmem = mem; + mem->base.offset = (u64)mem->base.pages[0]; return 0; }