From: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
To: Thomas Hellstrom <thomas@shipmail.org>
Cc: linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org,
thellstrom@vmware.com, airlied@redhat.com,
xen-devel@lists.xensource.com, j.glisse@redhat.com,
bskeggs@redhat.com
Subject: Re: [PATCH 06/11] ttm/driver: Expand ttm_backend_func to include two overrides for TTM page pool.
Date: Mon, 24 Oct 2011 13:27:28 -0400 [thread overview]
Message-ID: <20111024172728.GD2320@phenom.dumpdata.com> (raw)
In-Reply-To: <4EA28FA6.7000006@shipmail.org>
On Sat, Oct 22, 2011 at 11:40:54AM +0200, Thomas Hellstrom wrote:
> Konrad,
>
> I was hoping that we could get rid of the dma_address shuffling into
> core TTM,
> like I mentioned in the review. From what I can tell it's now only
> used in the backend and
> core ttm doesn't care about it.
>
> Is there a particular reason we're still passing it around?
Yes - and I should have addressed that in the writeup but forgot, sorry about that.
So initially I thought you meant this:
diff --git a/drivers/gpu/drm/ttm/ttm_page_alloc.c b/drivers/gpu/drm/ttm/ttm_page_alloc.c
index 360afb3..06ef048 100644
--- a/drivers/gpu/drm/ttm/ttm_page_alloc.c
+++ b/drivers/gpu/drm/ttm/ttm_page_alloc.c
@@ -662,8 +662,7 @@ out:
/* Put all pages in pages list to correct pool to wait for reuse */
static void __ttm_put_pages(struct list_head *pages, unsigned page_count,
- int flags, enum ttm_caching_state cstate,
- dma_addr_t *dma_address)
+ int flags, enum ttm_caching_state cstate)
{
unsigned long irq_flags;
struct ttm_page_pool *pool = ttm_get_pool(flags, cstate);
@@ -707,8 +706,7 @@ static void __ttm_put_pages(struct list_head *pages, unsigned page_count,
* cached pages.
*/
static int __ttm_get_pages(struct list_head *pages, int flags,
- enum ttm_caching_state cstate, unsigned count,
- dma_addr_t *dma_address)
+ enum ttm_caching_state cstate, unsigned count)
{
struct ttm_page_pool *pool = ttm_get_pool(flags, cstate);
struct page *p = NULL;
@@ -864,7 +862,7 @@ int ttm_get_pages(struct ttm_tt *ttm, struct list_head *pages,
if (ttm->be && ttm->be->func && ttm->be->func->get_pages)
return ttm->be->func->get_pages(ttm, pages, count, dma_address);
return __ttm_get_pages(pages, ttm->page_flags, ttm->caching_state,
- count, dma_address);
+ count)
}
void ttm_put_pages(struct ttm_tt *ttm, struct list_head *pages,
unsigned page_count, dma_addr_t *dma_address)
@@ -873,5 +871,5 @@ void ttm_put_pages(struct ttm_tt *ttm, struct list_head *pages,
ttm->be->func->put_pages(ttm, pages, page_count, dma_address);
else
__ttm_put_pages(pages, page_count, ttm->page_flags,
- ttm->caching_state, dma_address);
+ ttm->caching_state)
}
which is trivial (thought I have not compile tested it), but it should do it.
But I think you mean eliminate the dma_address handling completly in
ttm_page_alloc.c and ttm_tt.c.
For that there are couple of architectural issues I am not sure how to solve.
There has to be some form of TTM<->[Radeon|Nouveau] lookup mechanism
to say: "here is a 'struct page *', give me the bus address". Currently
this is solved by keeping an array of DMA addresses along with the list
of pages. And passing the list and DMA address up the stack (and down)
from TTM up to the driver (when ttm->be->func->populate is called and they
are handed off) does it. It does not break any API layering .. and the internal
TTM pool (non-DMA) can just ignore the dma_address altogether (see patch above).
But if we wanted to rip all mention of dma_addr from TTM, one immediate way
that comes to my mind is:
1). Provide a new function in the ttm->be->func that would be called 'get_dma' of:
(int)( *get_dma)(struct list_head *pages, unsigned page_count, dma_addr_t *dma_address)
which would call the TTM DMA to search the internal list and find 'pages*'
(which were just a microsecond ago allocated by calling ttm->be->func->get_pages)
and stick the bus address on the 'dma_address' array.
2). The radeon|nouveau driver would both call this if they decided to use the
TTM DMA API. They would need to provide the newly allocated dma_address for this
call.
3). Not sure how to wrap this in macros though - it looks as if both drivers will
be riddled with 'if (ttm->be->func->get_pages) { private->dma_addr=kzalloc(...) } else {}'.
But that is more an implemention problem..
.. While this idea looks correct, I am struck that it looks like it is breaking the layering
of APIs, where the driver is reaching behind the TTM API and calling this extra function?
Another idea is to transform the 'struct dma_addr *dma_addr' to a 'void *override_p' in
the 'struct ttm_tt'. That means still keeping the TTM API layers seperate, and "passing"
the array of DMA address through the 'override_p' array (which would be allocated by TTM DMA
code). Something along these lines (not tested):
I like this more, but I haven't actually tested it so not sure if it works right?
diff --git a/drivers/gpu/drm/nouveau/nouveau_sgdma.c b/drivers/gpu/drm/nouveau/nouveau_sgdma.c
index e0d4474..8760a04 100644
--- a/drivers/gpu/drm/nouveau/nouveau_sgdma.c
+++ b/drivers/gpu/drm/nouveau/nouveau_sgdma.c
@@ -23,7 +23,7 @@ struct nouveau_sgdma_be {
static int
nouveau_sgdma_populate(struct ttm_backend *be, unsigned long num_pages,
struct page **pages, struct page *dummy_read_page,
- dma_addr_t *dma_addrs)
+ void *override_p)
{
struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be;
struct drm_device *dev = nvbe->dev;
@@ -43,9 +43,9 @@ nouveau_sgdma_populate(struct ttm_backend *be, unsigned long num_pages,
nvbe->nr_pages = 0;
while (num_pages--) {
- if (dma_addrs[nvbe->nr_pages] != 0) {
- nvbe->pages[nvbe->nr_pages] =
- dma_addrs[nvbe->nr_pages];
+ dma_addr_t *ttm_dma = (dma_addr_t *)override_p;
+ if (ttm_dma && ttm_dma[nvbe->nr_pages] != 0) {
+ nvbe->pages[nvbe->nr_pages] = ttm_dma[nvbe->nr_pages];
nvbe->ttm_alloced[nvbe->nr_pages] = true;
} else {
nvbe->pages[nvbe->nr_pages] =
diff --git a/drivers/gpu/drm/radeon/radeon_gart.c b/drivers/gpu/drm/radeon/radeon_gart.c
index 068ba09..dc700f4 100644
--- a/drivers/gpu/drm/radeon/radeon_gart.c
+++ b/drivers/gpu/drm/radeon/radeon_gart.c
@@ -181,7 +181,7 @@ int radeon_gart_bind(struct radeon_device *rdev, unsigned offset,
p = t / (PAGE_SIZE / RADEON_GPU_PAGE_SIZE);
for (i = 0; i < pages; i++, p++) {
- if (dma_addr[i] != 0) {
+ if (dma_addr && dma_addr[i] != 0) {
rdev->gart.ttm_alloced[p] = true;
rdev->gart.pages_addr[p] = dma_addr[i];
} else {
diff --git a/drivers/gpu/drm/radeon/radeon_ttm.c b/drivers/gpu/drm/radeon/radeon_ttm.c
index 2e7419f..690545d 100644
--- a/drivers/gpu/drm/radeon/radeon_ttm.c
+++ b/drivers/gpu/drm/radeon/radeon_ttm.c
@@ -661,7 +661,7 @@ struct radeon_ttm_backend {
unsigned long num_pages;
struct page **pages;
struct page *dummy_read_page;
- dma_addr_t *dma_addrs;
+ dma_addr_t *dma_addrs; /* Can be NULL */
bool populated;
bool bound;
unsigned offset;
@@ -671,13 +671,13 @@ static int radeon_ttm_backend_populate(struct ttm_backend *backend,
unsigned long num_pages,
struct page **pages,
struct page *dummy_read_page,
- dma_addr_t *dma_addrs)
+ void *override_p)
{
struct radeon_ttm_backend *gtt;
gtt = container_of(backend, struct radeon_ttm_backend, backend);
gtt->pages = pages;
- gtt->dma_addrs = dma_addrs;
+ gtt->dma_addrs = (dma_addr_t *)override_p;
gtt->num_pages = num_pages;
gtt->dummy_read_page = dummy_read_page;
gtt->populated = true;
diff --git a/drivers/gpu/drm/ttm/ttm_page_alloc.c b/drivers/gpu/drm/ttm/ttm_page_alloc.c
index 360afb3..458727a 100644
--- a/drivers/gpu/drm/ttm/ttm_page_alloc.c
+++ b/drivers/gpu/drm/ttm/ttm_page_alloc.c
@@ -662,8 +662,7 @@ out:
/* Put all pages in pages list to correct pool to wait for reuse */
static void __ttm_put_pages(struct list_head *pages, unsigned page_count,
- int flags, enum ttm_caching_state cstate,
- dma_addr_t *dma_address)
+ int flags, enum ttm_caching_state cstate)
{
unsigned long irq_flags;
struct ttm_page_pool *pool = ttm_get_pool(flags, cstate);
@@ -707,8 +706,7 @@ static void __ttm_put_pages(struct list_head *pages, unsigned page_count,
* cached pages.
*/
static int __ttm_get_pages(struct list_head *pages, int flags,
- enum ttm_caching_state cstate, unsigned count,
- dma_addr_t *dma_address)
+ enum ttm_caching_state cstate, unsigned count)
{
struct ttm_page_pool *pool = ttm_get_pool(flags, cstate);
struct page *p = NULL;
@@ -766,7 +764,7 @@ static int __ttm_get_pages(struct list_head *pages, int flags,
printk(KERN_ERR TTM_PFX
"Failed to allocate extra pages "
"for large request.");
- __ttm_put_pages(pages, 0, flags, cstate, NULL);
+ __ttm_put_pages(pages, 0, flags, cstate);
return r;
}
}
@@ -859,19 +857,19 @@ int ttm_page_alloc_debugfs(struct seq_file *m, void *data)
}
EXPORT_SYMBOL(ttm_page_alloc_debugfs);
int ttm_get_pages(struct ttm_tt *ttm, struct list_head *pages,
- unsigned count, dma_addr_t *dma_address)
+ unsigned count, int index)
{
if (ttm->be && ttm->be->func && ttm->be->func->get_pages)
- return ttm->be->func->get_pages(ttm, pages, count, dma_address);
+ return ttm->be->func->get_pages(ttm, pages, count, index);
return __ttm_get_pages(pages, ttm->page_flags, ttm->caching_state,
- count, dma_address);
+ count);
}
void ttm_put_pages(struct ttm_tt *ttm, struct list_head *pages,
- unsigned page_count, dma_addr_t *dma_address)
+ unsigned page_count, int index)
{
if (ttm->be && ttm->be->func && ttm->be->func->put_pages)
- ttm->be->func->put_pages(ttm, pages, page_count, dma_address);
+ ttm->be->func->put_pages(ttm, pages, page_count, index);
else
__ttm_put_pages(pages, page_count, ttm->page_flags,
- ttm->caching_state, dma_address);
+ ttm->caching_state);
}
diff --git a/drivers/gpu/drm/ttm/ttm_page_alloc_dma.c b/drivers/gpu/drm/ttm/ttm_page_alloc_dma.c
index a5be62e..08e182f 100644
--- a/drivers/gpu/drm/ttm/ttm_page_alloc_dma.c
+++ b/drivers/gpu/drm/ttm/ttm_page_alloc_dma.c
@@ -45,6 +45,7 @@
#include <linux/atomic.h>
#include <linux/device.h>
#include <linux/kthread.h>
+#include <drm/drm_mem_util.h>
#include "ttm/ttm_bo_driver.h"
#include "ttm/ttm_page_alloc.h"
#ifdef TTM_HAS_AGP
@@ -1097,7 +1098,7 @@ out:
* cached pages. On failure will hold the negative return value (-ENOMEM, etc).
*/
int ttm_dma_get_pages(struct ttm_tt *ttm, struct list_head *pages,
- unsigned count, dma_addr_t *dma_address)
+ unsigned count, int idx)
{
int r = -ENOMEM;
@@ -1105,6 +1106,11 @@ int ttm_dma_get_pages(struct ttm_tt *ttm, struct list_head *pages,
gfp_t gfp_flags;
enum pool_type type;
struct device *dev = ttm->be->dev;
+ dma_addr_t *dma_address;
+
+ /* We _MUST_ have a proper index value. */
+ if (WARN_ON(idx < 0));
+ return -EINVAL;
type = ttm_to_type(ttm->page_flags, ttm->caching_state);
@@ -1129,6 +1135,7 @@ int ttm_dma_get_pages(struct ttm_tt *ttm, struct list_head *pages,
cstate);
}
#endif
+ dma_address = &((dma_addr_t *)ttm->override_p)[idx];
/* Take pages out of a pool (if applicable) */
r = ttm_dma_pool_get_pages(pool, pages, dma_address, count);
/* clear the pages coming from the pool if requested */
@@ -1201,7 +1208,7 @@ static int ttm_dma_pool_get_num_unused_pages(void)
/* Put all pages in pages list to correct pool to wait for reuse */
void ttm_dma_put_pages(struct ttm_tt *ttm, struct list_head *pages,
- unsigned page_count, dma_addr_t *dma_address)
+ unsigned page_count, int idx)
{
struct dma_pool *pool;
enum pool_type type;
@@ -1230,9 +1237,12 @@ void ttm_dma_put_pages(struct ttm_tt *ttm, struct list_head *pages,
count = ttm_dma_put_pages_in_pool(pool, pages, page_count, is_cached);
- for (i = 0; i < count; i++)
- dma_address[i] = 0;
-
+ /* Optional. */
+ if (idx >= 0) {
+ dma_addr_t *dma_address = &((dma_addr_t *)ttm->override_p)[idx];
+ for (i = 0; i < count; i++)
+ dma_address[i] = 0;
+ }
spin_lock_irqsave(&pool->lock, irq_flags);
pool->npages_in_use -= count;
if (is_cached)
@@ -1386,11 +1396,27 @@ int ttm_dma_page_alloc_debugfs(struct seq_file *m, void *data)
return 0;
}
EXPORT_SYMBOL_GPL(ttm_dma_page_alloc_debugfs);
+
+static int ttm_dma_alloc_priv(struct ttm_tt *ttm)
+{
+ ttm->override_p = drm_calloc_large(ttm->num_pages, sizeof(dma_addr_t *));
+ if (WARN_ON(!ttm->override_p))
+ return -ENOMEM;
+ return 0;
+
+}
+static void ttm_dma_free_priv(struct ttm_tt *ttm)
+{
+ drm_free_large(ttm->override_p);
+ ttm->override_p = NULL;
+}
bool ttm_dma_override(struct ttm_backend_func *be)
{
if (swiotlb_nr_tbl() && be && !ttm_dma_disable) {
be->get_pages = &ttm_dma_get_pages;
be->put_pages = &ttm_dma_put_pages;
+ be->alloc_priv = &ttm_dma_alloc_priv;
+ be->free_priv = &ttm_dma_free_priv;
return true;
}
return false;
diff --git a/drivers/gpu/drm/ttm/ttm_tt.c b/drivers/gpu/drm/ttm/ttm_tt.c
index 31ae359..0f0d57f 100644
--- a/drivers/gpu/drm/ttm/ttm_tt.c
+++ b/drivers/gpu/drm/ttm/ttm_tt.c
@@ -50,16 +50,16 @@ static int ttm_tt_swapin(struct ttm_tt *ttm);
static void ttm_tt_alloc_page_directory(struct ttm_tt *ttm)
{
ttm->pages = drm_calloc_large(ttm->num_pages, sizeof(*ttm->pages));
- ttm->dma_address = drm_calloc_large(ttm->num_pages,
- sizeof(*ttm->dma_address));
+ if (ttm->be && ttm->be->func && ttm->be->func->alloc_priv)
+ ttm->be->func->alloc_priv(ttm);
}
static void ttm_tt_free_page_directory(struct ttm_tt *ttm)
{
drm_free_large(ttm->pages);
ttm->pages = NULL;
- drm_free_large(ttm->dma_address);
- ttm->dma_address = NULL;
+ if (ttm->be && ttm->be->func && ttm->be->func->free_priv)
+ ttm->be->func->free_priv(ttm);
}
static void ttm_tt_free_user_pages(struct ttm_tt *ttm)
@@ -110,7 +110,7 @@ static struct page *__ttm_tt_get_page(struct ttm_tt *ttm, int index)
INIT_LIST_HEAD(&h);
- ret = ttm_get_pages(ttm, &h, 1, &ttm->dma_address[index]);
+ ret = ttm_get_pages(ttm, &h, 1, index);
if (ret != 0)
return NULL;
@@ -169,7 +169,7 @@ int ttm_tt_populate(struct ttm_tt *ttm)
}
be->func->populate(be, ttm->num_pages, ttm->pages,
- ttm->dummy_read_page, ttm->dma_address);
+ ttm->dummy_read_page, ttm->override_p);
ttm->state = tt_unbound;
return 0;
}
@@ -303,7 +303,7 @@ static void ttm_tt_free_alloced_pages(struct ttm_tt *ttm, bool call_clear)
count++;
}
}
- ttm_put_pages(ttm, &h, count, ttm->dma_address);
+ ttm_put_pages(ttm, &h, count, 0 /* start at zero and go up to count */);
ttm->state = tt_unpopulated;
ttm->first_himem_page = ttm->num_pages;
ttm->last_lomem_page = -1;
diff --git a/include/drm/ttm/ttm_bo_driver.h b/include/drm/ttm/ttm_bo_driver.h
index 1826c3b..771697a 100644
--- a/include/drm/ttm/ttm_bo_driver.h
+++ b/include/drm/ttm/ttm_bo_driver.h
@@ -58,7 +58,7 @@ struct ttm_backend_func {
int (*populate) (struct ttm_backend *backend,
unsigned long num_pages, struct page **pages,
struct page *dummy_read_page,
- dma_addr_t *dma_addrs);
+ void *override_p);
/**
* struct ttm_backend_func member clear
*
@@ -109,10 +109,11 @@ struct ttm_backend_func {
*
* @ttm: ttm which contains flags for page allocation and caching state.
* @pages: head of empty linked list where pages are filled.
- * @dma_address: The DMA (bus) address of pages
+ * @idx: The current index in ttm->pages[] array. Negative means
+ * don't assume ttm->pages[idx] order matches the order in *pages.
*/
int (*get_pages) (struct ttm_tt *ttm, struct list_head *pages,
- unsigned count, dma_addr_t *dma_address);
+ unsigned count, int idx);
/**
* ttm_put_pages override. The backend can override the default
@@ -124,10 +125,17 @@ struct ttm_backend_func {
* @pages: list of pages to free.
* @page_count: number of pages in the list. Zero can be passed for
* unknown count.
- * @dma_address: The DMA (bus) address of pages
+ * @idx: The current index in the ttm->pages[] array. Negative means
+ * don't assume ttm->pages[idx] order matches the order in *pages.
*/
void (*put_pages) (struct ttm_tt *ttm, struct list_head *pages,
- unsigned page_count, dma_addr_t *dma_address);
+ unsigned page_count, int idx);
+
+ /**
+ * TODO: Flesh this out.
+ */
+ int (*alloc_priv) (struct ttm_tt *ttm);
+ void (*free_priv) (struct ttm_tt *ttm);
};
/**
@@ -207,7 +215,7 @@ struct ttm_tt {
tt_unbound,
tt_unpopulated,
} state;
- dma_addr_t *dma_address;
+ void *override_p;
};
#define TTM_MEMTYPE_FLAG_FIXED (1 << 0) /* Fixed (on-card) PCI memory */
diff --git a/include/drm/ttm/ttm_page_alloc.h b/include/drm/ttm/ttm_page_alloc.h
index daf5db6..31e6079 100644
--- a/include/drm/ttm/ttm_page_alloc.h
+++ b/include/drm/ttm/ttm_page_alloc.h
@@ -68,12 +68,12 @@ static inline int ttm_dma_page_alloc_debugfs(struct seq_file *m, void *data)
* @ttm: ttm which contains flags for page allocation and caching state.
* @pages: heado of empty linked list where pages are filled.
* @count: number of pages to allocate.
- * @dma_address: The DMA (bus) address of pages - (by default zero).
+ * @idx: The current index in ttm->pages[idx]. Negative means ignore.
*/
int ttm_get_pages(struct ttm_tt *ttm,
struct list_head *pages,
unsigned count,
- dma_addr_t *dma_address);
+ int idx);
/**
* Put linked list of pages to pool.
*
@@ -81,12 +81,12 @@ int ttm_get_pages(struct ttm_tt *ttm,
* @pages: list of pages to free.
* @page_count: number of pages in the list. Zero can be passed for unknown
* count.
- * @dma_address: The DMA (bus) address of pages (by default zero).
+ * @idx: The current index in ttm->pages[idx]. Negative means ignore.
*/
void ttm_put_pages(struct ttm_tt *ttm,
struct list_head *pages,
unsigned page_count,
- dma_addr_t *dma_address);
+ int idx);
/**
* Initialize pool allocator.
*/
next prev parent reply other threads:[~2011-10-24 17:28 UTC|newest]
Thread overview: 24+ messages / expand[flat|nested] mbox.gz Atom feed top
2011-10-19 22:19 [PATCH] TTM DMA pool v2.1 Konrad Rzeszutek Wilk
2011-10-19 22:19 ` [PATCH 01/11] swiotlb: Expose swiotlb_nr_tlb function to modules Konrad Rzeszutek Wilk
2011-10-22 4:49 ` FUJITA Tomonori
2011-10-19 22:19 ` [PATCH 02/11] nouveau/radeon: Set coherent DMA mask Konrad Rzeszutek Wilk
2011-10-19 22:19 ` [PATCH 03/11] ttm/radeon/nouveau: Check the DMA address from TTM against known value Konrad Rzeszutek Wilk
2011-10-19 22:19 ` [PATCH 04/11] ttm: Wrap ttm_[put|get]_pages and extract GFP_* and caching states from 'struct ttm_tt' Konrad Rzeszutek Wilk
2011-10-19 22:19 ` [PATCH 05/11] ttm: Get rid of temporary scaffolding Konrad Rzeszutek Wilk
2011-10-19 22:19 ` [PATCH 06/11] ttm/driver: Expand ttm_backend_func to include two overrides for TTM page pool Konrad Rzeszutek Wilk
2011-10-22 9:40 ` Thomas Hellstrom
2011-10-24 17:27 ` Konrad Rzeszutek Wilk [this message]
2011-10-24 17:42 ` Thomas Hellstrom
2011-10-24 18:18 ` Konrad Rzeszutek Wilk
2011-11-01 14:37 ` Jerome Glisse
2011-11-01 14:48 ` Thomas Hellstrom
2011-10-19 22:19 ` [PATCH 07/11] ttm: Do not set the ttm->be to NULL before calling the TTM page pool to free pages Konrad Rzeszutek Wilk
2011-10-19 22:19 ` [PATCH 08/11] ttm: Provide DMA aware TTM page pool code Konrad Rzeszutek Wilk
2011-10-31 19:37 ` Jerome Glisse
2011-11-01 13:51 ` Konrad Rzeszutek Wilk
2011-10-19 22:19 ` [PATCH 09/11] ttm: Add 'no_dma' parameter to turn the TTM DMA pool off during runtime Konrad Rzeszutek Wilk
2011-10-19 22:19 ` [PATCH 10/11] nouveau/ttm/dma: Enable the TTM DMA pool if device can only do 32-bit DMA Konrad Rzeszutek Wilk
2011-10-19 22:19 ` [PATCH 11/11] radeon/ttm/dma: Enable the TTM DMA pool if the device can only do 32-bit Konrad Rzeszutek Wilk
2011-10-20 1:38 ` [PATCH] TTM DMA pool v2.1 Konrad Rzeszutek Wilk
2011-10-31 22:05 ` Jerome Glisse
-- strict thread matches above, loose matches on Subject: below --
2011-11-01 18:47 [PATCH] TTM DMA pool v2.2 or [GIT PULL] (stable/ttm.dma_pool.v2.3) for 3.3 Konrad Rzeszutek Wilk
2011-11-01 18:47 ` [PATCH 06/11] ttm/driver: Expand ttm_backend_func to include two overrides for TTM page pool Konrad Rzeszutek Wilk
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20111024172728.GD2320@phenom.dumpdata.com \
--to=konrad.wilk@oracle.com \
--cc=airlied@redhat.com \
--cc=bskeggs@redhat.com \
--cc=dri-devel@lists.freedesktop.org \
--cc=j.glisse@redhat.com \
--cc=linux-kernel@vger.kernel.org \
--cc=thellstrom@vmware.com \
--cc=thomas@shipmail.org \
--cc=xen-devel@lists.xensource.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).