scatter/gatherのつぶやき
#define BUF_SIZE (4096)
#define NUM_PAGES (BUF_SIZE / PAGE_SIZE)
struct sg_data {
struct scatterlist sg_list[NUM_PAGES];
char buf[BUF_SIZE];
};
struct sg_data *data;
/* Allocate DMA-safe memory using the scatter/gather method */
data = kmalloc(sizeof(struct sg_data), GFP_KERNEL);
if (!data) {
pr_err("Failed to allocate memory for DMA\n");
return -ENOMEM;
}
/* Initialize the scatter/gather list */
for (i = 0; i < NUM_PAGES; i++) {
sg_init_one(&data->sg_list[i], &data->buf[i * PAGE_SIZE], PAGE_SIZE);
}
/* Map the DMA-safe memory for the DMA transfer */
int sg_count = dma_map_sg(dev, data->sg_list, NUM_PAGES, DMA_TO_DEVICE);
if (sg_count <= 0) {
pr_err("Failed to map DMA buffer\n");
kfree(data);
return -EIO;
}
/* Perform the DMA transfer using the scatter/gather method */
dmaengine_submit(desc);
dma_sync_sg_for_cpu(dev, data->sg_list, NUM_PAGES, DMA_FROM_DEVICE);
/* Unmap the DMA-safe memory and free the DMA-safe memory */
dma_unmap_sg(dev, data->sg_list, NUM_PAGES, DMA_TO_DEVICE);
for (i = 0; i < NUM_PAGES; i++) {
sg_cleanup_one(&data->sg_list[i]);
}
kfree(data);
ChatGPTが書いてくれたサンプル。
にも書かれていない関数を使っているのですが...
今風のドキュメント
sg_init_one(&data->sg_list[i], &data->buf[i * PAGE_SIZE], PAGE_SIZE);
戻り値なし。エラーが来てもわからない。
int sg_count = dma_map_sg(dev, data->sg_list, NUM_PAGES, DMA_TO_DEVICE);
上記の単純なサンプルでもエラーが返る。
おさらい
sg_init_one(&data->sg_list[i], &data->buf[i * PAGE_SIZE], PAGE_SIZE);
シングルエントリSGリストを初期化する。
[in?] : SG entry : struct scatterlist * sg
[in?] : Virtual address for IO : const void * buf,
[in?] : IO length : unsigned int buflen
参考
int sg_count = dma_map_sg(dev, data->sg_list, NUM_PAGES, DMA_TO_DEVICE);
マクロ関数でdma_map_sg_attrsが本体
[in] struct device *dev
[in] struct scatterlist *sg
[in] int nents
[in] enum dma_data_direction
nentsとは渡されるスキャッタリストの数のこと。
Q.sg_init_one と sg_init_table のどちらを使うべきですか?
A.sg_init_one と sg_init_table のどちらを使うかは、特定の使用例と DMA 転送のためにマッピングされる必要のあるバッファの数に依存します。
sg_init_one は DMA 転送のために単一のバッファをマッピングするときに使用されるべきです。これはバッファを記述する1つのscatterlistエントリを初期化し、sg_chainやsg_set_bufのような関数を使用してscatterlistに追加することができます。
sg_init_table は、DMA転送のために複数のバッファをマッピングするときに使用されるべきです。これは、scatterlist の配列を含む scatterlist テーブルを初期化し、各 scatterlist はバッファを記述する scatterlist エントリのリストを含んでいます。スキャッタリストテーブルは、sg_chain や sg_set_table のような関数を使用してスキャッタリストに追加することができます。
一般的に、DMA転送のために単一のバッファをマッピングする必要があるだけなら、sg_init_oneで十分であり、sg_init_tableを使用するよりも効率的です。一方、DMA転送のために複数のバッファをマッピングする必要がある場合、sg_init_tableはスキャッタリストを管理する便利な方法を提供し、各バッファを別々にマッピングするオーバーヘッドを削減します。
sg_init_one と sg_init_table の両方は、DMA 転送のためのスキャッタリストを作成し管理するために、 sg_chain, sg_set_buf, sg_set_page, sg_map など、スキャッタリスト API の他の関数と組み合わせて使用できることも注目すべき点でしょう。
Q. sg_alloc_table と sg_init_table のどちらを使うべきですか?
A. sg_alloc_table と sg_init_table のどちらを使うかは、 scatterlist テーブルのために動的にメモリを割り当てる必要があるか、あるいはあらかじめ割り当てられたバッファを使うことができるかによって決まります。
コンパイル時に使用する必要があるスキャッタリストの最大数がわかっている場合、sg_init_table を使用して、スキャッタリストテーブルのためにあらかじめ割り当てられたバッファを初期化することができます。これは、実行時にスキャッタリストテーブルのために動的にメモリを割り当てるために sg_alloc_table を使用するよりも効率的です。
一方、使用するスキャッタリストの数が実行時に変化する可能性がある場合、または、他の理由でスキャッタリストテーブルを動的に割り当てる必要がある場合、sg_alloc_table を使用してスキャッタリストテーブルのために動的にメモリを割り当てる必要があります。
sg_init_table が引数としてあらかじめ割り当てられたバッファを必要とするのに対し、sg_alloc_table は scatterlist テーブルのために動的にメモリを割り当てるということは注目に値します。さらに、sg_init_table は事前に割り当てられたバッファが要求された数の散布表を保持するのに十分な大きさであると仮定します。一方 sg_alloc_table は要求された数の散布表に対して十分なメモリを割り当てられない場合、失敗する可能性があります。
要約すると、使用する必要のあるスキャッタリストの最大数が分かっていて、あらかじめ割り当てられたバッファがある場合、sg_init_table を使用する必要があります。使用する必要がある散布図の数が実行時に変わる可能性がある場合、あるいは他の理由で散布図テーブルを動的に割り当てる必要がある場合、sg_alloc_table を使用する必要があります。
Q. How is the number of scatter gather lists determined in DMA for Linux device drivers?
A. The number of scatter-gather lists (also known as scatter-gather entries or SG entries) needed for a DMA transfer in a Linux device driver is typically determined by the size of the data being transferred and the maximum size of each SG entry.
In a scatter-gather DMA transfer, the data to be transferred is divided into smaller pieces or buffers, and each buffer is associated with an SG entry that describes the buffer's location, size, and other relevant information. The DMA engine then reads these SG entries to perform the transfer, processing each buffer in turn.
The number of SG entries needed for a given transfer depends on the size of the data being transferred and the maximum size of each SG entry. Typically, the maximum size of an SG entry is determined by the hardware capabilities of the DMA engine and the memory layout of the system.
For example, if the DMA engine can only handle SG entries up to 4KB in size, and the driver needs to transfer a 16KB block of data, then the driver will need to create at least four SG entries, each describing a 4KB buffer of the data to be transferred.
The Linux kernel provides APIs for allocating and managing SG entries, such as sg_alloc_table() and sg_init_table(), which can be used by device drivers to create the necessary SG entries for a DMA transfer. The driver typically uses these APIs to allocate a contiguous block of memory for the SG entries, then populates each SG entry with the appropriate information about the associated buffer.
Q. How is the number of segment tables determined in DMA for Linux device drivers?
A. In Linux device drivers, the number of segment tables needed for a DMA transfer is determined by the maximum size of the transfer and the hardware capabilities of the DMA controller.
Segment tables are used in DMA transfers to describe the physical memory regions that will be accessed by the DMA controller. Each segment table entry typically represents a contiguous physical memory region, and the DMA controller reads these entries to perform the data transfer.
The number of segment tables needed for a given transfer depends on the maximum size of the transfer and the maximum size of each segment table entry. For example, if the maximum transfer size is 64KB and the maximum size of each segment table entry is 4KB, then the driver will need to create at least 16 segment table entries to describe the entire memory region being accessed.
In Linux, the DMA mapping APIs provide functions for allocating and managing segment tables, such as dma_map_sg() and dma_unmap_sg(). These functions take as input a list of scatter-gather entries describing the data to be transferred, and allocate and populate the necessary segment tables for the transfer.
The number of segment tables created by these APIs depends on the size and number of the scatter-gather entries provided by the driver, as well as the maximum size of each segment table entry supported by the DMA controller. The APIs may also take into account any hardware-specific constraints or optimizations that are needed to ensure efficient and reliable data transfer.