The translation is temporarily closed for contributions due to maintenance, please come back later.

Translation

Before calling the callback function from <function>bus_dmamap_load()</function> the segment array is stored in the stack. And it gets pre-allocated for the maximal number of segments allowed by the tag. BecauseAs a result of this the practical limit for the number of segments on i386 architecture is about 250-300 (the kernel stack is 4KB minus the size of the user structure, size of a segment array entry is 8 bytes, and some space must be left). BecausSince the array is allocated based on the maximal number this value must not be set higher than really needed. Fortunately, for most of hardware the maximal supported number of segments is much lower. But if the driver wants to handle buffers with a very large number of scatter-gather segments it should do that in portions: load part of the buffer, transfer it to the device, load next part of the buffer, and so on.
(itstool) path: sect1/para
English
Before calling the callback function from <function>bus_dmamap_load()</function> the segment array is stored in the stack. And it gets pre-allocated for the maximal number of segments allowed by the tag. As a result of this the practical limit for the number of segments on i386 architecture is about 250-300 (the kernel stack is 4KB minus the size of the user structure, size of a segment array entry is 8 bytes, and some space must be left). Since the array is allocated based on the maximal number this value must not be set higher than really needed. Fortunately, for most of hardware the maximal supported number of segments is much lower. But if the driver wants to handle buffers with a very large number of scatter-gather segments it should do that in portions: load part of the buffer, transfer it to the device, load next part of the buffer, and so on.
Context English Chinese (Simplified) (zh_CN) State
Each entry in the segments array contains the fields: 段数组中的每一项包含如下字段:
<emphasis>ds_addr</emphasis> - physical bus address of the segment <emphasis>ds_addr</emphasis> - 段物理地址
<emphasis>ds_len</emphasis> - length of the segment <emphasis>ds_len</emphasis> - 段长度
<function>void bus_dmamap_unload(bus_dma_tag_t dmat, bus_dmamap_t map)</function> <function>void bus_dmamap_unload(bus_dma_tag_t dmat, bus_dmamap_t map)</function>
unload the map. 卸载映射。
<emphasis>dmat</emphasis> - tag <emphasis>dmat</emphasis> - 标签
<emphasis>map</emphasis> - loaded map <emphasis>map</emphasis> - 已加载的映射
<function>void bus_dmamap_sync (bus_dma_tag_t dmat, bus_dmamap_t map, bus_dmasync_op_t op)</function> <function>void bus_dmamap_sync (bus_dma_tag_t dmat, bus_dmamap_t map, bus_dmasync_op_t op)</function>
Synchronise a loaded buffer with its bounce pages before and after physical transfer to or from device. This is the function that does all the necessary copying of data between the original buffer and its mapped version. The buffers must be synchronized both before and after doing the transfer. 与设备进行物理传输前后,将加载的缓冲区与其反弹页面进行同步。此函数完成原始缓冲区与其映射版本之间所有必需的数据拷贝工作。进行传输之前和之后必须对缓冲区进行同步。
<emphasis>op</emphasis> - type of synchronization operation to perform: <emphasis>op</emphasis> - 要执行的同步操作的类型:
<function>BUS_DMASYNC_PREREAD</function> - before reading from device into buffer <function>BUS_DMASYNC_PREREAD</function> - 从设备到缓冲区的读操作之前
<function>BUS_DMASYNC_POSTREAD</function> - after reading from device into buffer <function>BUS_DMASYNC_POSTREAD</function> - 从设备到缓冲区的读操作之后
<function>BUS_DMASYNC_PREWRITE</function> - before writing the buffer to device <function>BUS_DMASYNC_PREWRITE</function> - 从缓冲区到设备的写操作之前
<function>BUS_DMASYNC_POSTWRITE</function> - after writing the buffer to device <function>BUS_DMASYNC_POSTWRITE</function> - 从缓冲区到设备的写操作之后
As of now PREREAD and POSTWRITE are null operations but that may change in the future, so they must not be ignored in the driver. Synchronization is not needed for the memory obtained from <function>bus_dmamem_alloc()</function>. 当前PREREAD和POSTWRITE为空操作,但将来可能会改变,因此驱动程序中不能忽略它们。由<function>bus_dmamem_alloc()</function>获得的内存不需要同步。
Before calling the callback function from <function>bus_dmamap_load()</function> the segment array is stored in the stack. And it gets pre-allocated for the maximal number of segments allowed by the tag. As a result of this the practical limit for the number of segments on i386 architecture is about 250-300 (the kernel stack is 4KB minus the size of the user structure, size of a segment array entry is 8 bytes, and some space must be left). Since the array is allocated based on the maximal number this value must not be set higher than really needed. Fortunately, for most of hardware the maximal supported number of segments is much lower. But if the driver wants to handle buffers with a very large number of scatter-gather segments it should do that in portions: load part of the buffer, transfer it to the device, load next part of the buffer, and so on. 从<function>bus_dmamap_load()</function>中调用回调函数之前,段数组是存储在栈中的。并且是按标签允许的最大数目的段预先分配好的。这样由于i386体系结构上对段数目的实际限制约为250-300(内核栈为4KB减去用户结构的大小,段数组条目的大小为8字节,和其它必须留出来的空间)。由于数组基于最大数目而分配,因此这个值必须不能设置成超出实际需要。幸运的是,对于大多数硬件而言,所支持的段的最大数目低很多。但如果驱动程序想处理具有非常多分散/收集段的缓冲区,则应当一部分一部分地处理:加载缓冲区的一部分,传输到设备,然后加载缓冲区的下一部分,如此反复。
Another practical consequence is that the number of segments may limit the size of the buffer. If all the pages in the buffer happen to be physically non-contiguous then the maximal supported buffer size for that fragmented case would be (nsegments * page_size). For example, if a maximal number of 10 segments is supported then on i386 maximal guaranteed supported buffer size would be 40K. If a higher size is desired then special tricks should be used in the driver. 另一个实践结论是段数目可能限制缓冲区的大小。如果缓冲区中的所有页面碰巧物理上不连续,则分片情况下支持的最大缓冲区尺寸为(nsegments * page_size)。例如,如果支持的段的最大数目为10,则在i386上可以确保支持的最大缓冲区大小为40K。如果希望更大的则需要在驱动程序中使用一些特殊技巧。
If the hardware does not support scatter-gather at all or the driver wants to support some buffer size even if it is heavily fragmented then the solution is to allocate a contiguous buffer in the driver and use it as intermediate storage if the original buffer does not fit. 如果硬件根本不支持分散/收集,或者驱动程序希望即使在严重分片的情况下仍然支持某种缓冲区大小,则解决办法是:如果无法容纳下原始缓冲区,就在驱动程序中分配一个连续的缓冲区作为中间存储。
Below are the typical call sequences when using a map depend on the use of the map. The characters -&gt; are used to show the flow of time. 下面是当使用映射时的典型调用顺序,根据对映射的具体使用而不同。字符-&gt;用于显示时间流。
For a buffer which stays practically fixed during all the time between attachment and detachment of a device: 对于从连接到分离设备,这期间位置一直不变的缓冲区:
bus_dmamem_alloc -&gt; bus_dmamap_load -&gt; ...use buffer... -&gt; -&gt; bus_dmamap_unload -&gt; bus_dmamem_free bus_dmamem_alloc -&gt; bus_dmamap_load -&gt; ...use buffer... -&gt; -&gt; bus_dmamap_unload -&gt; bus_dmamem_free
bus_dmamap_create -&gt;
-&gt; bus_dmamap_load -&gt; bus_dmamap_sync(PRE...) -&gt; do transfer -&gt;
-&gt; bus_dmamap_sync(POST...) -&gt; bus_dmamap_unload -&gt;
...
-&gt; bus_dmamap_load -&gt; bus_dmamap_sync(PRE...) -&gt; do transfer -&gt;
-&gt; bus_dmamap_sync(POST...) -&gt; bus_dmamap_unload -&gt;
-&gt; bus_dmamap_destroy
bus_dmamap_create -&gt;
-&gt; bus_dmamap_load -&gt; bus_dmamap_sync(PRE...) -&gt; do transfer -&gt;
-&gt; bus_dmamap_sync(POST...) -&gt; bus_dmamap_unload -&gt;
...
-&gt; bus_dmamap_load -&gt; bus_dmamap_sync(PRE...) -&gt; do transfer -&gt;
-&gt; bus_dmamap_sync(POST...) -&gt; bus_dmamap_unload -&gt;
-&gt; bus_dmamap_destroy
For a buffer that changes frequently and is passed from outside the driver: <_:programlisting-1/> 对于从驱动程序外部传递进去,并且经常变化的缓冲区:<_:programlisting-1/>
When loading a map created by <function>bus_dmamem_alloc()</function> the passed address and size of the buffer must be the same as used in <function>bus_dmamem_alloc()</function>. In this case it is guaranteed that the whole buffer will be mapped as one segment (so the callback may be based on this assumption) and the request will be executed immediately (EINPROGRESS will never be returned). All the callback needs to do in this case is to save the physical address. 当加载由<function>bus_dmamem_alloc()</function>创建的映射时,传递进去的缓冲区的地址和大小必须和<function>bus_dmamem_alloc()</function>中使用的一样。这种情况下就可以保证整个缓冲区被作为一个段而映射(因而回调可以基于此假设),并且请求被立即执行(永远不会返回EINPROGRESS)。这种情况下回调函数需要作的只是保存物理地址。
A typical example would be: 典型示例如下:
static void
alloc_callback(void *arg, bus_dma_segment_t *seg, int nseg, int error)
{
*(bus_addr_t *)arg = seg[0].ds_addr;
}

...
int error;
struct somedata {
....
};
struct somedata *vsomedata; /* virtual address */
bus_addr_t psomedata; /* physical bus-relative address */
bus_dma_tag_t tag_somedata;
bus_dmamap_t map_somedata;
...

error=bus_dma_tag_create(parent_tag, alignment,
boundary, lowaddr, highaddr, /*filter*/ NULL, /*filterarg*/ NULL,
/*maxsize*/ sizeof(struct somedata), /*nsegments*/ 1,
/*maxsegsz*/ sizeof(struct somedata), /*flags*/ 0,
&amp;tag_somedata);
if(error)
return error;

error = bus_dmamem_alloc(tag_somedata, &amp;vsomedata, /* flags*/ 0,
&amp;map_somedata);
if(error)
return error;

bus_dmamap_load(tag_somedata, map_somedata, (void *)vsomedata,
sizeof (struct somedata), alloc_callback,
(void *) &amp;psomedata, /*flags*/0);
static void
alloc_callback(void *arg, bus_dma_segment_t *seg, int nseg, int error)
{
*(bus_addr_t *)arg = seg[0].ds_addr;
}

...
int error;
struct somedata {
....
};
struct somedata *vsomedata; /* virtual address */
bus_addr_t psomedata; /* physical bus-relative address */
bus_dma_tag_t tag_somedata;
bus_dmamap_t map_somedata;
...

error=bus_dma_tag_create(parent_tag, alignment,
boundary, lowaddr, highaddr, /*filter*/ NULL, /*filterarg*/ NULL,
/*maxsize*/ sizeof(struct somedata), /*nsegments*/ 1,
/*maxsegsz*/ sizeof(struct somedata), /*flags*/ 0,
&amp;tag_somedata);
if(error)
return error;

error = bus_dmamem_alloc(tag_somedata, &amp;vsomedata, /* flags*/ 0,
&amp;map_somedata);
if(error)
return error;

bus_dmamap_load(tag_somedata, map_somedata, (void *)vsomedata,
sizeof (struct somedata), alloc_callback,
(void *) &amp;psomedata, /*flags*/0);
Looks a bit long and complicated but that is the way to do it. The practical consequence is: if multiple memory areas are allocated always together it would be a really good idea to combine them all into one structure and allocate as one (if the alignment and boundary limitations permit). 代码看起来有点长,也比较复杂,但那是正确的使用方法。实际结果是:如果分配多个内存区域,则总将它们组合成一个结构并作为整体分配(如果对齐和边界限制允许的话)是一个很好的主意。
When loading an arbitrary buffer into the map created by <function>bus_dmamap_create()</function> special measures must be taken to synchronize with the callback in case it would be delayed. The code would look like: 当加载任意缓冲区到由<function>bus_dmamap_create()</function>创建的映射时,由于回调可能被延迟,因此必须采取特殊措施与回调函数进行同步。代码看起来像下面的样子:
{
int s;
int error;

s = splsoftvm();
error = bus_dmamap_load(
dmat,
dmamap,
buffer_ptr,
buffer_len,
callback,
/*callback_arg*/ buffer_descriptor,
/*flags*/0);
if (error == EINPROGRESS) {
/*
* Do whatever is needed to ensure synchronization
* with callback. Callback is guaranteed not to be started
* until we do splx() or tsleep().
*/
}
splx(s);
}
{
int s;
int error;

s = splsoftvm();
error = bus_dmamap_load(
dmat,
dmamap,
buffer_ptr,
buffer_len,
callback,
/*callback_arg*/ buffer_descriptor,
/*flags*/0);
if (error == EINPROGRESS) {
/*
* Do whatever is needed to ensure synchronization
* with callback. Callback is guaranteed not to be started
* until we do splx() or tsleep().
*/
}
splx(s);
}
Two possible approaches for the processing of requests are: 处理请求的两种方法分别是:
1. If requests are completed by marking them explicitly as done (such as the CAM requests) then it would be simpler to put all the further processing into the callback driver which would mark the request when it is done. Then not much extra synchronization is needed. For the flow control reasons it may be a good idea to freeze the request queue until this request gets completed. 1. 如果通过显式地标记请求已经结束来完成请求(例如CAM请求),则将所有进一步的处理放入回调驱动程序中会比较简单,回调结束后会标记请求。之后不需要太多额外的同步。由于流控制的原因,冻结请求队列直到请求完成才释放可能是个好主意。

Loading…

Before calling the callback function from <function>bus_dmamap_load()</function> the segment array is stored in the stack. And it gets pre-allocated for the maximal number of segments allowed by the tag. BecauseAs a result of this the practical limit for the number of segments on i386 architecture is about 250-300 (the kernel stack is 4KB minus the size of the user structure, size of a segment array entry is 8 bytes, and some space must be left). BecausSince the array is allocated based on the maximal number this value must not be set higher than really needed. Fortunately, for most of hardware the maximal supported number of segments is much lower. But if the driver wants to handle buffers with a very large number of scatter-gather segments it should do that in portions: load part of the buffer, transfer it to the device, load next part of the buffer, and so on.
a month ago
Browse all component changes

Glossary

English Chinese (Simplified) (zh_CN)
No related strings found in the glossary.

Source information

Source string comment
(itstool) path: sect1/para
Source string location
book.translate.xml:17809
String age
a month ago
Source string age
a month ago
Translation file
books/zh_CN/arch-handbook.po, string 1935