spark架构代码分析-设备初始化

        操作系统在启动时会扫描I/O设备,以确定设备所使用的I/O端口,内存以及中断号等资源,以提供给设备管理模块如驱动等系统其他部分使用,这个过程就叫做设备初始化。不过在T2上,I/O设备扫描的工作并不是由并不是由处于priviled模式下的Linux来完成的,而是由处于hypervilged模式下的hypervisor来完成的。在系统启动时,OBP会把hypervisor扫描到的设备资源以设备节点树的形式呈现给Linux。因此T2 Linux设备初始化的一个工作是与该这个节点树交互,建立自己的设备资源列表和设备基本结构。
        在T2 CPU上集成了PCIe总线中的root complex,因此T2 Linux设备初始化会以这个root complex作为PCIe主控器初始化PCI总线以及该总线上扩出的pci总线及pci设备。
本章的主要内容包括一下三个部分代码的分析:
        1,Linux中的设备节点树的建立,它会与OBP设备节点树交互,在Linux中建立一个设备节点树,在之后的初始化过程中,从该节点树中得到设备的资源信息。
        2,of虚拟总线的建立,会建立一个虚拟的of总线,作为系统中所有设备挂载的根总线。
        3,PCI总线及设备的初始化,会初始化好pci总线及设备的基本结构,并以设备驱动模型的形式组织起来。

        Linux中的设备节点树的建立:
        代表设备节点树一个节点的数据结构定义如下:

 struct device_node {
         const char      *name;
         const char      *type;
         phandle node;
         char    *path_component_name;
         char    *full_name;   
         truct  property *properties;
    	 struct  property *deadprops; 
     	struct  device_node *parent;
     	struct  device_node *child;
     	struct  device_node *sibling;
     	struct  device_node *next;      
     	struct  device_node *allnext;   
     	struct  proc_dir_entry *pde;    
    	 struct  kref kref;
   		  unsigned long _flags;
   		  void    *data;
   		  unsigned int unique_id;
   	   struct of_irq_controller *irq_trans;
 };

        该结构的各成员的含义如下:
        Name:该节点的名字,该名字其实就是该节点所代表设备的名字。
        Type:该节点所代表设备的设备类型。
        Node:该节点的节点号。
        path_component_name:该成员是一个组合的名字,它是由name成员与该设备的索引组成的,用来与父节点的名称组合成一个完整的名字,比如系统中第一个pci网卡的名称可能是ethernet,则name成员的即是字符串“name“,而这里的path_component_name成员则是字符串"ethernet@0",不过设备节点树的根节点的path_component_name成员却不适用这一规则,因为根节点并不与一个具体的设备实体相关联,因此它叫什么名字并不重要,根节点的名字直接赋值为NULL。
        full_name:全名,即该节点设备所代表设备的完整的名字,它是由父节点的full_name与该节点的path_component_name组成的,比如如果系统中第一个pci网卡的父节点的full_name是"/pci@0/pci@1"的话,该节点的全名就是"/pci@0/pci@1/ethernet@0",设备节点树的根节点的full_name直接赋值为"/"。
        Properties:该成员后根的是该节点的属性链表,其中每一个成员都是该节点的一个属性,代表该节点所代表设备所使用的某一种资源的信息。
        Deadprops:该成员后跟的是已经删除的属性。
        Parent:指向该节点的父节点。
        Child:指向该节点的第一个子节点。
        Sibling:指向该节点的兄弟节点,它们有一个共同的父节点。
        Next:该成员把相同类型设备的节点链接到一个链表中。
        Allnext:该成员把设备节点树中的所有节点链接到一个链表中。
        这些是一些比较重要的成员,其他的成员就不做介绍了。

        每个节点都有很多属性,每一个属性都代表了设备的某一种资源信息,代表节点属性的结构体定义如下:

 struct property {
         char    *name;
         int     length;
         void    *value;
         struct property *next;
         unsigned long _flags;
         unsigned int unique_id;
 };

        其各成员含义如下:
        Name:属性的名称。
        Length:属性的长度。
        Value:属性的值。
        Next:该成员把同一节点的所有属性链接到一个链表中。

        T2 Linux设备节点树的代码定义在文件arch/sparc/kernel/prom_common.c文件中,它是由函数prom_build_devicetree来完成的,我们来看这个函数:

306 void __init prom_build_devicetree(void)
307 {
308         struct device_node **nextp;
309 
310         allnodes = prom_create_node(prom_root_node, NULL);
311         allnodes->path_component_name = "";
312         allnodes->full_name = "/";
313 
314         nextp = &allnodes->allnext;
315         allnodes->child = prom_build_tree(allnodes,
316                                    prom_getchild(allnodes->node),
317                                    &nextp);
318         of_console_init();
319 
320         printk("PROM: Built device tree with %u bytes of memory.\n",
321                prom_early_allocated);
322 }

        第310行,allnodes是一个全局变量,它保存了指向设备节点树根节点的指针,通过该变量就能够找到设备节点树,prom_create_node函数会创建一个设备节点树的节点,在这里它是创建设备节点树的根节点,根节点的节点号prom_root_node在很早之前就已经得到了,我们在head_64.S文件的分析中,提到过,你可以在那里去看。我们来看这个函数:

223 static struct device_node * __init prom_create_node(phandle node,
224                                                     struct device_node *parent)
225 {
226         struct device_node *dp;
227 
228         if (!node)
229                 return NULL;
230 
231         dp = prom_early_alloc(sizeof(*dp));
232         dp->unique_id = prom_unique_id++;
233         dp->parent = parent;
234 
235         kref_init(&dp->kref);
236 
237         dp->name = get_one_property(node, "name");
238         dp->type = get_one_property(node, "device_type");
239         dp->node = node;
240 
241         dp->properties = build_prop_list(node);
242 
243         irq_trans_init(dp);
244 
245         return dp;
246 }

        第231行,为该节点分配空间。
        第232行,为节点的unique_id成员赋值,该成员可以看作是节点的id号,它实        际上就是按照节点树中所有节点创建的顺序从0开始逐次递加计算出来的一个值。
        第233行,使parent成员指向其父节点,父节点的指针是通过参数传进来的,对与根节点来说,该值为空。
        第235行,初始化节点的引用计数。
        第237行,以该节点的节点号为参数调用get_one_property从OBP的设备节点树中得到节点的name属性,它实际上就是设备的名称,赋值给节点的name成员。
        第238行,从OBP的设备节点树,得到设备的设备类型,赋值给节点的device_type成员。
        第239行,把节点的节点号赋值给节点的node成员。
        第241行,初始化节点的properties,他会从OBP节点树中得到该节点的所有属性,然后链接到properties成员中形成一个链表。build_prop_list函数我们过一会儿分析。
        第243行,irq_trans_init函数与中断初始化函数相关,它会为不同类型的设备提过不同的分配中断请求号的函数,参见Linux T2 中断机制那一章。
        我们现在再来看刚才提到为节点建立属性链表的函数build_prop_list,当然这个函数也是直接从OBP的设备节点树中取出相应节点的属性加入到这个节点的节点链表中来:

191 static struct property * __init build_prop_list(phandle node)
192 {
193         struct property *head, *tail;
194 
195         head = tail = build_one_prop(node, NULL,
196                                      ".node", &node, sizeof(node));
197 
198         tail->next = build_one_prop(node, NULL, NULL, NULL, 0);
199         tail = tail->next;
200         while(tail) {
201                 tail->next = build_one_prop(node, tail->name,
202                                             NULL, NULL, 0);
203                 tail = tail->next;
204         }
205 
206         return head;
207 }

        第195行,调用build_one_prop函数建立节点的属性链表的第一个属性,实际上这个属性并不是从OBP的节点的属性中得到的,而是直接把该节点的节点号作为该节点的第一个属性。
        第198行,调用build_one_prop函数建立节点属性链表的第二个属性,这个属性是从OBP中的到的节点的第一个属性。
        第200到204行的循环,一直调用build_one_prop从OBP中得到该节点的属性,直到得到所有的属性,这时就把该节点的属性链表完全建立起来了。
        第206行,返回指向属性链表的指针。
        现在我们看build_one_prop函数,它会为节点建立一个属性,它有5个参数,其中第一个参数是节点的节点号,第是节点的前一个属性的名称,利用该参数可以在OBP节点的属性中得到其下一个属性,第3个参数是一个指定的名称,如果该参数不为空,则build_one_prop不会从OBP节点树中得到该节点的一个属性,而是直接以该参数为名称建立一个属性,它会同后面的两个参数一起共同建立一个属性,第4个参数是,属性的属性值,第5个参数,属性值的长度。我们来看这个函数:

136 static struct property * __init build_one_prop(phandle node, char *prev,
137                                                char *special_name,
138                                                void *special_val,
139                                                int special_len)
140 {
141         static struct property *tmp = NULL;
142         struct property *p;
143         const char *name;
144 
145         if (tmp) {
146                 p = tmp;
147                 memset(p, 0, sizeof(*p) + 32);
148                 tmp = NULL;
149         } else {
150                 p = prom_early_alloc(sizeof(struct property) + 32);
151                 p->unique_id = prom_unique_id++;
152         }
153 
154         p->name = (char *) (p + 1);
155         if (special_name) {
156                 strcpy(p->name, special_name);
157                 p->length = special_len;
158                 p->value = prom_early_alloc(special_len);
159                 memcpy(p->value, special_val, special_len);
160         } else {
161                 if (prev == NULL) {
162                         name = prom_firstprop(node, p->name);
163                 } else {
164                         name = prom_nextprop(node, prev, p->name);
165                 }
166 
167                 if (!name || strlen(name) == 0) {
168                         tmp = p;
169                         return NULL;
170                 }
171 #ifdef CONFIG_SPARC32
172                 strcpy(p->name, name);
173 #endif
174                 p->length = prom_getproplen(node, p->name);
175                 if (p->length <= 0) {
176                         p->length = 0;
177                 } else {
178                         int len;
179 
180                         p->value = prom_early_alloc(p->length + 1);
181                         len = prom_getproperty(node, p->name, p->value,
182                                                p->length);
183                         if (len <= 0)
184                                 p->length = 0;
185                         ((unsigned char *)p->value)[p->length] = '\0';
186                 }
187         }
188         return p;
189 }

        第145到148行,这是一个if语句,tmp是指向属性结构的指针,它是一个静态变量。从名字就可以看出它是一个临时变量,这个临时变量是做什么用呢?在建立一个属性的时候需要为其分配属性结构的空间,但是在获取一个属性的时候可能会出现获取失败的情况,比如已经获取到一个节点的最后一个属性,再获取其下一个属性就会获取失败了,出现这种情况时一般的做法是把之前分配的属性结构的空间释放掉,但是这里没有这样,这里是把之前分配的空间保存到一个静态临时变量中,以备下一次建立属性时使用,这个临时变量就是这里的tmp变量。如果tmp不为空,则说明在前一次建立属性时存在一个未被分配的空间
,直接把该空间清空,用它来建立新属性。
        第150,151行,如果tmp不为空,则要为要新建立的属性分配空间。可以看出这里不仅为属性结构分配了空间,而且还多分配了32个字节的空间,这32个字节是用来保存属性的名称的。第151行还初始化了属性结够的unique_id成员。
        第154行,使属性结构的name成员指向多分配的那32个字节的空间的首地址。
        第155到159行,如果special_name不为空,则不从OBP中得到节点属性,而是直接使用第3,4,5个参数建立属性,这几条语句把这几个参数分别赋值给属性结构的各成员。其中属性值的空间是新分配的。
否则,如果special_name为空,则需要从从OBP中获取节点属性了。
        第161,162行,如果prev参数为空,则说明这里是为一个节点获得在OBP节点树中的第一个属性,调用prom_firstprop函数与获得第一个属性的名称。
prom_firstprop实际上是调用OBP服务接口,这里就不分析了。
        第164行,如果prev参数不为空,则以前一个属性的名称为参数调用prom_nextprop函数得到节点的下一个属性的名称。prom_nextprop也是调用OBP服务接口,这里也不分析了。
        第167到170行,如果前面获取属性名称失败,则把分配的空间保存到tmp中,然后返回退出。
        第174行,以属性名称为参数调用prom_getproplen函数获取属性值的长度,prom_getproplen函数也是调OBP服务接口。
        第175,176行,如果属性长度小于或等于0,则把属性长度置为0,返回新建立的属性的指针。
否则,
        第180行,为属性值分配空间。
        第181行,调用prom_getproperty函数从OBP中获取该属性的属性值。
        第184到186行,进行一些错误处理,就不管了。
最后返回新建立的属性的指针。

        我们回到prom_build_devicetree函数中来,
        在第310行的语句中已经为设备节点树的根节点建立了节点实体,它的节点号为prom_root_node,父节点为NULL。
        第311行,把根节点的path_component_name成员置为空,上文中提过。
        第312行,把根节点的full_name置为"/"。
        第314行,把根节点的allnext保存到nextp中,节点的该成员会把所有的节点链接到一个链表中来。
        第315行,调用prom_build_tree函数建立设备节点树,它的第一个参数是指向一个要建立的节点的父节点的指针。第二个参数是父亲节点的第一个子节点的节点号,也就是要建立的节点的节点号,最后一个参数是nextp。prom_build_tree是一个递归函数,它是按照深度遍历的顺序递归的建立设备节点树的节点的。即当一个节点的所有的子节点都已经建立时才去接着建立下一个兄弟节点。
        第275行,while循环,直到所有的设备节点树的所有节点都已经建立,才退出循环。
        第276行,调用prom_create_node创建一个节点,这个函数在上文中分析过,这里建立的节点即有可能是一个子节点,也有可能是一个兄弟节点。
        第280,281行,prev_sibling变量指向该节点的前一个兄弟节点,一个父亲节点的第一个子节点是没有前一个兄弟节点的,这里如果有前一个兄弟节点,则使前一个兄弟节点的prev_sibling成员指向该节点,利用该成员可以把一个父节点的所有兄弟节点链接到一个链表中来。
        第283,284行,ret变量是prom_build_tree函数的返回值,它返回的实际上是第一个子节点的指针,这里使它指向第一个子节点。
        第285行,为prev_sibling变量赋值,以使建立下一个兄弟节点时可以把下一个兄弟节点链接到兄弟节点链表中去。
        第287,288行,利用节点的allnext成员把节点链接到所有节点的链表中来。
        第290行,为节点的path_component_name赋值,build_path_component函数就不分析了,有兴趣的读者可自行分析。
        第291行,为节点的full_name成员赋值。
        第293行,递归调用prom_build_tree建立字节点,这个递归调用会建立该节点所有的子节点。
        第298行,得到其兄弟节点的节点号,以在下一次循环中该兄弟节点及该兄弟节点的所有子节点。
        第310行,返回父节点的第一个子节点的指针。
        当prom_build_tree函数执行玩后,设备节点树就建立起来了。

        of虚拟总线的建立:

在Linux 2.6的内核中引入了设备驱动模型,在设备驱动模型中,所有的设备都被认为是挂载在某一个"总线"上,"总线"可以是实        际的物理总线,也可以是一个虚拟总线。对于pci设备等挂载在一条实际物理总线上的设备而言,在设备驱动模型中有一个与该实际总线相对应的"总线"实例,但是对于PCIe root complex等并没有挂载在一条实际物理总线上的设备,为了使用设备驱动模型,Linux在设备驱动模型为这种设备提过了一些虚拟的总线,如platform总线和of总线,T2 Linux使用了of总线。Of总线的大部分代码是Linux提供的体系结构无关的公共代码,这部分代码的实现分析我们这里不会涉及,我们这里要分析的T2 Linux如何使用of虚拟总线,在这个过程中,会调用大量的of总线的公共代码,在代码分析中遇到这些公共代码时都会一笔带过,不会深入分析。因此从本文中你无法获得of总线的实现及工作原理,有兴趣的读者可以查阅其他资料。
        T2 Linux使用of总线时并不仅仅是只把没有挂载在实际总线上的设备挂载在of总线上来,它会把设备节点树中的所有设备都挂载到of总线上来,这样做的好处是接下来初始化pci设备等其他设备时不许要从设备节点树中查找其资源信息,只需要使用Linux公共代码从of总线里查找就行了。
        T2 Linux把设备节点树里的设备挂载到of总线中的代码定义在arch/sparc/kernel/of_device_64.c文件中。
        我们从其初始化函数开始:

715 static int __init of_bus_driver_init(void)
716 {
717         int err;
718 
719         err = of_bus_type_init(&of_platform_bus_type, "of");
720         if (!err)
721                 scan_of_devices();
722 
723         return err;
724 }
725 

        726 postcore_initcall(of_bus_driver_init);
        第719行,of_bus_type_init函数是of公共代码提过的函数,它会初始化一个of总线实体,初始化的总线的名字叫做"of",of_platform_bus_type定义于arch/sparc/kernel/of_device_common.c文件中,定义如下:
        struct bus_type of_platform_bus_type;
        第722行,scan_of_devices函数会扫描设备节点树,把设备节点树中的所有设备都挂载到of总线上来。我们来看这个函数:

703 static void __init scan_of_devices(void)
704 {
705         struct device_node *root = of_find_node_by_path("/");
706         struct of_device *parent;
707 
708         parent = scan_one_device(root, NULL);
709         if (!parent)
710                 return;
711 
712         scan_tree(root->child, &parent->dev);
713 }

        第705行,of_find_node_by_path会根据设备节点树中节点的路径(也就是节点的full_name)查找某个节点,返回指向该节点的指针,这里是查找设备节点树的根节点。of_find_node_by_path是of代码提过的公共函数。
        第708行,scan_one_devic函数扫描一个设备,该函数的实际作用就是把一个设备挂载到of总线上来,这里是把设备节点树的根设备挂载到of总线上来,我们来看scan_one_devic函数:

631 static struct of_device * __init scan_one_device(struct device_node *dp,
632                                                  struct device *parent)
633 {
634         struct of_device *op = kzalloc(sizeof(*op), GFP_KERNEL);
635         const unsigned int *irq;
636         struct dev_archdata *sd;
637         int len, i;
638 
639         if (!op)
640                 return NULL;
641 
642         sd = &op->dev.archdata;
643         sd->prom_node = dp;
644         sd->op = op;
645 
646         op->node = dp;
647 
648         op->clock_freq = of_getintprop_default(dp, "clock-frequency",
649                                                (25*1000*1000));
650         op->portid = of_getintprop_default(dp, "upa-portid", -1);
651         if (op->portid == -1)
652                 op->portid = of_getintprop_default(dp, "portid", -1);
653 
654         irq = of_get_property(dp, "interrupts", &len);
655         if (irq) {
656                 op->num_irqs = len / 4;
657 
658                 /* Prevent overrunning the op->irqs[] array.  */
659                 if (op->num_irqs > PROMINTR_MAX) {
660                         printk(KERN_WARNING "%s: Too many irqs (%d), "
661                                "limiting to %d.\n",
662                                dp->full_name, op->num_irqs, PROMINTR_MAX);
663                         op->num_irqs = PROMINTR_MAX;
664                 }
665                 memcpy(op->irqs, irq, op->num_irqs * 4);
666         } else {
667                 op->num_irqs = 0;
668         }
669 
670         build_device_resources(op, parent);
671         for (i = 0; i < op->num_irqs; i++)
672                 op->irqs[i] = build_one_device_irq(op, parent, op->irqs[i]);
673 
674         op->dev.parent = parent;
675         op->dev.bus = &of_platform_bus_type;
676         if (!parent)
677                 dev_set_name(&op->dev, "root");
678         else
679                 dev_set_name(&op->dev, "%08x", dp->node);
680 
681         if (of_device_register(op)) {
682                 printk("%s: Could not register of device.\n",
683                        dp->full_name);
684                 kfree(op);
685                 op = NULL;
686         }
687 
688         return op;
689 }

        第634行,of总线上挂载的设备都是以struct of_device结构的形式挂载的,因此对于设备节点树中的所有设备,都会先将其转换为struct of_device结构,这里是为struct of_device结构分配空间。
        第642行,使sd指向struct of_device的dev成员的archdata成员,该成员是用来保存设备的平台相关信息的。当然这个结构的定义也是平台相关的,在sparc平台,它的定义如下:

struct dev_archdata {
         void                    *iommu;
         void                    *stc;
         void                    *host_controller;
 
         struct device_node      *prom_node;
         struct of_device        *op;
 
         int                     numa_node;
};

        其他的成员我们现在不要关心,只关系其prom_node成员和op成员,prom_node成员保存该设备的设备节点树中节点的指针,op成员保存设备的struct of_device结构的指针。
        第643行,archdata的prom_node成员指向设备的设备节点。
        第644行,archdata的op成员执行设备的struct of_device结构。
        第646行,使设备的struct of_device结构的node成员指向设备的设备节点。
        第648行,使struct of_device结构的clock_freq成员赋值为设备节点的
clock-frequency属性,该属性反映了设备的工作频率,对于根节点,它实际上是cpu的频率,当然有些设备是没有该属性的。of_getintprop_default函数会到设备节点的属性链表中查找,找到名称与传入参数匹配的属性,然后返回其属性值,这里就不分析了。
        第650到652行,得到节点的upa-portid或者portid属性,赋值给struct of_device结构的portid成员。
        第654行,得到节点的interrupts属性,实际上其返回值是一个指向存放其使用中断资源的内存快,其中每个中断资源占4个字节,这里的中断资源实际上就是devino(参见Linux T2 中断机制那一章)。
        第655行,如果能够获取到中断资源,则:
        第656行,计算出使用的中断资源的数量,保存到struct of_device结构的num_irqs。
        第659到664行,如果中断资源的数目超过了限定的最大值,则把num_irqs成员置为限定的最大值。
        第665行,把设备的中断资源保存到struct of_device结构的irqs成员中。
        第666,667行,如果没有获取的设备的中断资源,则把num_irqs成员置为0。
        第670行,对于根节点来说,build_device_resources函数什么都不做,但是对于其他节点来说,该函数会从设备节点中取出一些资源信息,保存到struct of_device结构中来,这个函数我们后面再分析。
        第671,672行,为设备使用的每一个中断资源,调用build_one_device_irq函数为其分配中断请求号。这个函数在Linux T2 中断机制那一章中提到过,该函数就不仔细分析了,有兴趣的读者可自行分析。
        第674行,为struct of_device结构的dev成员的parent成员赋值,对于根节点来说,该值赋值为空。
        第675行,为struct of_device结构的dev成员的parent成员赋值,赋值为我们使用的of总线的描述符。
        第676,677行,如果是根节点则为struct of_device结构的dev成员的name成员赋值为名称"root"。
        第678,679行,否则为struct of_device结构的dev成员的name成员赋值为设备节点树中节点的名称。
        第681行,of_device_register是公共代码,它会把struct of_device结构挂载到of总线上去。
        第688行,返回为该设备实现的struct of_device结构实体的指针。

        现在回到scan_of_devices函数中来。
        第712行,扫描完根节点后,scan_tree函数会扫描设备节点树中的所有节点,然后把节点所代表设备都挂载到of总线上来,这样,整个of总线结构就建立起来了。我们来看这个函数:

691 static void __init scan_tree(struct device_node *dp, struct device *parent)
692 {
693         while (dp) {
694                 struct of_device *op = scan_one_device(dp, parent);
695 
696                 if (op)
697                         scan_tree(dp->child, &op->dev);
698 
699                 dp = dp->sibling;
700         }
701 }

        可以看出,这个函数的核心是前面分析过的scan_one_device函数,该函数会按照深度遍历的顺序遍历节点树中的每个节点,然后为每个节点调用scan_one_device函数,scan_one_device函数会从节点树中取出资源信息,然后把设备挂载到of总线上去。
        在scan_one_device函数中,从设备节点中取出资源信息的函数为build_device_resources,在分析scan_one_device函数时这个函数没有分析,现在来分析这个函数:

313 static void __init build_device_resources(struct of_device *op,
314                                           struct device *parent)
315 {
316         struct of_device *p_op;
317         struct of_bus *bus;
318         int na, ns;
319         int index, num_reg;
320         const void *preg;
321 
322         if (!parent)
323                 return;
324 
325         p_op = to_of_device(parent);
326         bus = of_match_bus(p_op->node);
327         bus->count_cells(op->node, &na, &ns);
328 
329         preg = of_get_property(op->node, bus->addr_prop_name, &num_reg);
330         if (!preg || num_reg == 0)
331                 return;
332 
333         /* Convert to num-cells.  */
334         num_reg /= 4;
335 
336         /* Convert to num-entries.  */
337         num_reg /= na + ns;
338 
339         /* Prevent overrunning the op->resources[] array.  */
340         if (num_reg > PROMREG_MAX) {
341                 printk(KERN_WARNING "%s: Too many regs (%d), "
342                        "limiting to %d.\n",
343                        op->node->full_name, num_reg, PROMREG_MAX);
344                 num_reg = PROMREG_MAX;
345         }
346 
347         for (index = 0; index < num_reg; index++) {
348                 struct resource *r = &op->resource[index];
349                 u32 addr[OF_MAX_ADDR_CELLS];
350                 const u32 *reg = (preg + (index * ((na + ns) * 4)));
351                 struct device_node *dp = op->node;
352                 struct device_node *pp = p_op->node;
353                 struct of_bus *pbus, *dbus;
354                 u64 size, result = OF_BAD_ADDR;
355                 unsigned long flags;
356                 int dna, dns;
357                 int pna, pns;
358 
359                 size = of_read_addr(reg + na, ns);
360                 memcpy(addr, reg, na * 4);
361 
362                 flags = bus->get_flags(addr, 0);
363 
364                 if (use_1to1_mapping(pp)) {
365                         result = of_read_addr(addr, na);
366                         goto build_res;
367                 }
368 
369                 dna = na;
370                 dns = ns;
371                 dbus = bus;
372 
373                 while (1) {
374                         dp = pp;
375                         pp = dp->parent;
376                         if (!pp) {
377                                 result = of_read_addr(addr, dna);
378                                 break;
379                         }
380 
381                         pbus = of_match_bus(pp);
382                         pbus->count_cells(dp, &pna, &pns);
383 
384                         if (build_one_resource(dp, dbus, pbus, addr,
385                                                dna, dns, pna))
386                                 break;
387 
388                         flags = pbus->get_flags(addr, flags);
389 
390                         dna = pna;
391                         dns = pns;
392                         dbus = pbus;
393                 }
394 
395         build_res:
396                 memset(r, 0, sizeof(*r));
397 
398                 if (of_resource_verbose)
399                         printk("%s reg[%d] -> %llx\n",
400                                op->node->full_name, index,
401                                result);
402 
403                 if (result != OF_BAD_ADDR) {
404                         if (tlb_type == hypervisor)
405                                 result &= 0x0fffffffffffffffUL;
406 
407                         r->start = result;
408                         r->end = result + size - 1;
409                         r->flags = flags;
410                 }
411                 r->name = op->node->name;
412         }
413 }

        第325行,得到父亲节点的struct of_device 结构。
        第326行,of_match_bus函数会找到父设备的所属的总线类型,注意这里的总线类型并不是指设备驱动模型里面的"总线",而是指物理总线,比如一个pci设备对应的就是pci总线。T2 linux定义了一个所需匹配的总线的数组,这些总线都是T2上可能使用的总线:

 static struct of_bus of_busses[] = {
         /* PCI */
         {
                 .name = "pci",
                 .addr_prop_name = "assigned-addresses",
                 .match = of_bus_pci_match,
                 .count_cells = of_bus_pci_count_cells,
                 .map = of_bus_pci_map,
                 .get_flags = of_bus_pci_get_flags,
         },
         /* SIMBA */
         {
                 .name = "simba",
                 .addr_prop_name = "assigned-addresses",
                 .match = of_bus_simba_match,
                 .count_cells = of_bus_pci_count_cells,
                 .map = of_bus_simba_map,
                 .get_flags = of_bus_pci_get_flags,
         },
         /* SBUS */
         {
                 .name = "sbus",
                 .addr_prop_name = "reg",
                 .match = of_bus_sbus_match,
                 .count_cells = of_bus_sbus_count_cells,
                 .map = of_bus_default_map,
                 .get_flags = of_bus_default_get_flags,
         },
         /* FHC */
         {
                 .name = "fhc",
                 .addr_prop_name = "reg",
                 .match = of_bus_fhc_match,
                 .count_cells = of_bus_fhc_count_cells,
                 .map = of_bus_default_map,
                 .get_flags = of_bus_default_get_flags,
         },
         /* Default */
         {
                 .name = "default",
                 .addr_prop_name = "reg",
                 .match = NULL,
                 .count_cells = of_bus_default_count_cells,
                 .map = of_bus_default_map,
                 .get_flags = of_bus_default_get_flags,
         },
 };

        of_match_bus函数实际上就是遍历这个数组,调用每个成员的match函数,如果match函数匹配成功,则返回1,当然如果没有总线匹配成功,最终会返回最后一个成员,即default成员。我们只看其中的pci总线的匹配函数,因为我们系统中大部分设备都是pci设备。

44 static int of_bus_pci_match(struct device_node *np)
45 {
46         if (!strcmp(np->name, "pci")) {
47                 const char *model = of_get_property(np, "model", NULL);
48 
49                 if (model && !strcmp(model, "SUNW,simba"))
50                         return 0;
51 
52                 /* Do not do PCI specific frobbing if the
53                  * PCI bridge lacks a ranges property.  We
54                  * want to pass it through up to the next
55                  * parent as-is, not with the PCI translate
56                  * method which chops off the top address cell.
57                  */
58                 if (!of_find_property(np, "ranges", NULL))
59                         return 0;
60 
61                 return 1;
62         }
63 
64         return 0;
65 }

        第46行,对于pci设备,其父节点的名称应该是pci,因此这里比较,如果不相等,则直接返回。
        第47到50行,得到节点的model属性,应该是"SUNW,simba",否则也直接返回,因为T2中pci总线的根设备是cpu上集成的,其model属性应该是"SUNW,simba"。
        第58行,该节点应该有一个ranges属性,否则直接返回
        第61行,如果上面的条件都满足,则代表匹配成功,返回1。

        现在回到build_device_resources函数,
        第327行,如果找到匹配的总线,则为该节点调用该总线类型的count_cells函数,我们还是看pci总线的该函数:

 91 static void of_bus_pci_count_cells(struct device_node *np,
 92                                    int *addrc, int *sizec)
 93 {
 94         if (addrc)
 95                 *addrc = 3;
 96         if (sizec)
 97                 *sizec = 2;
 98 }

        可以看出该函数是直接把传入的两个参数分别置为3和2。这两个参数也就是build_device_resources函数的局部变量na和ns,至于这两个变量有什么作用,用到的时候再说。
        回到build_device_resources函数中来,
        第329行,调用of_get_property结构得到总线类型结构里面指定的属性,对于pci总线来说,该属性是assigned-addresses。当然,该属性可能不止一个属性值,这些属性值顺序存放在一个内存块中,该内存块的长度会保存到传入的第3个参数中。实际上该属性反映的是设备使用的I/O或内存资源的信息。对pci设备来说,每个I/O或内存资源占有20个字节的空间,其中第一个4字节反映是该资源是I/O资源,还是32位或者64位内存资源,第二个和第三个4字节反映的是I/O或内存空间的基地址,第4和第5个4字节空间的信息反映的是I/O或内存资源的长度。
        第334到337行,计算出I/O或内存资源的数目,它是把内存块的大小除以20。
        第340到345行,判断资源数目是否大于限定的最大值,如果大于限定的最大值,则置为限定的最大值。
        第347到413行,这些行就不仔细分析了,它实际上是把I/O或内存资源信息保存到struct of_device结构的resource数组成员中去,struct resource结构有3个成员,分别是I/O或内存基地址,I/O或内存大小,以及反映是I/O资源,32位内存资源还是64位内存资源的标志。注意,这些语句取出资源信息时会先判断该节点的父亲节点中有没有保存相关的资源信息,如果父亲节点有保存,则使用父亲节点保存的资源信息。这些行看着复杂,其实就是因为要从父节点中取资源信息。
到这里of虚拟总线的建立就分析完了。在建立of总线的过程中,把设备节点树中所有的节点所代表设备都挂载到了of总线上,同时在struct of_device结构中保存了设备所使用的中断资源,I/O或内存资源等信息。

        PCI总线及设备的初始化:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 使用spark-submit命令提交Spark应用程序到YARN集群模式,可以按照以下步骤进行操作: 1. 确保已经安装了Spark和YARN,并且配置了正确的环境变量。 2. 编写Spark应用程序代码,并将其打包成jar包。 3. 打开终端,输入以下命令: ``` spark-submit --class <main-class> --master yarn --deploy-mode cluster <jar-file> <args> ``` 其中,`<main-class>`是Spark应用程序的主类名,`<jar-file>`是打包好的jar包路径,`<args>`是传递给应用程序的参数。 4. 提交命令后,Spark会将应用程序提交到YARN集群中,并在集群中启动应用程序的Driver程序。 5. 可以通过YARN的Web界面或命令行工具来监控应用程序的运行状态和日志输出。 注意事项: - 在提交应用程序时,需要指定`--master yarn`和`--deploy-mode cluster`参数,以告诉Spark将应用程序提交到YARN集群中运行。 - 如果应用程序需要访问HDFS或其他外部资源,需要在应用程序中指定相应的路径或URL,并确保YARN集群中的节点也能够访问这些资源。 - 在提交应用程序时,可以通过`--num-executors`、`--executor-memory`、`--executor-cores`等参数来指定应用程序在集群中的资源分配情况。 ### 回答2: Spark是一个快速、通用、可扩展的大数据处理引擎,能够处理包括离线批处理、实时流处理、图形处理等多种数据处理场景。其中,Spark中常见的数据处理方式是通过RDD(弹性分布式数据集)来进行计算处理。 对于Spark应用的部署,有两种方式:一种是通过Spark Standalone模式,将Spark应用作为单一进程的方式运行;另一种则是通过YARN模式,将Spark应用分布到一组计算节点中去运行。在这里,我们介绍一种常见的部署方式:通过spark-submit命令将应用提交到YARN集群上运行。 spark-submit命令是Spark提供的专门用于提交应用的命令,根据不同的运行模式,有不同的参数指定方式。其中,将应用部署到YARN模式的一般命令如下: ``` ./bin/spark-submit \ --class [app-class] \ --master yarn \ --deploy-mode cluster \ [--executor-memory <memory>] \ [--num-executors <num>] \ [path-to-app-jar] [app-arguments] ``` 其中,各参数含义如下: 1. --class:指定应用的入口类。 2. --master:指定Spark应用运行在YARN模式下。 3. --deploy-mode:指定应用在YARN模式下的部署方式,有两种模式:client和cluster。其中,client模式是指在本地运行应用,而cluster模式则是将应用提交到YARN集群上运行。 4. --executor-memory:指定每个executor占用的内存大小。 5. --num-executors:指定在YARN集群上运行的executor数目。 6. [path-to-app-jar]:指定应用程序的jar包路径。 7. [app-arguments]:应用的命令行参数。 需要注意的是,将应用提交到YARN集群上运行时,需要提前从HDFS中将数据加载到内存中,否则可能会降低应用的性能。在代码中,可以通过使用SparkContext的textFile等方法,将HDFS中的数据读取到RDD中进行处理。 总之,通过spark-submit命令将Spark应用部署到YARN集群上运行,可以充分利用集群资源,实现高效处理大规模数据。而在代码编写方面,需要注意处理好HDFS中数据的读取和分布式操作等问题。 ### 回答3: Spark是一种开源的大数据处理框架,其可以通过Spark-submit进行提交。Spark-submit是一个命令行工具,可用于将Spark应用程序提交到集群中运行。它支持多种模式,包括local模式、standalone模式和yarn-cluster模式等。其中,在yarn-cluster模式中,Spark应用程序将在YARN集群上运行。 在使用Spark-submit提交Spark应用程序到YARN集群的时候,需要考虑以下几个方面: 1. 配置参数 首先,需要指定Spark-submit的参数,例如--class参数用于指定要运行的主类。在YARN集群上运行Spark应用程序需要使用--master参数,并将其设置为yarn-cluster。同时,可以添加其它的参数,例如--num-executors用于设置执行程序的数量,--executor-memory用于设置每个执行程序的内存。 示例: spark-submit --class com.test.TestApp \ --master yarn-cluster \ --num-executors 5 \ --executor-memory 4G \ /path/to/your/application.jar 2. 配置环境 为了让Spark应用程序在YARN集群上运行,需要为其配置适当的环境。需要确保所有必要的依赖项都已安装,并将其添加到Spark-submit命令中。在集群节点上,需要确保Spark和Hadoop已正确配置并运行。 3. 访问资源 将应用程序提交到YARN集群后,需要确保它能够访问必要的资源。这些资源包括存储在HDFS中的数据和应用程序所需的库和文件。如果应用程序需要访问外部资源,则还需要配置适当的访问权限。 总的来说,Spark-submit提交yarn-cluster模式是一种将Spark应用程序提交到YARN集群上运行的方法。在提交之前,需要考虑配置参数、配置环境和访问资源,以确保应用程序能够正确运行并访问所需的资源和库。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值