I/O函数 writel __raw_writel mb()/rmb()/wmb()
189 {
190 __raw_writel(b, addr);
191 mb();
192 }
130 {
131 IO_CONCAT(__IO_PREFIX,writel)(b, addr);
132 }
135 #define _IO_CONCAT(a,b) a ## _ ## b
502 #define __IO_PREFIX apecs
{
apecs_writel(b, addr);
}
you should refer to the file "arch\alpha\kernle\Machvec_impl.h"
"~\Machve.h" "~\io.c" "~\io.h" "~\core_**.h".
as you have analysized before, in the file Machvec_impl.h and Machve.h,
DO_CIA_IO,IO,IO_LITE, these three macros implement the symbole
connection between ** arch and writel function, and the function
pointer initializations.
so, the details implementation to writel is to init the
alpha_machine_vector structure and the definition to the relevant
function pointer invoked to complete the low-level write operation.
.mv_writel =CAT(low,_writel),<---IO(CIA,cia)<-->cia_writel(b, addr); <---
|
writel(b, addr)-->__raw_writel(b, addr);--->cia_writel(b,addr)---------------
For the second quesiton,
mb()--->__asm__ __volatile__("mb": : :"memory");
so, it is a memory barrier for alpha architecture to ensure some
operations before some actions could be occured.
and, it is similiar with the barrier() in x86 platform/arm platform.
件里面寻找答案。对于你的apsec,看看以下代码段(linux-2.6.28-rc4)
arch/alpha/include/asm/core_apecs.h
------------------------------------------
#undef __IO_PREFIX
#define __IO_PREFIX apecs
#define apecs_trivial_io_bw 0
#define apecs_trivial_io_lq 0
#define apecs_trivial_rw_bw 2
#define apecs_trivial_rw_lq 1
#define apecs_trivial_iounmap 1
#include <asm/io_trivial.h>
------------------------------------------
arch/alpha/include/asm/io_trivial.h
------------------------------------------
__EXTERN_INLINE void
IO_CONCAT(__IO_PREFIX,writel)(u32 b, volatile void __iomem *a)
{
*(volatile u32 __force *)a = b;
}
来写入数据的。
在阅读linux 2.6.23内核代码中遇到mb()/rmb()/wmb() 这几个宏,不明白如何使用,
在分析其汇编代码后,大概的了解了这和内存屏障有关,代码如下:
#define X86_FEATURE_XMM2 (0*32+26) /* Streaming SIMD Extensions-2 */
......
#define mb() alternative("lock; addl $0,0(%%esp)", "mfence", X86_FEATURE_XMM2)
#define rmb() alternative("lock; addl $0,0(%%esp)", "lfence", X86_FEATURE_XMM2)
#ifdef CONFIG_X86_OOSTORE
/* Actually there are no OOO store capable CPUs for now that do SSE,
but make it already an possibility. */
#define wmb() alternative("lock; addl $0,0(%%esp)", "sfence", X86_FEATURE_XMM)
#else
#define wmb() __asm__ __volatile__ ("": : :"memory")
#endif
.......
/*
* Alternative instructions for different CPU types or capabilities.
*
* This allows to use optimized instructions even on generic binary
* kernels.
*
* length of oldinstr must be longer or equal the length of newinstr
* It can be padded with nops as needed.
*
* For non barrier like inlines please define new variants
* without volatile and memory clobber.
*/
#define alternative(oldinstr, newinstr, feature) \
asm volatile ("661:\n\t" oldinstr "\n662:\n" \
".section .altinstructions,\"a\"\n" \
" .align 4\n" \
" .long 661b\n" /* label */ \
" .long 663f\n" /* new instruction */ \
" .byte %c0\n" /* feature bit */ \
" .byte 662b-661b\n" /* sourcelen */ \
" .byte 664f-663f\n" /* replacementlen */ \
".previous\n" \
".section .altinstr_replacement,\"ax\"\n" \
"663:\n\t" newinstr "\n664:\n" /* replacement */\
".previous" :: "i" (feature) : "memory")
内存屏障主要解决的问题是编译器的优化和CPU的乱序执行。
编译器在优化的时候,生成的汇编指令可能和c语言程序的执行顺序不一样,在需要 程序严格按照c语言顺序执行时,需要显式的告诉编译不需要优化,这在linux下是通过barrier()宏完成的,它依靠volidate关键字和 memory关键字,前者告诉编译barrier()周围的指令不要被优化,后者作用是告诉编译器汇编代码会使内存里面的值更改,编译器应使用内存里的新 值而非寄存器里保存的老值。
同样,CPU执行会通过乱序以提高性能。汇编里的指令不一定是按照我们看到的顺序执行的。linux中通过mb()系 列宏来保证执行的顺序。具体做法是通过mfence/lfence指令(它们是奔4后引进的,早期x86没有)以及x86指令中带有串行特性的指令(这样 的指令很多,例如linux中实现时用到的lock指令,I/O指令,操作控制寄存器、系统寄存器、调试寄存器的指令、iret指令等等)。简单的说,如 果在程序某处插入了mb()/rmb()/wmb()宏,则宏之前的程序保证比宏之后的程序先执行,从而实现串行化。wmb的实现和barrier()类 似,是因为在x86平台上,写内存的操作不会被乱序执行。
实际上在RSIC平台上,这些串行工作都有专门的指令由程序员显式的完成,比如在需要的地方调用串行指令,而不像x86上有这么多隐性的带有串行特性指令(例如lock指令)。所以在risc平台下工作的朋友通常对串行化操作理解的容易些。
因为对别的平台不了解,下面仅谈它们在ARM上的区别
__raw_writel: 因为有volatile关键字, 所以编译器不会打乱多个__raw_writel的执行顺序。
对于ARM而言,当多个写以代码的顺序到达相同设备时,执行的顺序也是被保证的,不过
对于不同的设备,执行的顺序就不被保证了。
write_relaxed: 在ARM平台上与__raw_writel一样,因为与__raw_writel相比,它只多做
了一个大端到小端转换。
writel: 当CONFIG_ARM_DMA_MEM_BUFFERABLE被定义时,在写之前,它会多做
一个DSB和L2的sync。 为什么需要这样呢? 因为,DMA buffer都是 bufferable了,
(详见加入CONFIG_ARM_DMA_MEM_BUFFERABLE的commit log)
在DMA进行之前要保证write buffer里的数据都到memory里。
所以说: 只有在有DMA操作的代码里,writel才是必须的,其实它是一个
当DMA buffer变成bufferable之后的一个补充品。因为要保证DMA buffer里的
内容在DMA开始之前都到memory里,所以开始DMA的指令(writel)里加了
sync memory的指令。
其实: writel也不能保证写的东西一定完成,它只能保证写的指令或写的内容
已经到了设备端,但具体有没有写完成是不知道的。如果有一定要设备端
写完成才能做下面的指令的要求,最好再用readl把它读回来。
几个常用的宏:likely和unlikely __raw_writel
在源码中,宏likely和unlikely 是这么定义的(位于include/linux/compiler.h):
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
要理解宏likely和unlikely ,很明显必须理解__builtin_expect。__builtin_expect是GCC(version>=2.9)引进的宏,其作用就是帮助编译器判断条件跳转的预期值,避免跳转造成时间浪费。拿下面的代码来说:
if (likely(acat == 1)) //表示大多数情况下if里面是真,程序大多数直接执行if里面的程序
而
if (unlikely (thread_memory_magazine1_is_empty (tmem, ix)))//表示大多数情况if里面为假,程序大多数直接执行else里面的程序
两段代码编译生成的汇编语句所使用到的跳转指令不一样,仔细分析下会发现__builtin_expect实际上是为了满足在大多数情况不执行跳转指令,所以__builtin_expect仅仅是告诉编译器优化,并没有改变其对真值的判断。
有关文章:http://kernelnewbies.org/FAQ/LikelyUnlikely
http://hi.baidu.com/lammy/blog/item/bc5e3d4e869073c3d1c86a89.html
http://www.cublog.cn/u/31100/showart_240658.html
__raw_writel到底干了些什么?
在linux/arch/arm/mach-at91/gpio.c 中 at91_set_gpio_output函数看到这个宏
int __init_or_module at91_set_gpio_output(unsigned pin, int value)
{
void __iomem *pio = pin_to_controller(pin);
unsigned mask = pin_to_mask(pin);
if (!pio)
return -EINVAL;
__raw_writel(mask, pio + PIO_IDR);
__raw_writel(mask, pio + PIO_PUDR);
__raw_writel(mask, pio + (value ? PIO_SODR : PIO_CODR));
__raw_writel(mask, pio + PIO_OER);
__raw_writel(mask, pio + PIO_PER);
return 0;
}
定义是:
#define __raw_writel(v,a) (__chk_io_ptr(a), *(volatile unsigned int __force *)(a) = (v))
# define __chk_io_ptr(x) (void)0
这样看就是赋值,
__chk_io_ptr()是编译器为了更细致地检查参数的属性,用于调试,正常编译时没有作用。
volatile为了防止Compiler优化,说明它是一个随时被改变的量,每次使用它的时候,不应该被假设,而要从相应的寄存器中读取。
I/O函数 writel __raw_writel mb()/rmb()/wmb()相关推荐
- linux与RMB的关系,linux mb()/rmb()/wmb()
在阅读linux 2.6.23内核代码中遇到mb()/rmb()/wmb() 这几个宏,不明白如何使用, 在分析其汇编代码后,大概的了解了这和内存屏障有关,代码如下: #define X86_FEAT ...
- js字节单位转换函数(KB MB GB TB PB EB ZB)
先来个初级的 byteChange = byte => {let size = ''if (byte < 0.1 * 1024) { // 小于0.1KB 则转化成Bsize = byte ...
- Linux代码优化和内存屏障(barrier、mb、rmb、wmb)
内存屏障主要解决的问题是编译器的优化和CPU的乱序执行. 编译器在优化的时候,生成的汇编指令可能和c语言程序的执行顺序不一样,在需要程序严格按照c语言顺序执行时,需要显式的告诉编译不需要优化,这在li ...
- rcu_assign_pointer、rcu_dereference、ACCESS_ONCE
题记:看代码不要死死的一条一条往后看,要适当的联系上下文,看出整体逻辑性,流程性.事实上,编译器和处理器也不是呆板的一条一条往后执行的,它也会有预取和逻辑判断. 由内存屏障到RCU的发布订阅 内存屏障 ...
- 04.并发和互斥.md
文章目录 4.1 什么是并发 4.2 互斥的实现 4.3 硬件互斥 4.3.1 中断禁用 4.3.2 专用机器指令 4.3.2.1 比较交换指令 4.3.2.2 exchange指令 4.3.3 使用 ...
- 内核同步 (来自chinaunix总结)
内核同步 内核服务请求的方式 老板(硬件中断),客人(用户态发出的系统调用,或异常) 不顾客人顾老板 顾完新老板,再顾旧老板 顾完老板,可能顾旧客人,也可能顾新客人 抢占和并发 抢占 抢占式内核的特定 ...
- 基于Linux 5.4.18的nvme驱动学习 - Linux相关概念 (一)
目录 1.node 2.pci_request_mem_regions 3.pci_resource_len.pci_resource_start 4.work_queue 5.dma_pool_cr ...
- 内存屏障什么的(经典)
转载:http://www.spongeliu.com/clanguage/memorybarrier/ 当你看到"内存屏障"四个字的时候,你的第一反应是什么?寄存器里取出了错误的 ...
- Why Memory Barriers中文翻译(下)
转载自:Why Memory Barriers中文翻译(下) 在上一篇why memory barriers文档中,由于各种原因,有几个章节没有翻译.其实所谓的各种原因总结出一句话就是还没有明白那些章 ...
最新文章
- 线程同步之——互斥量及死锁问题
- 基于Boost::beast模块的同步WebSocket服务器
- ES5-拓展 原型链、继承、类
- 【渝粤教育】国家开放大学2018年秋季 0107-21T现代货币金融学 参考试题
- 【转】Apache 配置虚拟主机三种方式
- 空间数据共享与交换技术现状
- 在 Windows 上安装Rabbit MQ 指南
- 剑指offer——面试题47:不用加减乘除做加法
- js校验明细列表字段是否存在相同值(js循环嵌套初始值问题)
- Kylo 之 spark-job-profiler 源码阅读
- 汽车之家各种车型参数爬虫
- 安装apk文件到linux,linux系统如何安装apk文件
- Asp.net学习总结
- C# 和MsComm
- 寒武纪cnstream模型加速的python环境搭建笔记
- nginx证书填写路径报错:SSL: error:0200107B:system library:fopen:Unknown error:fopen
- 什么是Bugzilla
- Yii 框架执行流程
- iphone分辨率终极指南(含iphone6/6+)
- 简书python_用python轻松刷简书文章访问量
热门文章
- 《HelloGitHub》第 65 期
- qq邮箱隐藏代码html,QQ邮箱原来这么好用,4个隐藏设置格调满满
- 怎么将计算机的触摸鼠标锁定,戴尔笔记本触摸鼠标怎么锁定
- 液化气瓶口的二维码“身份证”
- Python多继承mro
- js基础--数据类型
- C基础学习笔记——01-C基础第02天(用户权限、VI操作、Linux服务器搭建)
- PAT甲级1143 Lowest Common Ancestor (30 分):[C++题解]LCA、最低公共祖先
- 百度经纬度和google经纬度转换测试
- 低成本DC/DC转换器34063的应用(图)