Message ID | 20240123221227.868341-2-afd@ti.com |
---|---|
State | New |
Headers |
Return-Path: <linux-kernel+bounces-36122-ouuuleilei=gmail.com@vger.kernel.org> Delivered-To: ouuuleilei@gmail.com Received: by 2002:a05:7300:2553:b0:103:945f:af90 with SMTP id p19csp634021dyi; Tue, 23 Jan 2024 14:13:45 -0800 (PST) X-Google-Smtp-Source: AGHT+IGhRMP5vDvWz9H4QksS3+7UynjQrpCqqjH6Q8M8KYkGqr1NbeeWa9EQ4ash8gKUpsDDzopI X-Received: by 2002:a05:6214:21ed:b0:685:6792:b522 with SMTP id p13-20020a05621421ed00b006856792b522mr1720427qvj.57.1706048024929; Tue, 23 Jan 2024 14:13:44 -0800 (PST) ARC-Seal: i=2; a=rsa-sha256; t=1706048024; cv=pass; d=google.com; s=arc-20160816; b=cLi5Et/rJidXpDVtEZQhXHJP0b2sj2oaLpHVUroA3g/9tQgITiKWC/VZKB43OUwXAK rYutemL9XKfpbz7tpnnHd56HBlYpzxk4cOMBb+o2oiYPf0CC952rmJei4o9il6IOuXzA aYRVQLOvGmy5s2w/BofiEoyRmjW1KOQvpxAlyVlE4G+csPn4jlDe6U8x1NCZjPt15C6Q MXKVTzGCv+VqCOIFXui3ls3VNR+2qSI0EE1kY5O1UkqBkaNQ2lxOKn3nqcIL+bHBLZT9 Hd6rlSNFPCZL0Zk3UGNg8UyAB5ST0le8VpY8S7A1xRBDwXHjgFr1gl/TPnQE278mvAOX BlJA== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=content-transfer-encoding:mime-version:list-unsubscribe :list-subscribe:list-id:precedence:references:in-reply-to:message-id :date:subject:cc:to:from:dkim-signature; bh=O9hVuXwF6nE6rUWyrB9U3f/wPgQjIzOpQGzpiCphLH8=; fh=JtJoRmEMNM7h+1I1BByQR+6uMM5KgzVUsDnK41zecMo=; b=i0OqcfvwjMFG1+yObvuG8HqQmmnrNPQpnDQIIZH6B9Hb4hrFvqZsNfM4W8oODvUMwB EYkmEJ47lmU/xXlFDnasIj8oO1F+BZ0RCE33Jrk+LsLc+bPB/7LT0ejHy34592+pwb9R Sl4zcLME7CCpanOwhk+ZYehHBAOr6S4u1e7tsVIkVBjkLRAd4W+fc0UOhbvA39i+e+Ho wjZKaGgT62MNaMBx115+qDKQbmMz7/8kUbX3I5ZG8F19HNzELuY9XPAaeuc57O+JHI8I YDROCCuqKDbXjsFGihjQ1lrMtSCkewJSLO7nGxtjhqz6xHpO5MSd+IXbKLGQ85b8iH8a umtA== ARC-Authentication-Results: i=2; mx.google.com; dkim=pass header.i=@ti.com header.s=ti-com-17Q1 header.b="vmty/q4l"; arc=pass (i=1 spf=pass spfdomain=ti.com dkim=pass dkdomain=ti.com dmarc=pass fromdomain=ti.com); spf=pass (google.com: domain of linux-kernel+bounces-36122-ouuuleilei=gmail.com@vger.kernel.org designates 147.75.199.223 as permitted sender) smtp.mailfrom="linux-kernel+bounces-36122-ouuuleilei=gmail.com@vger.kernel.org"; dmarc=pass (p=QUARANTINE sp=NONE dis=NONE) header.from=ti.com Received: from ny.mirrors.kernel.org (ny.mirrors.kernel.org. [147.75.199.223]) by mx.google.com with ESMTPS id qr17-20020a05620a391100b007833e965418si9562290qkn.424.2024.01.23.14.13.44 for <ouuuleilei@gmail.com> (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 23 Jan 2024 14:13:44 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel+bounces-36122-ouuuleilei=gmail.com@vger.kernel.org designates 147.75.199.223 as permitted sender) client-ip=147.75.199.223; Authentication-Results: mx.google.com; dkim=pass header.i=@ti.com header.s=ti-com-17Q1 header.b="vmty/q4l"; arc=pass (i=1 spf=pass spfdomain=ti.com dkim=pass dkdomain=ti.com dmarc=pass fromdomain=ti.com); spf=pass (google.com: domain of linux-kernel+bounces-36122-ouuuleilei=gmail.com@vger.kernel.org designates 147.75.199.223 as permitted sender) smtp.mailfrom="linux-kernel+bounces-36122-ouuuleilei=gmail.com@vger.kernel.org"; dmarc=pass (p=QUARANTINE sp=NONE dis=NONE) header.from=ti.com Received: from smtp.subspace.kernel.org (wormhole.subspace.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ny.mirrors.kernel.org (Postfix) with ESMTPS id B60481C20C85 for <ouuuleilei@gmail.com>; Tue, 23 Jan 2024 22:13:44 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 78E0552F9C; Tue, 23 Jan 2024 22:12:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=ti.com header.i=@ti.com header.b="vmty/q4l" Received: from fllv0016.ext.ti.com (fllv0016.ext.ti.com [198.47.19.142]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E7F894E1D0; Tue, 23 Jan 2024 22:12:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.47.19.142 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1706047962; cv=none; b=l7sXA8yh4LmSnI7NLXCLgIdAMovO/8J4IBDwUKjgoHCyb5Y6Urxs/kptcfLobvIIhXqLf90PbAkYixP2CfeyHQiatKR/xrSEwmjaLUErr4HBgK4ZCKnEf6xFAMM+shYVODmurWhgKcruDqIft+O6jPuDdpBodYgu9/h/YfKIBy4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1706047962; c=relaxed/simple; bh=U+S/isMESvEy8JNCOpfvoHDmrDoGEXoUbOEJFbu6JEk=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=l7at1Ihe952s+aZhW4dSMY38SPw5eCa2A+Ba3n5uykHPzHrT27ojSenQ4/mcacNjP2JWmSryWbU9Ejf8gKGQ0tprysIqle1JFO40YgFDSwdkedDLCPfsnK2Zh6IUYn9TCRKikDVAwCGML3i+0U6jC4XDczNCDP7/HOx18i9t+0o= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=ti.com; spf=pass smtp.mailfrom=ti.com; dkim=pass (1024-bit key) header.d=ti.com header.i=@ti.com header.b=vmty/q4l; arc=none smtp.client-ip=198.47.19.142 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=ti.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=ti.com Received: from lelv0265.itg.ti.com ([10.180.67.224]) by fllv0016.ext.ti.com (8.15.2/8.15.2) with ESMTP id 40NMCTA0037452; Tue, 23 Jan 2024 16:12:29 -0600 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ti.com; s=ti-com-17Q1; t=1706047949; bh=O9hVuXwF6nE6rUWyrB9U3f/wPgQjIzOpQGzpiCphLH8=; h=From:To:CC:Subject:Date:In-Reply-To:References; b=vmty/q4lJum4ChM0nmBfE+luuoEk2v+xFJ/dc2MuxnlqJrr13o/nP8dypXgK4YJ0Q Sl0BdC2J0Jpps1MxHqBt2QosGfOM0iA/vzGTKRUvNUEvrkoLTqf/a3vdUC5nQa/uK1 UwapsJ4hae9RC3mM+ATs9H7p5bYqqzdLK1RTo0DI= Received: from DLEE110.ent.ti.com (dlee110.ent.ti.com [157.170.170.21]) by lelv0265.itg.ti.com (8.15.2/8.15.2) with ESMTPS id 40NMCTK9024752 (version=TLSv1.2 cipher=AES256-GCM-SHA384 bits=256 verify=FAIL); Tue, 23 Jan 2024 16:12:29 -0600 Received: from DLEE107.ent.ti.com (157.170.170.37) by DLEE110.ent.ti.com (157.170.170.21) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2507.23; Tue, 23 Jan 2024 16:12:29 -0600 Received: from lelvsmtp6.itg.ti.com (10.180.75.249) by DLEE107.ent.ti.com (157.170.170.37) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2507.23 via Frontend Transport; Tue, 23 Jan 2024 16:12:29 -0600 Received: from lelvsmtp6.itg.ti.com ([10.249.42.149]) by lelvsmtp6.itg.ti.com (8.15.2/8.15.2) with ESMTP id 40NMCSuT068795; Tue, 23 Jan 2024 16:12:29 -0600 From: Andrew Davis <afd@ti.com> To: Gerd Hoffmann <kraxel@redhat.com>, Sumit Semwal <sumit.semwal@linaro.org>, =?utf-8?q?Christian_K=C3=B6nig?= <christian.koenig@amd.com>, Paul Cercueil <paul@crapouillou.net> CC: <dri-devel@lists.freedesktop.org>, <linux-media@vger.kernel.org>, <linaro-mm-sig@lists.linaro.org>, <linux-kernel@vger.kernel.org>, Andrew Davis <afd@ti.com> Subject: [PATCH 2/3] udmabuf: Sync buffer mappings for attached devices Date: Tue, 23 Jan 2024 16:12:26 -0600 Message-ID: <20240123221227.868341-2-afd@ti.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240123221227.868341-1-afd@ti.com> References: <20240123221227.868341-1-afd@ti.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: <linux-kernel.vger.kernel.org> List-Subscribe: <mailto:linux-kernel+subscribe@vger.kernel.org> List-Unsubscribe: <mailto:linux-kernel+unsubscribe@vger.kernel.org> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Content-Type: text/plain X-EXCLAIMER-MD-CONFIG: e1e8a2fd-e40a-4ac6-ac9b-f7e9cc9ee180 X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1788921013804205720 X-GMAIL-MSGID: 1788921013804205720 |
Series |
[1/3] udmabuf: Keep track current device mappings
|
|
Commit Message
Andrew Davis
Jan. 23, 2024, 10:12 p.m. UTC
Currently this driver creates a SGT table using the CPU as the
target device, then performs the dma_sync operations against
that SGT. This is backwards to how DMA-BUFs are supposed to behave.
This may have worked for the case where these buffers were given
only back to the same CPU that produced them as in the QEMU case.
And only then because the original author had the dma_sync
operations also backwards, syncing for the "device" on begin_cpu.
This was noticed and "fixed" in this patch[0].
That then meant we were sync'ing from the CPU to the CPU using
a pseudo-device "miscdevice". Which then caused another issue
due to the miscdevice not having a proper DMA mask (and why should
it, the CPU is not a DMA device). The fix for that was an even
more egregious hack[1] that declares the CPU is coherent with
itself and can access its own memory space..
Unwind all this and perform the correct action by doing the dma_sync
operations for each device currently attached to the backing buffer.
[0] commit 1ffe09590121 ("udmabuf: fix dma-buf cpu access")
[1] commit 9e9fa6a9198b ("udmabuf: Set the DMA mask for the udmabuf device (v2)")
Signed-off-by: Andrew Davis <afd@ti.com>
---
drivers/dma-buf/udmabuf.c | 41 +++++++++++++++------------------------
1 file changed, 16 insertions(+), 25 deletions(-)
Comments
Hi Andrew, > Currently this driver creates a SGT table using the CPU as the > target device, then performs the dma_sync operations against > that SGT. This is backwards to how DMA-BUFs are supposed to behave. > This may have worked for the case where these buffers were given > only back to the same CPU that produced them as in the QEMU case. > And only then because the original author had the dma_sync > operations also backwards, syncing for the "device" on begin_cpu. > This was noticed and "fixed" in this patch[0]. > > That then meant we were sync'ing from the CPU to the CPU using > a pseudo-device "miscdevice". Which then caused another issue > due to the miscdevice not having a proper DMA mask (and why should > it, the CPU is not a DMA device). The fix for that was an even > more egregious hack[1] that declares the CPU is coherent with > itself and can access its own memory space.. > > Unwind all this and perform the correct action by doing the dma_sync > operations for each device currently attached to the backing buffer. Makes sense. > > [0] commit 1ffe09590121 ("udmabuf: fix dma-buf cpu access") > [1] commit 9e9fa6a9198b ("udmabuf: Set the DMA mask for the udmabuf > device (v2)") > > Signed-off-by: Andrew Davis <afd@ti.com> > --- > drivers/dma-buf/udmabuf.c | 41 +++++++++++++++------------------------ > 1 file changed, 16 insertions(+), 25 deletions(-) > > diff --git a/drivers/dma-buf/udmabuf.c b/drivers/dma-buf/udmabuf.c > index 3a23f0a7d112a..ab6764322523c 100644 > --- a/drivers/dma-buf/udmabuf.c > +++ b/drivers/dma-buf/udmabuf.c > @@ -26,8 +26,6 @@ MODULE_PARM_DESC(size_limit_mb, "Max size of a > dmabuf, in megabytes. Default is > struct udmabuf { > pgoff_t pagecount; > struct page **pages; > - struct sg_table *sg; > - struct miscdevice *device; > struct list_head attachments; > struct mutex lock; > }; > @@ -169,12 +167,8 @@ static void unmap_udmabuf(struct > dma_buf_attachment *at, > static void release_udmabuf(struct dma_buf *buf) > { > struct udmabuf *ubuf = buf->priv; > - struct device *dev = ubuf->device->this_device; > pgoff_t pg; > > - if (ubuf->sg) > - put_sg_table(dev, ubuf->sg, DMA_BIDIRECTIONAL); What happens if the last importer maps the dmabuf but erroneously closes it immediately? Would unmap somehow get called in this case? Thanks, Vivek > - > for (pg = 0; pg < ubuf->pagecount; pg++) > put_page(ubuf->pages[pg]); > kfree(ubuf->pages); > @@ -185,33 +179,31 @@ static int begin_cpu_udmabuf(struct dma_buf > *buf, > enum dma_data_direction direction) > { > struct udmabuf *ubuf = buf->priv; > - struct device *dev = ubuf->device->this_device; > - int ret = 0; > - > - if (!ubuf->sg) { > - ubuf->sg = get_sg_table(dev, buf, direction); > - if (IS_ERR(ubuf->sg)) { > - ret = PTR_ERR(ubuf->sg); > - ubuf->sg = NULL; > - } > - } else { > - dma_sync_sg_for_cpu(dev, ubuf->sg->sgl, ubuf->sg->nents, > - direction); > - } > + struct udmabuf_attachment *a; > > - return ret; > + mutex_lock(&ubuf->lock); > + > + list_for_each_entry(a, &ubuf->attachments, list) > + dma_sync_sgtable_for_cpu(a->dev, a->table, direction); > + > + mutex_unlock(&ubuf->lock); > + > + return 0; > } > > static int end_cpu_udmabuf(struct dma_buf *buf, > enum dma_data_direction direction) > { > struct udmabuf *ubuf = buf->priv; > - struct device *dev = ubuf->device->this_device; > + struct udmabuf_attachment *a; > > - if (!ubuf->sg) > - return -EINVAL; > + mutex_lock(&ubuf->lock); > + > + list_for_each_entry(a, &ubuf->attachments, list) > + dma_sync_sgtable_for_device(a->dev, a->table, direction); > + > + mutex_unlock(&ubuf->lock); > > - dma_sync_sg_for_device(dev, ubuf->sg->sgl, ubuf->sg->nents, > direction); > return 0; > } > > @@ -307,7 +299,6 @@ static long udmabuf_create(struct miscdevice > *device, > exp_info.priv = ubuf; > exp_info.flags = O_RDWR; > > - ubuf->device = device; > buf = dma_buf_export(&exp_info); > if (IS_ERR(buf)) { > ret = PTR_ERR(buf); > -- > 2.39.2
On 1/24/24 5:05 PM, Kasireddy, Vivek wrote: > Hi Andrew, > >> Currently this driver creates a SGT table using the CPU as the >> target device, then performs the dma_sync operations against >> that SGT. This is backwards to how DMA-BUFs are supposed to behave. >> This may have worked for the case where these buffers were given >> only back to the same CPU that produced them as in the QEMU case. >> And only then because the original author had the dma_sync >> operations also backwards, syncing for the "device" on begin_cpu. >> This was noticed and "fixed" in this patch[0]. >> >> That then meant we were sync'ing from the CPU to the CPU using >> a pseudo-device "miscdevice". Which then caused another issue >> due to the miscdevice not having a proper DMA mask (and why should >> it, the CPU is not a DMA device). The fix for that was an even >> more egregious hack[1] that declares the CPU is coherent with >> itself and can access its own memory space.. >> >> Unwind all this and perform the correct action by doing the dma_sync >> operations for each device currently attached to the backing buffer. > Makes sense. > >> >> [0] commit 1ffe09590121 ("udmabuf: fix dma-buf cpu access") >> [1] commit 9e9fa6a9198b ("udmabuf: Set the DMA mask for the udmabuf >> device (v2)") >> >> Signed-off-by: Andrew Davis <afd@ti.com> >> --- >> drivers/dma-buf/udmabuf.c | 41 +++++++++++++++------------------------ >> 1 file changed, 16 insertions(+), 25 deletions(-) >> >> diff --git a/drivers/dma-buf/udmabuf.c b/drivers/dma-buf/udmabuf.c >> index 3a23f0a7d112a..ab6764322523c 100644 >> --- a/drivers/dma-buf/udmabuf.c >> +++ b/drivers/dma-buf/udmabuf.c >> @@ -26,8 +26,6 @@ MODULE_PARM_DESC(size_limit_mb, "Max size of a >> dmabuf, in megabytes. Default is >> struct udmabuf { >> pgoff_t pagecount; >> struct page **pages; >> - struct sg_table *sg; >> - struct miscdevice *device; >> struct list_head attachments; >> struct mutex lock; >> }; >> @@ -169,12 +167,8 @@ static void unmap_udmabuf(struct >> dma_buf_attachment *at, >> static void release_udmabuf(struct dma_buf *buf) >> { >> struct udmabuf *ubuf = buf->priv; >> - struct device *dev = ubuf->device->this_device; >> pgoff_t pg; >> >> - if (ubuf->sg) >> - put_sg_table(dev, ubuf->sg, DMA_BIDIRECTIONAL); > What happens if the last importer maps the dmabuf but erroneously > closes it immediately? Would unmap somehow get called in this case? > Good question, had to scan the framework code a bit here. I thought closing a DMABUF handle would automatically unwind any current attachments/mappings, but it seems nothing in the framework does that. Looks like that is up to the importing drivers[0]: > Once a driver is done with a shared buffer it needs to call > dma_buf_detach() (after cleaning up any mappings) and then > release the reference acquired with dma_buf_get() by > calling dma_buf_put(). So closing a DMABUF after mapping without first unmapping it would be a bug in the importer, it is not the exporters problem to check for (although some more warnings in the framework checking for that might not be a bad idea..). Andrew [0] https://www.kernel.org/doc/html/v6.7/driver-api/dma-buf.html > Thanks, > Vivek > >> - >> for (pg = 0; pg < ubuf->pagecount; pg++) >> put_page(ubuf->pages[pg]); >> kfree(ubuf->pages); >> @@ -185,33 +179,31 @@ static int begin_cpu_udmabuf(struct dma_buf >> *buf, >> enum dma_data_direction direction) >> { >> struct udmabuf *ubuf = buf->priv; >> - struct device *dev = ubuf->device->this_device; >> - int ret = 0; >> - >> - if (!ubuf->sg) { >> - ubuf->sg = get_sg_table(dev, buf, direction); >> - if (IS_ERR(ubuf->sg)) { >> - ret = PTR_ERR(ubuf->sg); >> - ubuf->sg = NULL; >> - } >> - } else { >> - dma_sync_sg_for_cpu(dev, ubuf->sg->sgl, ubuf->sg->nents, >> - direction); >> - } >> + struct udmabuf_attachment *a; >> >> - return ret; >> + mutex_lock(&ubuf->lock); >> + >> + list_for_each_entry(a, &ubuf->attachments, list) >> + dma_sync_sgtable_for_cpu(a->dev, a->table, direction); >> + >> + mutex_unlock(&ubuf->lock); >> + >> + return 0; >> } >> >> static int end_cpu_udmabuf(struct dma_buf *buf, >> enum dma_data_direction direction) >> { >> struct udmabuf *ubuf = buf->priv; >> - struct device *dev = ubuf->device->this_device; >> + struct udmabuf_attachment *a; >> >> - if (!ubuf->sg) >> - return -EINVAL; >> + mutex_lock(&ubuf->lock); >> + >> + list_for_each_entry(a, &ubuf->attachments, list) >> + dma_sync_sgtable_for_device(a->dev, a->table, direction); >> + >> + mutex_unlock(&ubuf->lock); >> >> - dma_sync_sg_for_device(dev, ubuf->sg->sgl, ubuf->sg->nents, >> direction); >> return 0; >> } >> >> @@ -307,7 +299,6 @@ static long udmabuf_create(struct miscdevice >> *device, >> exp_info.priv = ubuf; >> exp_info.flags = O_RDWR; >> >> - ubuf->device = device; >> buf = dma_buf_export(&exp_info); >> if (IS_ERR(buf)) { >> ret = PTR_ERR(buf); >> -- >> 2.39.2 >
On Tue, Jan 23, 2024 at 04:12:26PM -0600, Andrew Davis wrote: > Currently this driver creates a SGT table using the CPU as the > target device, then performs the dma_sync operations against > that SGT. This is backwards to how DMA-BUFs are supposed to behave. > This may have worked for the case where these buffers were given > only back to the same CPU that produced them as in the QEMU case. > And only then because the original author had the dma_sync > operations also backwards, syncing for the "device" on begin_cpu. > This was noticed and "fixed" in this patch[0]. > > That then meant we were sync'ing from the CPU to the CPU using > a pseudo-device "miscdevice". Which then caused another issue > due to the miscdevice not having a proper DMA mask (and why should > it, the CPU is not a DMA device). The fix for that was an even > more egregious hack[1] that declares the CPU is coherent with > itself and can access its own memory space.. > > Unwind all this and perform the correct action by doing the dma_sync > operations for each device currently attached to the backing buffer. > > [0] commit 1ffe09590121 ("udmabuf: fix dma-buf cpu access") > [1] commit 9e9fa6a9198b ("udmabuf: Set the DMA mask for the udmabuf device (v2)") > > Signed-off-by: Andrew Davis <afd@ti.com> So yeah the above hacks are terrible, but I don't think this is better. What you're doing now is that you're potentially doing the flushing multiple times, so if you have a lot of importers with life mappings this is a performance regression. It's probably time to bite the bullet and teach the dma-api about flushing for multiple devices. Or some way we can figure out which is the one device we need to pick which gives us the right amount of flushing. Cheers, Sima > --- > drivers/dma-buf/udmabuf.c | 41 +++++++++++++++------------------------ > 1 file changed, 16 insertions(+), 25 deletions(-) > > diff --git a/drivers/dma-buf/udmabuf.c b/drivers/dma-buf/udmabuf.c > index 3a23f0a7d112a..ab6764322523c 100644 > --- a/drivers/dma-buf/udmabuf.c > +++ b/drivers/dma-buf/udmabuf.c > @@ -26,8 +26,6 @@ MODULE_PARM_DESC(size_limit_mb, "Max size of a dmabuf, in megabytes. Default is > struct udmabuf { > pgoff_t pagecount; > struct page **pages; > - struct sg_table *sg; > - struct miscdevice *device; > struct list_head attachments; > struct mutex lock; > }; > @@ -169,12 +167,8 @@ static void unmap_udmabuf(struct dma_buf_attachment *at, > static void release_udmabuf(struct dma_buf *buf) > { > struct udmabuf *ubuf = buf->priv; > - struct device *dev = ubuf->device->this_device; > pgoff_t pg; > > - if (ubuf->sg) > - put_sg_table(dev, ubuf->sg, DMA_BIDIRECTIONAL); > - > for (pg = 0; pg < ubuf->pagecount; pg++) > put_page(ubuf->pages[pg]); > kfree(ubuf->pages); > @@ -185,33 +179,31 @@ static int begin_cpu_udmabuf(struct dma_buf *buf, > enum dma_data_direction direction) > { > struct udmabuf *ubuf = buf->priv; > - struct device *dev = ubuf->device->this_device; > - int ret = 0; > - > - if (!ubuf->sg) { > - ubuf->sg = get_sg_table(dev, buf, direction); > - if (IS_ERR(ubuf->sg)) { > - ret = PTR_ERR(ubuf->sg); > - ubuf->sg = NULL; > - } > - } else { > - dma_sync_sg_for_cpu(dev, ubuf->sg->sgl, ubuf->sg->nents, > - direction); > - } > + struct udmabuf_attachment *a; > > - return ret; > + mutex_lock(&ubuf->lock); > + > + list_for_each_entry(a, &ubuf->attachments, list) > + dma_sync_sgtable_for_cpu(a->dev, a->table, direction); > + > + mutex_unlock(&ubuf->lock); > + > + return 0; > } > > static int end_cpu_udmabuf(struct dma_buf *buf, > enum dma_data_direction direction) > { > struct udmabuf *ubuf = buf->priv; > - struct device *dev = ubuf->device->this_device; > + struct udmabuf_attachment *a; > > - if (!ubuf->sg) > - return -EINVAL; > + mutex_lock(&ubuf->lock); > + > + list_for_each_entry(a, &ubuf->attachments, list) > + dma_sync_sgtable_for_device(a->dev, a->table, direction); > + > + mutex_unlock(&ubuf->lock); > > - dma_sync_sg_for_device(dev, ubuf->sg->sgl, ubuf->sg->nents, direction); > return 0; > } > > @@ -307,7 +299,6 @@ static long udmabuf_create(struct miscdevice *device, > exp_info.priv = ubuf; > exp_info.flags = O_RDWR; > > - ubuf->device = device; > buf = dma_buf_export(&exp_info); > if (IS_ERR(buf)) { > ret = PTR_ERR(buf); > -- > 2.39.2 > > _______________________________________________ > Linaro-mm-sig mailing list -- linaro-mm-sig@lists.linaro.org > To unsubscribe send an email to linaro-mm-sig-leave@lists.linaro.org
> >> Currently this driver creates a SGT table using the CPU as the > >> target device, then performs the dma_sync operations against > >> that SGT. This is backwards to how DMA-BUFs are supposed to behave. > >> This may have worked for the case where these buffers were given > >> only back to the same CPU that produced them as in the QEMU case. > >> And only then because the original author had the dma_sync > >> operations also backwards, syncing for the "device" on begin_cpu. > >> This was noticed and "fixed" in this patch[0]. > >> > >> That then meant we were sync'ing from the CPU to the CPU using > >> a pseudo-device "miscdevice". Which then caused another issue > >> due to the miscdevice not having a proper DMA mask (and why should > >> it, the CPU is not a DMA device). The fix for that was an even > >> more egregious hack[1] that declares the CPU is coherent with > >> itself and can access its own memory space.. > >> > >> Unwind all this and perform the correct action by doing the dma_sync > >> operations for each device currently attached to the backing buffer. > > Makes sense. > > > >> > >> [0] commit 1ffe09590121 ("udmabuf: fix dma-buf cpu access") > >> [1] commit 9e9fa6a9198b ("udmabuf: Set the DMA mask for the udmabuf > >> device (v2)") > >> > >> Signed-off-by: Andrew Davis <afd@ti.com> > >> --- > >> drivers/dma-buf/udmabuf.c | 41 +++++++++++++++------------------------ > >> 1 file changed, 16 insertions(+), 25 deletions(-) > >> > >> diff --git a/drivers/dma-buf/udmabuf.c b/drivers/dma-buf/udmabuf.c > >> index 3a23f0a7d112a..ab6764322523c 100644 > >> --- a/drivers/dma-buf/udmabuf.c > >> +++ b/drivers/dma-buf/udmabuf.c > >> @@ -26,8 +26,6 @@ MODULE_PARM_DESC(size_limit_mb, "Max size of a > >> dmabuf, in megabytes. Default is > >> struct udmabuf { > >> pgoff_t pagecount; > >> struct page **pages; > >> - struct sg_table *sg; > >> - struct miscdevice *device; > >> struct list_head attachments; > >> struct mutex lock; > >> }; > >> @@ -169,12 +167,8 @@ static void unmap_udmabuf(struct > >> dma_buf_attachment *at, > >> static void release_udmabuf(struct dma_buf *buf) > >> { > >> struct udmabuf *ubuf = buf->priv; > >> - struct device *dev = ubuf->device->this_device; > >> pgoff_t pg; > >> > >> - if (ubuf->sg) > >> - put_sg_table(dev, ubuf->sg, DMA_BIDIRECTIONAL); > > What happens if the last importer maps the dmabuf but erroneously > > closes it immediately? Would unmap somehow get called in this case? > > > > Good question, had to scan the framework code a bit here. I thought > closing a DMABUF handle would automatically unwind any current > attachments/mappings, but it seems nothing in the framework does that. > > Looks like that is up to the importing drivers[0]: > > > Once a driver is done with a shared buffer it needs to call > > dma_buf_detach() (after cleaning up any mappings) and then > > release the reference acquired with dma_buf_get() by > > calling dma_buf_put(). > > So closing a DMABUF after mapping without first unmapping it would > be a bug in the importer, it is not the exporters problem to check It may be a bug in the importer but wouldn't the memory associated with the sg table and attachment get leaked if unmap doesn't get called in this scenario? Thanks, Vivek > for (although some more warnings in the framework checking for that > might not be a bad idea..). > > Andrew > > [0] https://www.kernel.org/doc/html/v6.7/driver-api/dma-buf.html > > > Thanks, > > Vivek > > > >> - > >> for (pg = 0; pg < ubuf->pagecount; pg++) > >> put_page(ubuf->pages[pg]); > >> kfree(ubuf->pages); > >> @@ -185,33 +179,31 @@ static int begin_cpu_udmabuf(struct dma_buf > >> *buf, > >> enum dma_data_direction direction) > >> { > >> struct udmabuf *ubuf = buf->priv; > >> - struct device *dev = ubuf->device->this_device; > >> - int ret = 0; > >> - > >> - if (!ubuf->sg) { > >> - ubuf->sg = get_sg_table(dev, buf, direction); > >> - if (IS_ERR(ubuf->sg)) { > >> - ret = PTR_ERR(ubuf->sg); > >> - ubuf->sg = NULL; > >> - } > >> - } else { > >> - dma_sync_sg_for_cpu(dev, ubuf->sg->sgl, ubuf->sg->nents, > >> - direction); > >> - } > >> + struct udmabuf_attachment *a; > >> > >> - return ret; > >> + mutex_lock(&ubuf->lock); > >> + > >> + list_for_each_entry(a, &ubuf->attachments, list) > >> + dma_sync_sgtable_for_cpu(a->dev, a->table, direction); > >> + > >> + mutex_unlock(&ubuf->lock); > >> + > >> + return 0; > >> } > >> > >> static int end_cpu_udmabuf(struct dma_buf *buf, > >> enum dma_data_direction direction) > >> { > >> struct udmabuf *ubuf = buf->priv; > >> - struct device *dev = ubuf->device->this_device; > >> + struct udmabuf_attachment *a; > >> > >> - if (!ubuf->sg) > >> - return -EINVAL; > >> + mutex_lock(&ubuf->lock); > >> + > >> + list_for_each_entry(a, &ubuf->attachments, list) > >> + dma_sync_sgtable_for_device(a->dev, a->table, direction); > >> + > >> + mutex_unlock(&ubuf->lock); > >> > >> - dma_sync_sg_for_device(dev, ubuf->sg->sgl, ubuf->sg->nents, > >> direction); > >> return 0; > >> } > >> > >> @@ -307,7 +299,6 @@ static long udmabuf_create(struct miscdevice > >> *device, > >> exp_info.priv = ubuf; > >> exp_info.flags = O_RDWR; > >> > >> - ubuf->device = device; > >> buf = dma_buf_export(&exp_info); > >> if (IS_ERR(buf)) { > >> ret = PTR_ERR(buf); > >> -- > >> 2.39.2 > >
On 1/25/24 2:30 PM, Daniel Vetter wrote: > On Tue, Jan 23, 2024 at 04:12:26PM -0600, Andrew Davis wrote: >> Currently this driver creates a SGT table using the CPU as the >> target device, then performs the dma_sync operations against >> that SGT. This is backwards to how DMA-BUFs are supposed to behave. >> This may have worked for the case where these buffers were given >> only back to the same CPU that produced them as in the QEMU case. >> And only then because the original author had the dma_sync >> operations also backwards, syncing for the "device" on begin_cpu. >> This was noticed and "fixed" in this patch[0]. >> >> That then meant we were sync'ing from the CPU to the CPU using >> a pseudo-device "miscdevice". Which then caused another issue >> due to the miscdevice not having a proper DMA mask (and why should >> it, the CPU is not a DMA device). The fix for that was an even >> more egregious hack[1] that declares the CPU is coherent with >> itself and can access its own memory space.. >> >> Unwind all this and perform the correct action by doing the dma_sync >> operations for each device currently attached to the backing buffer. >> >> [0] commit 1ffe09590121 ("udmabuf: fix dma-buf cpu access") >> [1] commit 9e9fa6a9198b ("udmabuf: Set the DMA mask for the udmabuf device (v2)") >> >> Signed-off-by: Andrew Davis <afd@ti.com> > > So yeah the above hacks are terrible, but I don't think this is better. > What you're doing now is that you're potentially doing the flushing > multiple times, so if you have a lot of importers with life mappings this > is a performance regression. I'd take lower performing but correct than fast and broken. :) Syncing for CPU/device is about making sure the CPU/device can see the data produced by the other. Some devices might be dma-coherent and syncing for them would be a NOP, but we cant know that here in this driver. Let's say we have two attached devices, one that is cache coherent and one that isn't. If we only sync for first attached device then that is converted to a NOP and we never flush like the second device needed. Same is true for devices behind IOMMU or with an L3 cache when syncing in the other direction for CPU. So we have to sync for all attached devices to ensure we get even the lowest common denominator device sync'd. It is up to the DMA-API layer to decide which syncs need to actually do something. If all attached devices are coherent then all syncs will be NOPs and we have no performance penalty. > > It's probably time to bite the bullet and teach the dma-api about flushing > for multiple devices. Or some way we can figure out which is the one > device we need to pick which gives us the right amount of flushing. > Seems like a constraint solving micro-optimization. The DMA-API layer would have to track which buffers have already been flushed from CPU cache and also track that nothing has been written into those caches since that point, only then could it skip the flush. But that is already the point of the dirty bit in the caches themselves, cleaning already clean cache lines is essentially free in hardware. And so is invalidating lines, it is just flipping a bit. Andrew > Cheers, Sima > >> --- >> drivers/dma-buf/udmabuf.c | 41 +++++++++++++++------------------------ >> 1 file changed, 16 insertions(+), 25 deletions(-) >> >> diff --git a/drivers/dma-buf/udmabuf.c b/drivers/dma-buf/udmabuf.c >> index 3a23f0a7d112a..ab6764322523c 100644 >> --- a/drivers/dma-buf/udmabuf.c >> +++ b/drivers/dma-buf/udmabuf.c >> @@ -26,8 +26,6 @@ MODULE_PARM_DESC(size_limit_mb, "Max size of a dmabuf, in megabytes. Default is >> struct udmabuf { >> pgoff_t pagecount; >> struct page **pages; >> - struct sg_table *sg; >> - struct miscdevice *device; >> struct list_head attachments; >> struct mutex lock; >> }; >> @@ -169,12 +167,8 @@ static void unmap_udmabuf(struct dma_buf_attachment *at, >> static void release_udmabuf(struct dma_buf *buf) >> { >> struct udmabuf *ubuf = buf->priv; >> - struct device *dev = ubuf->device->this_device; >> pgoff_t pg; >> >> - if (ubuf->sg) >> - put_sg_table(dev, ubuf->sg, DMA_BIDIRECTIONAL); >> - >> for (pg = 0; pg < ubuf->pagecount; pg++) >> put_page(ubuf->pages[pg]); >> kfree(ubuf->pages); >> @@ -185,33 +179,31 @@ static int begin_cpu_udmabuf(struct dma_buf *buf, >> enum dma_data_direction direction) >> { >> struct udmabuf *ubuf = buf->priv; >> - struct device *dev = ubuf->device->this_device; >> - int ret = 0; >> - >> - if (!ubuf->sg) { >> - ubuf->sg = get_sg_table(dev, buf, direction); >> - if (IS_ERR(ubuf->sg)) { >> - ret = PTR_ERR(ubuf->sg); >> - ubuf->sg = NULL; >> - } >> - } else { >> - dma_sync_sg_for_cpu(dev, ubuf->sg->sgl, ubuf->sg->nents, >> - direction); >> - } >> + struct udmabuf_attachment *a; >> >> - return ret; >> + mutex_lock(&ubuf->lock); >> + >> + list_for_each_entry(a, &ubuf->attachments, list) >> + dma_sync_sgtable_for_cpu(a->dev, a->table, direction); >> + >> + mutex_unlock(&ubuf->lock); >> + >> + return 0; >> } >> >> static int end_cpu_udmabuf(struct dma_buf *buf, >> enum dma_data_direction direction) >> { >> struct udmabuf *ubuf = buf->priv; >> - struct device *dev = ubuf->device->this_device; >> + struct udmabuf_attachment *a; >> >> - if (!ubuf->sg) >> - return -EINVAL; >> + mutex_lock(&ubuf->lock); >> + >> + list_for_each_entry(a, &ubuf->attachments, list) >> + dma_sync_sgtable_for_device(a->dev, a->table, direction); >> + >> + mutex_unlock(&ubuf->lock); >> >> - dma_sync_sg_for_device(dev, ubuf->sg->sgl, ubuf->sg->nents, direction); >> return 0; >> } >> >> @@ -307,7 +299,6 @@ static long udmabuf_create(struct miscdevice *device, >> exp_info.priv = ubuf; >> exp_info.flags = O_RDWR; >> >> - ubuf->device = device; >> buf = dma_buf_export(&exp_info); >> if (IS_ERR(buf)) { >> ret = PTR_ERR(buf); >> -- >> 2.39.2 >> >> _______________________________________________ >> Linaro-mm-sig mailing list -- linaro-mm-sig@lists.linaro.org >> To unsubscribe send an email to linaro-mm-sig-leave@lists.linaro.org >
On 1/26/24 1:25 AM, Kasireddy, Vivek wrote: >>>> Currently this driver creates a SGT table using the CPU as the >>>> target device, then performs the dma_sync operations against >>>> that SGT. This is backwards to how DMA-BUFs are supposed to behave. >>>> This may have worked for the case where these buffers were given >>>> only back to the same CPU that produced them as in the QEMU case. >>>> And only then because the original author had the dma_sync >>>> operations also backwards, syncing for the "device" on begin_cpu. >>>> This was noticed and "fixed" in this patch[0]. >>>> >>>> That then meant we were sync'ing from the CPU to the CPU using >>>> a pseudo-device "miscdevice". Which then caused another issue >>>> due to the miscdevice not having a proper DMA mask (and why should >>>> it, the CPU is not a DMA device). The fix for that was an even >>>> more egregious hack[1] that declares the CPU is coherent with >>>> itself and can access its own memory space.. >>>> >>>> Unwind all this and perform the correct action by doing the dma_sync >>>> operations for each device currently attached to the backing buffer. >>> Makes sense. >>> >>>> >>>> [0] commit 1ffe09590121 ("udmabuf: fix dma-buf cpu access") >>>> [1] commit 9e9fa6a9198b ("udmabuf: Set the DMA mask for the udmabuf >>>> device (v2)") >>>> >>>> Signed-off-by: Andrew Davis <afd@ti.com> >>>> --- >>>> drivers/dma-buf/udmabuf.c | 41 +++++++++++++++------------------------ >>>> 1 file changed, 16 insertions(+), 25 deletions(-) >>>> >>>> diff --git a/drivers/dma-buf/udmabuf.c b/drivers/dma-buf/udmabuf.c >>>> index 3a23f0a7d112a..ab6764322523c 100644 >>>> --- a/drivers/dma-buf/udmabuf.c >>>> +++ b/drivers/dma-buf/udmabuf.c >>>> @@ -26,8 +26,6 @@ MODULE_PARM_DESC(size_limit_mb, "Max size of a >>>> dmabuf, in megabytes. Default is >>>> struct udmabuf { >>>> pgoff_t pagecount; >>>> struct page **pages; >>>> - struct sg_table *sg; >>>> - struct miscdevice *device; >>>> struct list_head attachments; >>>> struct mutex lock; >>>> }; >>>> @@ -169,12 +167,8 @@ static void unmap_udmabuf(struct >>>> dma_buf_attachment *at, >>>> static void release_udmabuf(struct dma_buf *buf) >>>> { >>>> struct udmabuf *ubuf = buf->priv; >>>> - struct device *dev = ubuf->device->this_device; >>>> pgoff_t pg; >>>> >>>> - if (ubuf->sg) >>>> - put_sg_table(dev, ubuf->sg, DMA_BIDIRECTIONAL); >>> What happens if the last importer maps the dmabuf but erroneously >>> closes it immediately? Would unmap somehow get called in this case? >>> >> >> Good question, had to scan the framework code a bit here. I thought >> closing a DMABUF handle would automatically unwind any current >> attachments/mappings, but it seems nothing in the framework does that. >> >> Looks like that is up to the importing drivers[0]: >> >>> Once a driver is done with a shared buffer it needs to call >>> dma_buf_detach() (after cleaning up any mappings) and then >>> release the reference acquired with dma_buf_get() by >>> calling dma_buf_put(). >> >> So closing a DMABUF after mapping without first unmapping it would >> be a bug in the importer, it is not the exporters problem to check > It may be a bug in the importer but wouldn't the memory associated > with the sg table and attachment get leaked if unmap doesn't get called > in this scenario? > Yes the attachment data would be leaked if unattach was not called, but that is true for all DMABUF exporters. The .release() callback is meant to be the mirror of the export function and it only cleans up that. Same for attach/unattach, map/unmap, etc.. If these calls are not balanced then yes they can leak memory. Since balance is guaranteed by the API, checking the balance should be done at that level, not in each and every exporter. If your comment is that we should add those checks into the DMABUF framework layer then I would agree. Andrew > Thanks, > Vivek > >> for (although some more warnings in the framework checking for that >> might not be a bad idea..). >> >> Andrew >> >> [0] https://www.kernel.org/doc/html/v6.7/driver-api/dma-buf.html >> >>> Thanks, >>> Vivek >>> >>>> - >>>> for (pg = 0; pg < ubuf->pagecount; pg++) >>>> put_page(ubuf->pages[pg]); >>>> kfree(ubuf->pages); >>>> @@ -185,33 +179,31 @@ static int begin_cpu_udmabuf(struct dma_buf >>>> *buf, >>>> enum dma_data_direction direction) >>>> { >>>> struct udmabuf *ubuf = buf->priv; >>>> - struct device *dev = ubuf->device->this_device; >>>> - int ret = 0; >>>> - >>>> - if (!ubuf->sg) { >>>> - ubuf->sg = get_sg_table(dev, buf, direction); >>>> - if (IS_ERR(ubuf->sg)) { >>>> - ret = PTR_ERR(ubuf->sg); >>>> - ubuf->sg = NULL; >>>> - } >>>> - } else { >>>> - dma_sync_sg_for_cpu(dev, ubuf->sg->sgl, ubuf->sg->nents, >>>> - direction); >>>> - } >>>> + struct udmabuf_attachment *a; >>>> >>>> - return ret; >>>> + mutex_lock(&ubuf->lock); >>>> + >>>> + list_for_each_entry(a, &ubuf->attachments, list) >>>> + dma_sync_sgtable_for_cpu(a->dev, a->table, direction); >>>> + >>>> + mutex_unlock(&ubuf->lock); >>>> + >>>> + return 0; >>>> } >>>> >>>> static int end_cpu_udmabuf(struct dma_buf *buf, >>>> enum dma_data_direction direction) >>>> { >>>> struct udmabuf *ubuf = buf->priv; >>>> - struct device *dev = ubuf->device->this_device; >>>> + struct udmabuf_attachment *a; >>>> >>>> - if (!ubuf->sg) >>>> - return -EINVAL; >>>> + mutex_lock(&ubuf->lock); >>>> + >>>> + list_for_each_entry(a, &ubuf->attachments, list) >>>> + dma_sync_sgtable_for_device(a->dev, a->table, direction); >>>> + >>>> + mutex_unlock(&ubuf->lock); >>>> >>>> - dma_sync_sg_for_device(dev, ubuf->sg->sgl, ubuf->sg->nents, >>>> direction); >>>> return 0; >>>> } >>>> >>>> @@ -307,7 +299,6 @@ static long udmabuf_create(struct miscdevice >>>> *device, >>>> exp_info.priv = ubuf; >>>> exp_info.flags = O_RDWR; >>>> >>>> - ubuf->device = device; >>>> buf = dma_buf_export(&exp_info); >>>> if (IS_ERR(buf)) { >>>> ret = PTR_ERR(buf); >>>> -- >>>> 2.39.2 >>>
Am 26.01.24 um 18:24 schrieb Andrew Davis: > On 1/25/24 2:30 PM, Daniel Vetter wrote: >> On Tue, Jan 23, 2024 at 04:12:26PM -0600, Andrew Davis wrote: >>> Currently this driver creates a SGT table using the CPU as the >>> target device, then performs the dma_sync operations against >>> that SGT. This is backwards to how DMA-BUFs are supposed to behave. >>> This may have worked for the case where these buffers were given >>> only back to the same CPU that produced them as in the QEMU case. >>> And only then because the original author had the dma_sync >>> operations also backwards, syncing for the "device" on begin_cpu. >>> This was noticed and "fixed" in this patch[0]. >>> >>> That then meant we were sync'ing from the CPU to the CPU using >>> a pseudo-device "miscdevice". Which then caused another issue >>> due to the miscdevice not having a proper DMA mask (and why should >>> it, the CPU is not a DMA device). The fix for that was an even >>> more egregious hack[1] that declares the CPU is coherent with >>> itself and can access its own memory space.. >>> >>> Unwind all this and perform the correct action by doing the dma_sync >>> operations for each device currently attached to the backing buffer. >>> >>> [0] commit 1ffe09590121 ("udmabuf: fix dma-buf cpu access") >>> [1] commit 9e9fa6a9198b ("udmabuf: Set the DMA mask for the udmabuf >>> device (v2)") >>> >>> Signed-off-by: Andrew Davis <afd@ti.com> >> >> So yeah the above hacks are terrible, but I don't think this is better. >> What you're doing now is that you're potentially doing the flushing >> multiple times, so if you have a lot of importers with life mappings >> this >> is a performance regression. > > I'd take lower performing but correct than fast and broken. :) > > Syncing for CPU/device is about making sure the CPU/device can see > the data produced by the other. Some devices might be dma-coherent > and syncing for them would be a NOP, but we cant know that here > in this driver. Let's say we have two attached devices, one that > is cache coherent and one that isn't. If we only sync for first > attached device then that is converted to a NOP and we never flush > like the second device needed. > > Same is true for devices behind IOMMU or with an L3 cache when > syncing in the other direction for CPU. So we have to sync for all > attached devices to ensure we get even the lowest common denominator > device sync'd. It is up to the DMA-API layer to decide which syncs > need to actually do something. If all attached devices are coherent > then all syncs will be NOPs and we have no performance penalty. > >> >> It's probably time to bite the bullet and teach the dma-api about >> flushing >> for multiple devices. Or some way we can figure out which is the one >> device we need to pick which gives us the right amount of flushing. >> > > Seems like a constraint solving micro-optimization. The DMA-API layer > would have to track which buffers have already been flushed from CPU > cache and also track that nothing has been written into those caches > since that point, only then could it skip the flush. But that is already > the point of the dirty bit in the caches themselves, cleaning already > clean cache lines is essentially free in hardware. And so is invalidating > lines, it is just flipping a bit. Well to separate the functionality a bit. What the DMA-API should provide is abstracting how the platform does flushing and invalidation of caches and the information which devices uses which caches and what needs to be flushed/invalidated to allow access between devices and the CPU. In other words what's necessary is the following: 1. sync device to cpu 2. sync cpu to device 3. sync device to device 1 and 2 are already present and implemented for years, but 3 is missing together with some of the necessary infrastructure to actually implement this. E.g. we don't know which devices write into which caches etc... On top of this we need the functionality to track who has accessed which piece of data and what DMA-API functions needs to be called to make things work for a specific use case. But this is then DMA-buf, I/O layer drivers etc.. and should not belong into the DMA-API. I also strongly think that putting the SWIOTLB bounce buffer functionality into the DMA-API was not the right choice. Regards, Christian. > > Andrew > >> Cheers, Sima >> >>> --- >>> drivers/dma-buf/udmabuf.c | 41 >>> +++++++++++++++------------------------ >>> 1 file changed, 16 insertions(+), 25 deletions(-) >>> >>> diff --git a/drivers/dma-buf/udmabuf.c b/drivers/dma-buf/udmabuf.c >>> index 3a23f0a7d112a..ab6764322523c 100644 >>> --- a/drivers/dma-buf/udmabuf.c >>> +++ b/drivers/dma-buf/udmabuf.c >>> @@ -26,8 +26,6 @@ MODULE_PARM_DESC(size_limit_mb, "Max size of a >>> dmabuf, in megabytes. Default is >>> struct udmabuf { >>> pgoff_t pagecount; >>> struct page **pages; >>> - struct sg_table *sg; >>> - struct miscdevice *device; >>> struct list_head attachments; >>> struct mutex lock; >>> }; >>> @@ -169,12 +167,8 @@ static void unmap_udmabuf(struct >>> dma_buf_attachment *at, >>> static void release_udmabuf(struct dma_buf *buf) >>> { >>> struct udmabuf *ubuf = buf->priv; >>> - struct device *dev = ubuf->device->this_device; >>> pgoff_t pg; >>> - if (ubuf->sg) >>> - put_sg_table(dev, ubuf->sg, DMA_BIDIRECTIONAL); >>> - >>> for (pg = 0; pg < ubuf->pagecount; pg++) >>> put_page(ubuf->pages[pg]); >>> kfree(ubuf->pages); >>> @@ -185,33 +179,31 @@ static int begin_cpu_udmabuf(struct dma_buf *buf, >>> enum dma_data_direction direction) >>> { >>> struct udmabuf *ubuf = buf->priv; >>> - struct device *dev = ubuf->device->this_device; >>> - int ret = 0; >>> - >>> - if (!ubuf->sg) { >>> - ubuf->sg = get_sg_table(dev, buf, direction); >>> - if (IS_ERR(ubuf->sg)) { >>> - ret = PTR_ERR(ubuf->sg); >>> - ubuf->sg = NULL; >>> - } >>> - } else { >>> - dma_sync_sg_for_cpu(dev, ubuf->sg->sgl, ubuf->sg->nents, >>> - direction); >>> - } >>> + struct udmabuf_attachment *a; >>> - return ret; >>> + mutex_lock(&ubuf->lock); >>> + >>> + list_for_each_entry(a, &ubuf->attachments, list) >>> + dma_sync_sgtable_for_cpu(a->dev, a->table, direction); >>> + >>> + mutex_unlock(&ubuf->lock); >>> + >>> + return 0; >>> } >>> static int end_cpu_udmabuf(struct dma_buf *buf, >>> enum dma_data_direction direction) >>> { >>> struct udmabuf *ubuf = buf->priv; >>> - struct device *dev = ubuf->device->this_device; >>> + struct udmabuf_attachment *a; >>> - if (!ubuf->sg) >>> - return -EINVAL; >>> + mutex_lock(&ubuf->lock); >>> + >>> + list_for_each_entry(a, &ubuf->attachments, list) >>> + dma_sync_sgtable_for_device(a->dev, a->table, direction); >>> + >>> + mutex_unlock(&ubuf->lock); >>> - dma_sync_sg_for_device(dev, ubuf->sg->sgl, ubuf->sg->nents, >>> direction); >>> return 0; >>> } >>> @@ -307,7 +299,6 @@ static long udmabuf_create(struct miscdevice >>> *device, >>> exp_info.priv = ubuf; >>> exp_info.flags = O_RDWR; >>> - ubuf->device = device; >>> buf = dma_buf_export(&exp_info); >>> if (IS_ERR(buf)) { >>> ret = PTR_ERR(buf); >>> -- >>> 2.39.2 >>> >>> _______________________________________________ >>> Linaro-mm-sig mailing list -- linaro-mm-sig@lists.linaro.org >>> To unsubscribe send an email to linaro-mm-sig-leave@lists.linaro.org >>
Hi Andrew, > > On 1/26/24 1:25 AM, Kasireddy, Vivek wrote: > >>>> Currently this driver creates a SGT table using the CPU as the > >>>> target device, then performs the dma_sync operations against > >>>> that SGT. This is backwards to how DMA-BUFs are supposed to behave. > >>>> This may have worked for the case where these buffers were given > >>>> only back to the same CPU that produced them as in the QEMU case. > >>>> And only then because the original author had the dma_sync > >>>> operations also backwards, syncing for the "device" on begin_cpu. > >>>> This was noticed and "fixed" in this patch[0]. > >>>> > >>>> That then meant we were sync'ing from the CPU to the CPU using > >>>> a pseudo-device "miscdevice". Which then caused another issue > >>>> due to the miscdevice not having a proper DMA mask (and why should > >>>> it, the CPU is not a DMA device). The fix for that was an even > >>>> more egregious hack[1] that declares the CPU is coherent with > >>>> itself and can access its own memory space.. > >>>> > >>>> Unwind all this and perform the correct action by doing the dma_sync > >>>> operations for each device currently attached to the backing buffer. > >>> Makes sense. > >>> > >>>> > >>>> [0] commit 1ffe09590121 ("udmabuf: fix dma-buf cpu access") > >>>> [1] commit 9e9fa6a9198b ("udmabuf: Set the DMA mask for the > udmabuf > >>>> device (v2)") > >>>> > >>>> Signed-off-by: Andrew Davis <afd@ti.com> > >>>> --- > >>>> drivers/dma-buf/udmabuf.c | 41 +++++++++++++++------------------------ > >>>> 1 file changed, 16 insertions(+), 25 deletions(-) > >>>> > >>>> diff --git a/drivers/dma-buf/udmabuf.c b/drivers/dma-buf/udmabuf.c > >>>> index 3a23f0a7d112a..ab6764322523c 100644 > >>>> --- a/drivers/dma-buf/udmabuf.c > >>>> +++ b/drivers/dma-buf/udmabuf.c > >>>> @@ -26,8 +26,6 @@ MODULE_PARM_DESC(size_limit_mb, "Max size > of a > >>>> dmabuf, in megabytes. Default is > >>>> struct udmabuf { > >>>> pgoff_t pagecount; > >>>> struct page **pages; > >>>> - struct sg_table *sg; > >>>> - struct miscdevice *device; > >>>> struct list_head attachments; > >>>> struct mutex lock; > >>>> }; > >>>> @@ -169,12 +167,8 @@ static void unmap_udmabuf(struct > >>>> dma_buf_attachment *at, > >>>> static void release_udmabuf(struct dma_buf *buf) > >>>> { > >>>> struct udmabuf *ubuf = buf->priv; > >>>> - struct device *dev = ubuf->device->this_device; > >>>> pgoff_t pg; > >>>> > >>>> - if (ubuf->sg) > >>>> - put_sg_table(dev, ubuf->sg, DMA_BIDIRECTIONAL); > >>> What happens if the last importer maps the dmabuf but erroneously > >>> closes it immediately? Would unmap somehow get called in this case? > >>> > >> > >> Good question, had to scan the framework code a bit here. I thought > >> closing a DMABUF handle would automatically unwind any current > >> attachments/mappings, but it seems nothing in the framework does that. > >> > >> Looks like that is up to the importing drivers[0]: > >> > >>> Once a driver is done with a shared buffer it needs to call > >>> dma_buf_detach() (after cleaning up any mappings) and then > >>> release the reference acquired with dma_buf_get() by > >>> calling dma_buf_put(). > >> > >> So closing a DMABUF after mapping without first unmapping it would > >> be a bug in the importer, it is not the exporters problem to check > > It may be a bug in the importer but wouldn't the memory associated > > with the sg table and attachment get leaked if unmap doesn't get called > > in this scenario? > > > > Yes the attachment data would be leaked if unattach was not called, > but that is true for all DMABUF exporters. The .release() callback > is meant to be the mirror of the export function and it only cleans > up that. Same for attach/unattach, map/unmap, etc.. If these calls > are not balanced then yes they can leak memory. > > Since balance is guaranteed by the API, checking the balance should > be done at that level, not in each and every exporter. If your > comment is that we should add those checks into the DMABUF framework > layer then I would agree. Yeah, to avoid leaking memory, it would be even better if the framework can call unmap when the importer fails to do so. Not sure if this is easier said than done. Thanks, Vivek > > Andrew > > > Thanks, > > Vivek > > > >> for (although some more warnings in the framework checking for that > >> might not be a bad idea..). > >> > >> Andrew > >> > >> [0] https://www.kernel.org/doc/html/v6.7/driver-api/dma-buf.html > >> > >>> Thanks, > >>> Vivek > >>> > >>>> - > >>>> for (pg = 0; pg < ubuf->pagecount; pg++) > >>>> put_page(ubuf->pages[pg]); > >>>> kfree(ubuf->pages); > >>>> @@ -185,33 +179,31 @@ static int begin_cpu_udmabuf(struct > dma_buf > >>>> *buf, > >>>> enum dma_data_direction direction) > >>>> { > >>>> struct udmabuf *ubuf = buf->priv; > >>>> - struct device *dev = ubuf->device->this_device; > >>>> - int ret = 0; > >>>> - > >>>> - if (!ubuf->sg) { > >>>> - ubuf->sg = get_sg_table(dev, buf, direction); > >>>> - if (IS_ERR(ubuf->sg)) { > >>>> - ret = PTR_ERR(ubuf->sg); > >>>> - ubuf->sg = NULL; > >>>> - } > >>>> - } else { > >>>> - dma_sync_sg_for_cpu(dev, ubuf->sg->sgl, ubuf->sg->nents, > >>>> - direction); > >>>> - } > >>>> + struct udmabuf_attachment *a; > >>>> > >>>> - return ret; > >>>> + mutex_lock(&ubuf->lock); > >>>> + > >>>> + list_for_each_entry(a, &ubuf->attachments, list) > >>>> + dma_sync_sgtable_for_cpu(a->dev, a->table, direction); > >>>> + > >>>> + mutex_unlock(&ubuf->lock); > >>>> + > >>>> + return 0; > >>>> } > >>>> > >>>> static int end_cpu_udmabuf(struct dma_buf *buf, > >>>> enum dma_data_direction direction) > >>>> { > >>>> struct udmabuf *ubuf = buf->priv; > >>>> - struct device *dev = ubuf->device->this_device; > >>>> + struct udmabuf_attachment *a; > >>>> > >>>> - if (!ubuf->sg) > >>>> - return -EINVAL; > >>>> + mutex_lock(&ubuf->lock); > >>>> + > >>>> + list_for_each_entry(a, &ubuf->attachments, list) > >>>> + dma_sync_sgtable_for_device(a->dev, a->table, direction); > >>>> + > >>>> + mutex_unlock(&ubuf->lock); > >>>> > >>>> - dma_sync_sg_for_device(dev, ubuf->sg->sgl, ubuf->sg->nents, > >>>> direction); > >>>> return 0; > >>>> } > >>>> > >>>> @@ -307,7 +299,6 @@ static long udmabuf_create(struct miscdevice > >>>> *device, > >>>> exp_info.priv = ubuf; > >>>> exp_info.flags = O_RDWR; > >>>> > >>>> - ubuf->device = device; > >>>> buf = dma_buf_export(&exp_info); > >>>> if (IS_ERR(buf)) { > >>>> ret = PTR_ERR(buf); > >>>> -- > >>>> 2.39.2 > >>>
diff --git a/drivers/dma-buf/udmabuf.c b/drivers/dma-buf/udmabuf.c index 3a23f0a7d112a..ab6764322523c 100644 --- a/drivers/dma-buf/udmabuf.c +++ b/drivers/dma-buf/udmabuf.c @@ -26,8 +26,6 @@ MODULE_PARM_DESC(size_limit_mb, "Max size of a dmabuf, in megabytes. Default is struct udmabuf { pgoff_t pagecount; struct page **pages; - struct sg_table *sg; - struct miscdevice *device; struct list_head attachments; struct mutex lock; }; @@ -169,12 +167,8 @@ static void unmap_udmabuf(struct dma_buf_attachment *at, static void release_udmabuf(struct dma_buf *buf) { struct udmabuf *ubuf = buf->priv; - struct device *dev = ubuf->device->this_device; pgoff_t pg; - if (ubuf->sg) - put_sg_table(dev, ubuf->sg, DMA_BIDIRECTIONAL); - for (pg = 0; pg < ubuf->pagecount; pg++) put_page(ubuf->pages[pg]); kfree(ubuf->pages); @@ -185,33 +179,31 @@ static int begin_cpu_udmabuf(struct dma_buf *buf, enum dma_data_direction direction) { struct udmabuf *ubuf = buf->priv; - struct device *dev = ubuf->device->this_device; - int ret = 0; - - if (!ubuf->sg) { - ubuf->sg = get_sg_table(dev, buf, direction); - if (IS_ERR(ubuf->sg)) { - ret = PTR_ERR(ubuf->sg); - ubuf->sg = NULL; - } - } else { - dma_sync_sg_for_cpu(dev, ubuf->sg->sgl, ubuf->sg->nents, - direction); - } + struct udmabuf_attachment *a; - return ret; + mutex_lock(&ubuf->lock); + + list_for_each_entry(a, &ubuf->attachments, list) + dma_sync_sgtable_for_cpu(a->dev, a->table, direction); + + mutex_unlock(&ubuf->lock); + + return 0; } static int end_cpu_udmabuf(struct dma_buf *buf, enum dma_data_direction direction) { struct udmabuf *ubuf = buf->priv; - struct device *dev = ubuf->device->this_device; + struct udmabuf_attachment *a; - if (!ubuf->sg) - return -EINVAL; + mutex_lock(&ubuf->lock); + + list_for_each_entry(a, &ubuf->attachments, list) + dma_sync_sgtable_for_device(a->dev, a->table, direction); + + mutex_unlock(&ubuf->lock); - dma_sync_sg_for_device(dev, ubuf->sg->sgl, ubuf->sg->nents, direction); return 0; } @@ -307,7 +299,6 @@ static long udmabuf_create(struct miscdevice *device, exp_info.priv = ubuf; exp_info.flags = O_RDWR; - ubuf->device = device; buf = dma_buf_export(&exp_info); if (IS_ERR(buf)) { ret = PTR_ERR(buf);