将设备内存与用户映射相关联
第10 章• 映射设备和内核内存185
示例10–1 使用devmap_devmem_setup() 例程(续)
off_t regsize;
/* Set up data access attribute structure */
struct ddi_device_acc_attr xx_acc_attr = {
DDI_DEVICE_ATTR_V0,
DDI_NEVERSWAP_ACC,
DDI_STRICTORDER_ACC
};
xsp = ddi_get_soft_state(statep, getminor(dev));
if (xsp == NULL)
return (-1);
/* use register set 0 */
rnumber = 0;
/* get size of register set */
if (ddi_dev_regsize(xsp->dip, rnumber, ®size) != DDI_SUCCESS)
return (-1);
/* round up len to a multiple of a page size */
len = ptob(btopr(len));
if (off + len > regsize)
return (-1);
/* Set up the device mapping */
error = devmap_devmem_setup(handle, xsp->dip, NULL, rnumber,
将设备内存与用户映射相关联
186 编写设备驱动程序• 2006 年11 月
示例10–1 使用devmap_devmem_setup() 例程(续)
off, len, PROT_ALL, DEVMAP_DEFAULTS, &xx_acc_attr);
/* acknowledge the entire range */
*maplen = len;
return (error);
}
将内核内存与用户映射相关联
一些设备驱动程序可能需要分配可供用户程序通过mmap(2) 进行访问的内核内存。一个示例
是为两个应用程序间的通信设置共享内存。另一个示例是在驱动程序和应用程序之间共享
内存。
将内核内存导出到用户应用程序时,请执行以下步骤:
1. 使用ddi_umem_alloc(9F) 分配内核内存。
2. 使用devmap_umem_setup(9F) 导出内存。
3. 不再需要内存时,使用ddi_umem_free(9F) 释放内存。
为用户访问分配内核内存
使用ddi_umem_alloc(9F) 可以分配导出到应用程序的内核内存。ddi_umem_alloc() 使用以下
语法:
void *ddi_umem_alloc(size_t size, int flag, ddi_umem_cookie_t
*cookiep);
其中:
size 要分配的字节数。
flag 用于确定休眠条件和内存类型。
cookiep 指向内核内存cookie 的指针。
ddi_umem_alloc(9F) 分配按页对齐(Page-aligned) 的内核内存。ddi_umem_alloc() 可返回一个
指向所分配内存的指针。最初,内存被零填充。分配的字节数是系统页面大小的倍数,该
页面大小是通过size 参数向上舍入得到的。分配的内存可在内核中使用。此内存也可导出到
应用程序。cookiep 是指向用来描述所分配的内核内存的内核内存cookie 的指针。驱动程序
将内核内存导出到用户应用程序时,devmap_umem_setup(9F) 中会使用cookiep。
将内核内存与用户映射相关联
第10 章• 映射设备和内核内存187
flag 参数用于指示ddi_umem_alloc(9F) 是立即阻塞还是返回,以及分配的内核内存是否可换
页。flag 参数的值如下所示:
DDI_UMEM_NOSLEEP 驱动程序无需等待内存成为可用。如果内存不可用,则返回
NULL。
DDI_UMEM_SLEEP 驱动程序可以无限等待,直到内存可用为止。
DDI_UMEM_PAGEABLE 驱动程序允许内存页被换出。如果未设置,则锁定内存。
ddi_umem_lock() 可以执行设备锁定内存检查。此函数针对
project.max-device-locked-memory 中指定的限制值进行检查。如果当前项目的锁定内存使
用量低于限制,则会增加项目的锁定内存字节计数。进行限制检查后,内存会锁定。
ddi_umem_unlock() 可以解除锁定内存,从而减少项目的锁定内存字节计数。
其中所用的记帐方法是不严密的"full price"(足价)模式。例如,对于同一项目中
umem_lockmemory() 的具有重叠内存区域的两个调用程序会被计数两次。有关
project.max-device-locked-memory 调整的详细信息,请参见prctl(1)。
以下示例说明如何为应用程序访问分配内核内存。驱动程序会导出一页内核内存,它将被
多个应用程序用作共享存储区。应用程序第一次映射共享页时,会在segmap(9E) 中分配内
存。如果驱动程序必须支持多个应用程序数据模型,则会再分配一页。例如,64 位驱动程
序可能同时将内存导出到64 位应用程序和32 位应用程序。64 位应用程序共享第一页,32
位应用程序共享第二页。
示例10–2 使用ddi_umem_alloc() 例程
static int
xxsegmap(dev_t dev, off_t off, struct as *asp, caddr_t *addrp, off_t len,
unsigned int prot, unsigned int maxprot, unsigned int flags,
cred_t *credp)
{
int error;
minor_t instance = getminor(dev);
struct xxstate *xsp = ddi_get_soft_state(statep, instance);
size_t mem_size;
/* 64-bit driver supports 64-bit and 32-bit applications */
将内核内存与用户映射相关联
188 编写设备驱动程序• 2006 年11 月
示例10–2 使用ddi_umem_alloc() 例程(续)
switch (ddi_mmap_get_model()) {
case DDI_MODEL_LP64:
mem_size = ptob(2);
break;
case DDI_MODEL_ILP32:
mem_size = ptob(1);
break;
}
mutex_enter(&xsp->mu);
if (xsp->umem == NULL) {
/* allocate the shared area as kernel pageable memory */
xsp->umem = ddi_umem_alloc(mem_size,
DDI_UMEM_SLEEP| DDI_UMEM_PAGEABLE, &xsp->ucookie);
}
mutex_exit(&xsp->mu);
/* Set up the user mapping */
error = devmap_setup(dev, (offset_t)off, asp, addrp, len,
prot, maxprot, flags, credp);
return (error);
将内核内存与用户映射相关联
第10 章• 映射设备和内核内存189
示例10–2 使用ddi_umem_alloc() 例程(续)
}
将内核内存导出到应用程序
使用devmap_umem_setup(9F) 可将内核内存导出到用户应用程序。devmap_umem_setup() 必须
通过驱动程序的devmap(9E) 入口点进行调用。devmap_umem_setup() 的语法如下所示:
int devmap_umem_setup(devmap_cookie_t handle, dev_info_t *dip,
struct devmap_callback_ctl *callbackops, ddi_umem_cookie_t cookie,
offset_t koff, size_t len, uint_t maxprot, uint_t flags,
ddi_device_acc_attr_t *accattrp);
其中:
handle 用于描述映射的不透明结构。
dip 指向设备的dev_info 结构的指针。
callbackops 指向devmap_callback_ctl(9S) 结构的指针。
cookie ddi_umem_alloc(9F) 返回的内核内存cookie。
koff cookie 指定的内核内存中的偏移。
len 导出的长度(以字节为单位)。
maxprot 用于为导出的映射指定可能的最大保护。
flags 必须设置为DEVMAP_DEFAULTS。
accattrp 指向ddi_device_acc_attr(9S) 结构的指针。
handle 是系统用来标识映射的设备映射句柄。handle 通过devmap(9E) 入口点传入。dip 是指
向设备的dev_info 结构的指针。callbackops 允许向驱动程序通知有关映射的用户事件。导
出内核内存时,大多数驱动程序都会将callbackops 设置为NULL。
koff 和len 用于在ddi_umem_alloc(9F) 分配的内核内存中指定一个范围。如果用户的应用程
序映射位于通过devmap(9E) 入口点传入的偏移上,则可对此范围进行访问。驱动程序通常
将devmap(9E) 偏移直接传递给devmap_umem_setup(9F)。然后,mmap(2) 的返回地址将映射到
ddi_umem_alloc(9F) 返回的内核地址。koff 和len 必须按页对齐。
通过maxprot,驱动程序可为导出的内核内存中的不同区域指定不同的保护。例如,一个区
域可能不允许仅通过设置PROT_READ 和PROT_USER 来进行写访问。
将内核内存与用户映射相关联
190 编写设备驱动程序• 2006 年11 月
以下示例说明如何将内核内存导出到应用程序。驱动程序首先检查请求的映射是否位于分
配的内核内存区域之内。如果64 位驱动程序收到来自32 位应用程序的映射请求,则会将该
请求重定向到内核存储区的第二页。此重定向可确保仅有编译到相同数据模型的应用程序
才能共享相同的页。
示例10–3 devmap_umem_setup(9F) 例程
static int
xxdevmap(dev_t dev, devmap_cookie_t handle, offset_t off, size_t len,
size_t *maplen, uint_t model)
{
struct xxstate *xsp;
int error;
/* round up len to a multiple of a page size */
len = ptob(btopr(len));
/* check if the requested range is ok */
if (off + len > ptob(1))
return (ENXIO);
xsp = ddi_get_soft_state(statep, getminor(dev));
if (xsp == NULL)
return (ENXIO);
if (ddi_model_convert_from(model) == DDI_MODEL_ILP32)
/* request from 32-bit application. Skip first page */
off += ptob(1);
将内核内存与用户映射相关联
第10 章• 映射设备和内核内存191
示例10–3 devmap_umem_setup(9F) 例程(续)
/* export the memory to the application */
error = devmap_umem_setup(handle, xsp->dip, NULL, xsp->ucookie,
off, len, PROT_ALL, DEVMAP_DEFAULTS, NULL);
*maplen = len;
return (error);
}
释放为用户访问导出的内核内存
卸载驱动程序时,必须通过调用ddi_umem_free(9F) 释放ddi_umem_alloc(9F) 分配的内存。
void ddi_umem_free(ddi_umem_cookie_t cookie);
cookie 是ddi_umem_alloc(9F) 返回的内核内存cookie。
将内核内存与用户映射相关联
192 编写设备驱动程序• 2006 年11 月
设备上下文管理
一些设备驱动程序(如用于图形硬件的驱动程序)可为用户进程提供对设备的直接访问。
这些设备通常要求一次仅有一个进程访问设备。
本章介绍了可供设备驱动程序用于管理对此类设备的访问的接口集。
第193 页中的“设备上下文简介”
第193 页中的“上下文管理模型”
第195 页中的“上下文管理操作”
设备上下文简介
本节介绍设备上下文和上下文管理模型。
什么是设备上下文?
设备的上下文是指设备硬件的当前状态。设备驱动程序可代表进程管理该进程的设备上下
文。驱动程序必须分别为访问设备的每个进程保留单独的设备上下文。设备驱动程序负责
在进程访问设备时恢复正确的设备上下文。
上下文管理模型
帧缓冲区可作为设备上下文管理的一个很好的示例。使用加速的帧缓冲区,用户进程可以
通过内存映射访问直接处理设备的控制寄存器。由于这些进程不使用传统的系统调用,因
此访问设备的进程无需调用设备驱动程序。但是,如果进程要访问设备,则必须通知设备
驱动程序。驱动程序需要恢复正确的设备上下文并且提供所需的任何同步。
要解决这一问题,可以使用设备上下文管理接口,在用户进程访问设备的内存映射区域时
通知设备驱动程序,并控制对设备硬件的访问。设备驱动程序负责同步和管理各种设备上
下文。用户进程访问映射时,设备驱动程序必须为该进程恢复正确的设备上下文。
11 第1 1 章
193
每次用户进程执行以下任一操作时,都会通知设备驱动程序:
访问映射
复制映射
释放映射
创建映射
下图显示了映射到一个设备的内存中的多个用户进程。驱动程序授予了进程B 对设备的访
问权限,进程B 不再向驱动程序通知访问情况。但是,如果进程A或进程C 访问设备,仍
会通知驱动程序。
图11–1设备上下文管理
在将来某一时刻,进程A将访问设备。设备驱动程序会得到通知,并阻止进程B 将来对该
设备的访问。然后,驱动程序会为进程B 保存设备上下文。驱动程序恢复进程A的设备上
下文,然后授予进程A访问权限,如下图所示。此时,如果进程B 或进程C 访问设备,则
会通知设备驱动程序。
图11–2 切换到用户进程A的设备上下文
在多处理器计算机中,多个进程可能会尝试同时访问设备。此情况会引起抖动。有些设备
需要较长的时间才能恢复设备上下文。要防止恢复设备上下文所需的CPU 时间超过实际使
用该设备上下文所需的时间,可以使用devmap_set_ctx_timeout(9F) 设置进程访问设备所需
的最短时间。
内核可以保证一旦设备驱动程序向某一进程授予了访问权限,便不允许其他任何进程在
devmap_set_ctx_timeout(9F) 指定的时间间隔内请求访问同一设备。
设备上下文简介
194 编写设备驱动程序• 2006 年11 月
上下文管理操作
执行设备上下文管理的常规步骤如下:
1. 定义devmap_callback_ctl(9S) 结构。
2. 根据需要分配用于保存设备上下文的空间。
3. 通过devmap_devmem_setup(9F) 设置到设备的用户映射和驱动程序通知。
4. 通过devmap_load(9F) 和devmap_unload(9F) 管理用户对设备的访问。
5. 根据需要释放设备上下文结构。
devmap_callback_ctl 结构
设备驱动程序必须分配并初始化devmap_callback_ctl(9S) 结构,以便通知系统用于设备上
下文管理的入口点例程。
此结构使用以下语法:
struct devmap_callback_ctl {
int devmap_rev;
int (*devmap_map)(devmap_cookie_t dhp, dev_t dev,
uint_t flags, offset_t off, size_t len, void **pvtp);
int (*devmap_access)(devmap_cookie_t dhp, void *pvtp,
offset_t off, size_t len, uint_t type, uint_t rw);
int (*devmap_dup)(devmap_cookie_t dhp, void *pvtp,
devmap_cookie_t new_dhp, void **new_pvtp);
void (*devmap_unmap)(devmap_cookie_t dhp, void *pvtp,
offset_t off, size_t len, devmap_cookie_t new_dhp1,
void **new_pvtp1, devmap_cookie_t new_dhp2,
void **new_pvtp2);
};
devmap_rev devmap_callback_ctl 结构的版本号。版本号必须设置为
DEVMAP_OPS_REV。
devmap_map 必须设置为驱动程序的devmap_map(9E) 入口点的地址。
devmap_access 必须设置为驱动程序的devmap_access(9E) 入口点的地址。
阅读(563) | 评论(0) | 转发(0) |