在邮件列表里讨论了一下writel是如何实现的,这个函数实现在操作系统层,有内存保护的情况下,往一个寄存器或者内存地址写一个数据。
在arch/alpha/kernel/io.c中有
188 void writel(u32 b, volatile void __iomem *addr)
189 {
190     __raw_writel(b, addr);
191     mb();
192 }
 
这样一个writel函数的作用应该是向一个地址上写一个值,我想知道这个函数底下具体实现的细节,于是往下继续跟踪代码:__raw_writel(b, addr);
 
129 void __raw_writel(u32 b, volatile void __iomem *addr)
130 {
131     IO_CONCAT(__IO_PREFIX,writel)(b, addr);
132 }
 
再往下跟踪 IO_CONCAT,在对应的io.h中的定义如下:
134 #define IO_CONCAT(a,b)  _IO_CONCAT(a,b)
135 #define _IO_CONCAT(a,b) a ## _ ## b
这段代码前几天问过了,是标示将两边的字符串连接起来的意思。
跟踪__IO_PREFIX 定义如下
501 #undef __IO_PREFIX
502 #define __IO_PREFIX     apecs
到这里就结束了,再往下我就晕了,有问题如下:
1、到底是怎么将数据写入地址的?我把这些单独提取出来,进行预编译,宏展开后,发现是这样的:
void __raw_writel(                                )
{
    apecs_writel(b, addr);
}
但是在内核里根本就没找到apecs_writel函数,请帮忙解释下。
For the first question,
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.

继续阅读代码,看看定义__IO_PREFIX之后紧接着包含了哪个头文件。在哪个头文
件里面寻找答案。对于你的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;
}

就是最终通过*(volatile u32 __force *)a = b;
来写入数据的。
如果在没有os,没有mmu的情况下,当开发板裸跑的时候,我们只需要一句话就一切ok:
*(unsigned long *)addr = value;

在阅读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()相关推荐

  1. linux与RMB的关系,linux mb()/rmb()/wmb()

    在阅读linux 2.6.23内核代码中遇到mb()/rmb()/wmb() 这几个宏,不明白如何使用, 在分析其汇编代码后,大概的了解了这和内存屏障有关,代码如下: #define X86_FEAT ...

  2. js字节单位转换函数(KB MB GB TB PB EB ZB)

    先来个初级的 byteChange = byte => {let size = ''if (byte < 0.1 * 1024) { // 小于0.1KB 则转化成Bsize = byte ...

  3. Linux代码优化和内存屏障(barrier、mb、rmb、wmb)

    内存屏障主要解决的问题是编译器的优化和CPU的乱序执行. 编译器在优化的时候,生成的汇编指令可能和c语言程序的执行顺序不一样,在需要程序严格按照c语言顺序执行时,需要显式的告诉编译不需要优化,这在li ...

  4. rcu_assign_pointer、rcu_dereference、ACCESS_ONCE

    题记:看代码不要死死的一条一条往后看,要适当的联系上下文,看出整体逻辑性,流程性.事实上,编译器和处理器也不是呆板的一条一条往后执行的,它也会有预取和逻辑判断. 由内存屏障到RCU的发布订阅 内存屏障 ...

  5. 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 使用 ...

  6. 内核同步 (来自chinaunix总结)

    内核同步 内核服务请求的方式 老板(硬件中断),客人(用户态发出的系统调用,或异常) 不顾客人顾老板 顾完新老板,再顾旧老板 顾完老板,可能顾旧客人,也可能顾新客人 抢占和并发 抢占 抢占式内核的特定 ...

  7. 基于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 ...

  8. 内存屏障什么的(经典)

    转载:http://www.spongeliu.com/clanguage/memorybarrier/ 当你看到"内存屏障"四个字的时候,你的第一反应是什么?寄存器里取出了错误的 ...

  9. Why Memory Barriers中文翻译(下)

    转载自:Why Memory Barriers中文翻译(下) 在上一篇why memory barriers文档中,由于各种原因,有几个章节没有翻译.其实所谓的各种原因总结出一句话就是还没有明白那些章 ...

最新文章

  1. 线程同步之——互斥量及死锁问题
  2. 基于Boost::beast模块的同步WebSocket服务器
  3. ES5-拓展 原型链、继承、类
  4. 【渝粤教育】国家开放大学2018年秋季 0107-21T现代货币金融学 参考试题
  5. 【转】Apache 配置虚拟主机三种方式
  6. 空间数据共享与交换技术现状
  7. 在 Windows 上安装Rabbit MQ 指南
  8. 剑指offer——面试题47:不用加减乘除做加法
  9. js校验明细列表字段是否存在相同值(js循环嵌套初始值问题)
  10. Kylo 之 spark-job-profiler 源码阅读
  11. 汽车之家各种车型参数爬虫
  12. 安装apk文件到linux,linux系统如何安装apk文件
  13. Asp.net学习总结
  14. C# 和MsComm
  15. 寒武纪cnstream模型加速的python环境搭建笔记
  16. nginx证书填写路径报错:SSL: error:0200107B:system library:fopen:Unknown error:fopen
  17. 什么是Bugzilla
  18. Yii 框架执行流程
  19. iphone分辨率终极指南(含iphone6/6+)
  20. 简书python_用python轻松刷简书文章访问量

热门文章

  1. 《HelloGitHub》第 65 期
  2. qq邮箱隐藏代码html,QQ邮箱原来这么好用,4个隐藏设置格调满满
  3. 怎么将计算机的触摸鼠标锁定,戴尔笔记本触摸鼠标怎么锁定
  4. 液化气瓶口的二维码“身份证”
  5. Python多继承mro
  6. js基础--数据类型
  7. C基础学习笔记——01-C基础第02天(用户权限、VI操作、Linux服务器搭建)
  8. PAT甲级1143 Lowest Common Ancestor (30 分):[C++题解]LCA、最低公共祖先
  9. 百度经纬度和google经纬度转换测试
  10. 低成本DC/DC转换器34063的应用(图)