farkmt2official 1
farkmt2official
kralhakan2009 1
kralhakan2009
Vahsi Uzman 1
Vahsi Uzman
BlackFullMoon 1
BlackFullMoon
Hikaye Ekle

SMP Önyükleme

  • Konuyu başlatan Konuyu başlatan badie
  • Başlangıç tarihi Başlangıç tarihi
  • Cevaplar Cevaplar 0
  • Görüntüleme Görüntüleme 296

Ayyıldız2 | 2008 TR Yapısı • 1-99 Orta Emek Destan • Oto Avsız • 10 Temmuz 21:00 HEMEN TIKLA!

SMP ile ilgili CONFIG_SMP, CONFIG_X86_LOCAL_APIC, CONFIG_X86_IO_APIC, CONFIG_MULTIQUAD ve CONFIG_VISWS gibi bir kaç makro bulunmaktadır. Birçok insanın dikkate almadığı (eğer IBM üst-uç çok işlemcili sunucu veya SGI Görsel İşistasyonu kullanmıyorsa) CONFIG_MULTIQUAD veya CONFIG_VISWS gerektiren kodları yoksayacağım.
BSP APleri tetiklemek için start_kernel() -> smp_init() -> smp_boot_cpus() -> do_boot_cpu() -> wakeup_secondary_via_INIT()'i çalıştırır. Teknik ayrıntılar için bakınız: ve IA-32 Manual Vol.3 (Ch.7. Multile-Processor Management, and Ch.8. Advanced Programmable Interrupt Controller).
smp_init() Öncesi
smp_init()'i çağırmadan önce, start_kernel() SMP ortamını kurmak için birşeyler yapar.

start_kernel()|-- setup_arch()| |-- parse_cmdline_early(); // SMP "noht" ve "acpismp=force" arar| | `-- /* "noht" çoklu işlem (HyperThreading) özelliğini iptal eder| | * (Xeon başına 2 mantıksal işlemci) */| | if (!memcmp(from, "noht", 4)) {| | disable_x86_ht = 1;| | set_bit(X86_FEATURE_HT, disabled_x86_caps);| | }| | /* "acpismp=force" ACPI SMP tablosunu kullanıma ve| | * çözümlemeye zorlar */| | else if (!memcmp(from, "acpismp=force", 13))| | enable_acpi_smp_table = 1;| |-- setup_memory(); // MP yapılandırma tablosu için bellek ***rır| | |-- reserve_bootmem(PAGE_SIZE, PAGE_SIZE);| | `-- find_smp_config();| | `-- find_intel_smp();| | `-- smp_scan_config();| | |-- set flag smp_found_config| | |-- set MP floating pointer mpf_found| | `-- reserve_bootmem(mpf_found, PAGE_SIZE);| |-- if (disable_x86_ht) { // çoklu işlem iptal edilmişse| | clear_bit(X86_FEATURE_HT, &boot_cpu_data.x86_capability[0]);| | set_bit(X86_FEATURE_HT, disabled_x86_caps);| | enable_acpi_smp_table = 0;| | }| |-- if (test_bit(X86_FEATURE_HT, &boot_cpu_data.x86_capability[0]))| | enable_acpi_smp_table = 1;| |-- smp_alloc_memory();| | `-- /* AP işlemcilerinin gerçek kip kod uz***nı düşük bellekte ***rır */| | trampoline_base = (void *) alloc_bootmem_low_pages(PAGE_SIZE);| `-- get_smp_config(); /* önyükleme anı MP yapılandırmasını al */| |-- config_acpi_tables();| | |-- memset(&acpi_boot_ops, 0, sizeof(acpi_boot_ops));| | |-- acpi_boot_ops[ACPI_APIC] = acpi_parse_madt;| | `-- /* have_acpi_tables değerini ACPI tablosunda| | * MADT kullanıldığını belirtecek şekilde değiştirir;| | * başarısız olursa MPS kullan. */| | if (enable_acpi_smp_table && !acpi_tables_init())| | have_acpi_tables = 1;| |-- set pic_mode| | /* =1, IMCR varsa ve PIC Kipi gerçekleştirilmişse;| | * =0, aksi taktirde Sanal Tel Tipi (Virtual Wire Mode)| | * gerçekleştirilmiştir. */| |-- mp_lapic_addr'deki yerel APIC adresini sakla| `-- MP_PROCESSOR, MP_BUS, MP_IOAPIC, MP_INTSRC ve MP_LINTSRC.MP gibi| yapılandırma tablo girdilerini tara.||-- trap_init();| `-- init_apic_mappings(); // PTE'yi APIC için ayarla| |-- /* Eğer hiç yerel APIC bulunmazsa tüm sıfır sayfalarını| | * (zeroes page) yerel APIC'i ve bir de IO-APIC'i| | * taklit edecek şekilde ayarla. */| | if (!smp_found_config && detect_init_APIC()) {| | apic_phys = (unsigned long) alloc_bootmem_pages(PAGE_SIZE);| | apic_phys = __pa(apic_phys);| | } else| | apic_phys = mp_lapic_addr;| |-- /* yerel APIC adresini,| | * çoğu durumda mp_lapic_addr'dir (0xfee00000),| | * bunu doğrusal FIXADDR_TOP (0xffffe000) adresine eşle */| | set_fixmap_nocache(FIX_APIC_BASE, apic_phys);| |-- /* öntanımlı yapılandırmamız (veya MP tablosunun bozuk olması)| | * durumu için BSP'nin APIC ID değerini al. */| | if (boot_cpu_physical_apicid == -1U)| | boot_cpu_physical_apicid = GET_APIC_ID(apic_read(APIC_ID));| `-- // IOAPIC adresini arabelleksiz doğrusal adrese eşle| set_fixmap_nocache(idx, ioapic_phys);| // Artık APIC uz***na erişmek için doğrusal adres kullanabiliriz.|-- init_IRQ();| |-- init_ISA_irqs();| | |-- /* sanal tel kipinin ilk kurulumu. */| | | init_bsp_APIC();| | `-- init_8259A(auto_eoi=0);| `-- setup SMP/APIC interrupt handlers, esp. IPI.`-- mem_init(); `-- /* SMP için düşük adresleme geçişini (zapping) * geciktir: zap_low_mappings() */​

IPI (İşlemcilerarası Kesme - InterProcessor Interrupt), yani işlemciden işlemciye yerel APIC üzerinden kesme, BSP'nin APleri tetiklemek için kullandığı mekanizmadır.
Bilmeniz gerekir ki MP-uyumlu sistemlerde "CPU başına bir yerel APIC gereklidir". İşlemciler APIC yerel birimlerinin adres uz***nı (0xFEE00000 - 0xFEEFFFFF fiziksel adresini) paylaşmazlar, fakat APIC giriş-çıkış birimlerini (0xFEC00000 - 0xFECFFFFF) paylaşırlar. Her iki adres uz*** da arabelleklenebilir değildir.
smp_init()
BSP her bir işlemcinin veri yapılarını kurmak ve kalan APleri etkinleştirmek için start_kernel() -> smp_init() -> smp_boot_cpus() çağrısı yapar.

///////////////////////////////////////////////////////////////////////////////static void __init smp_init(void){ /* Get other processors into their bootup holding patterns. */ smp_boot_cpus(); wait_init_idle = cpu_online_map; clear_bit(current->processor, &wait_init_idle); /* Don't wait on me! */ smp_threads_ready=1; smp_commence() { /* Lets the callins below out of their loop. */ Dprintk("Setting commenced=1, go go go\n"); wmb(); atomic_set(&smp_commenced,1); } /* Wait for the other cpus to set up their idle processes */ printk("Waiting on wait_init_idle (map = 0x%lx)\n", wait_init_idle); while (wait_init_idle) { cpu_relax(); // i.e. "rep;nop" barrier(); } printk("All processors have done init_idle\n");}///////////////////////////////////////////////////////////////////////////////void __init smp_boot_cpus(void){ // ... çok ilginç olmayan birşey :-) /* Initialize the logical to physical CPU number mapping * and the per-CPU profiling router/multiplier */ prof_counter[0..NR_CPUS-1] = 0; prof_old_multiplier[0..NR_CPUS-1] = 0; prof_multiplier[0..NR_CPUS-1] = 0; init_cpu_to_apicid() { physical_apicid_2_cpu[0..MAX_APICID-1] = -1; logical_apicid_2_cpu[0..MAX_APICID-1] = -1; cpu_2_physical_apicid[0..NR_CPUS-1] = 0; cpu_2_logical_apicid[0..NR_CPUS-1] = 0; } /* Setup boot CPU information */ smp_store_cpu_info(0); /* Final full version of the data */ printk("CPU%d: ", 0); print_cpu_info(&cpu_data[0]); /* We have the boot CPU online for sure. */ set_bit(0, &cpu_online_map); boot_cpu_logical_apicid = logical_smp_processor_id() { GET_APIC_LOGICAL_ID(*(unsigned long *)(APIC_BASE+APIC_LDR)); } map_cpu_to_boot_apicid(0, boot_cpu_apicid) { physical_apicid_2_cpu[boot_cpu_apicid] = 0; cpu_2_physical_apicid[0] = boot_cpu_apicid; } global_irq_holder = 0; current->processor = 0; init_idle(); // will clear corresponding bit in wait_init_idle smp_tune_scheduling(); // ... some conditions checked connect_bsp_APIC(); // enable APIC mode if used to be PIC mode setup_local_APIC(); if (GET_APIC_ID(apic_read(APIC_ID)) != boot_cpu_physical_apicid) BUG(); /* Scan the CPU present map and fire up the other CPUs * via do_boot_cpu() */ Dprintk("CPU present map: %lx\n", phys_cpu_present_map); for (bit = 0; bit < NR_CPUS; bit++) { apicid = cpu_present_to_apicid(bit); /* Don't even attempt to start the boot CPU! */ if (apicid == boot_cpu_apicid) continue; if (!(phys_cpu_present_map & (1 << bit))) continue; if ((max_cpus >= 0) && (max_cpus <= cpucount+1)) continue; do_boot_cpu(apicid); /* Make sure we unmap all failed CPUs */ if ((boot_apicid_to_cpu(apicid) == -1) && (phys_cpu_present_map & (1 << bit))) printk("CPU #%d not responding - cannot use it.\n", apicid); } // ... SMP BogoMIPS // ... B stepping processor warning // ... HyperThreading handling /* Set up all local APIC timers in the system */ setup_APIC_clocks(); /* Synchronize the TSC with the AP */ if (cpu_has_tsc && cpucount) synchronize_tsc_bp();smp_done: zap_low_mappings();}///////////////////////////////////////////////////////////////////////////////static void __init do_boot_cpu (int apicid){ cpu = ++cpucount; // 1. prepare "idle process" task struct for next AP /* We can't use kernel_thread since we must avoid to * reschedule the child. */ if (fork_by_hand() < 0) panic("failed fork for CPU %d", cpu); /* We remove it from the pidhash and the runqueue * once we got the process: */ idle = init_task.prev_task; if (!idle) panic("No idle process for CPU %d", cpu); /* we schedule the first task manually */ idle->processor = cpu; idle->cpus_runnable = 1 << cpu; // only on this AP! map_cpu_to_boot_apicid(cpu, apicid) { physical_apicid_2_cpu[apicid] = cpu; cpu_2_physical_apicid[cpu] = apicid; } idle->thread.eip = (unsigned long) start_secondary; del_from_runqueue(idle); unhash_process(idle); init_tasks[cpu] = idle; // 2. prepare stack and code (CS:IP) for next AP /* start_eip had better be page-aligned! */ start_eip = setup_trampoline() { memcpy(trampoline_base, trampoline_data, trampoline_end - trampoline_data); /* trampoline_base was reserved in * start_kernel() -> setup_arch() -> smp_alloc_memory(), * and will be shared by all APs (one by one) */ return virt_to_phys(trampoline_base); } /* So we see what's up */ printk("Booting processor %d/%d eip %lx\n", cpu, apicid, start_eip); stack_start.esp = (void *) (1024 + PAGE_SIZE + (char *)idle); /* this value is used by next AP when it executes * "lss stack_start,%esp" in * linux/arch/i386/kernel/head.S:startup_32(). */ /* This grunge runs the startup process for * the targeted processor. */ atomic_set(&init_deasserted, 0); Dprintk("Setting warm reset code and vector.\n"); CMOS_WRITE(0xa, 0xf); local_flush_tlb(); Dprintk("1.\n"); *((volatile unsigned short *) TRAMPOLINE_HIGH) = start_eip >> 4; Dprintk("2.\n"); *((volatile unsigned short *) TRAMPOLINE_LOW) = start_eip & 0xf; Dprintk("3.\n"); // we have setup 0:467 to start_eip (trampoline_base) // 3. kick AP to run (AP gets CS:IP from 0:467) // Starting actual IPI sequence... boot_error = wakeup_secondary_via_INIT(apicid, start_eip); if (!boot_error) { // looks OK /* allow APs to start initializing. */ set_bit(cpu, &cpu_callout_map); /* ... Wait 5s total for a response */ // bit cpu in cpu_callin_map is set by AP in smp_callin() if (test_bit(cpu, &cpu_callin_map)) { print_cpu_info(&cpu_data[cpu]); } else { boot_error= 1; // marker 0xA5 set by AP in trampoline_data() if (*((volatile unsigned char *)phys_to_virt(8192)) == 0xA5) /* trampoline started but... */ printk("Stuck ??\n"); else /* trampoline code not run */ printk("Not responding.\n"); } } if (boot_error) { /* Try to put things back the way they were before ... */ unmap_cpu_to_boot_apicid(cpu, apicid); clear_bit(cpu, &cpu_callout_map); /* set in do_boot_cpu() */ clear_bit(cpu, &cpu_initialized); /* set in cpu_init() */ clear_bit(cpu, &cpu_online_map); /* set in smp_callin() */ cpucount--; } /* mark "stuck" area as not stuck */ *((volatile unsigned long *)phys_to_virt(8192)) = 0;}​

start_secondary() ile trampoline_data()'yı karıştırm***n. İlki AP "idle" süreci görev yapısı EIP değeridir, ikincisi ise AP'nin BSP onu uyardıktan sonra (wakeup_secondary_via_INIT() kullanarak) çalıştırdığı gerçek kipli koddur.
linux/arch/i386/kernel/trampoline.S
Bu dosya 16 bitlik gerçek kipli AP başlatma kodunu içerir. BSP start_kernel() -> setup_arch() -> smp_alloc_memory() içinde trampoline_base bellek uz***nı ***rdı. BSP APyi tetiklemeden önce trampoline kodunu trampoline_data ve trampoline_end arasından trampoline_base'e (do_boot_cpu() -> setup_trampoline() içindeki) kopyalar. BSP 0:467 noktasını, AP bu noktadan itibaren çalışabilmesi için, trampoline_base olarak atar.

///////////////////////////////////////////////////////////////////////////////trampoline_data(){r_base: wbinvd; // diğeri için zararsız olması gereken NUMA-Q için gerekli DS = CS; BX = 1; // Bir SMP trampoline seçeneği cli; // master'in çalıştığımızı bilmesi için imleyiciyi yaz trampoline_base = 0xA5A5A5A5; lidt idt_48; lgdt gdt_48; AX = 1; lmsw AX; // koru***ı kip! goto flush_instr;flush_instr: goto CS:100000; // see linux/arch/i386/kernel/head.S:startup_32()}idt_48: .word 0 # idt limit = 0 .word 0, 0 # idt base = 0Lgdt_48: .word 0x0800 # gdt limit = 2048, 256 GDT girdisi .long gdt_table-__PAGE_OFFSET # gdt base = gdt (ilk SMP CPU).globl SYMBOL_NAME(trampoline_end)SYMBOL_NAME_LABEL(tramp oline_end)​

Dikkat ederseniz AP linux/arch/i386/kernel/head.S:startup_32()'ye geçtiğinde, BSPninkinden (BX=0) farklı olarak, BX=1 oluyor. Bakınız: .
initialize_secondary()
BSP'den farklı olarak, bölümündeki
linux/arch/i386/kernel/head.S:startup_32()'nin sonunda AP start_kernel() yerine initialize_secondary()'yi çağırır.

/* İkincil işlemciler için herşey kurulmuş durumda; * bunlar sadece görev yapısındaki herşeyi yeniden * yüklemek için gerekli. * Bu işlev değer döndürmemeli. */void __init initialize_secondary(void){ /* Aslında tüm TSS'yi yüklememiz gerekmiyor, * basitçe sadece yığıt göstericisi ve eip. */ asm volatile( "movl %0,%%esp\n\t" "jmp *%1" : :"r" (current->thread.esp),"r" (current->thread.eip));}​

BSPnin thread.eip'yi start_secondary()'ye atamak için do_boot_cpu()'yu çağırmasıyla denetim bu işleve geçer. AP, BSP tarafından do_boot_cpu() -> fork_by_hand() -> do_fork() içinde kurulan, yeni bir yığıt çerçevesi kullanır.
start_secondary()
Tüm APler içindeki smp_init() -> smp_commence() ile tetiklenen BSP'den smp_commenced sinyalini beklerler. Bu sinyali aldıktan sonra "idle" süreçlerini çalıştırırlar.

///////////////////////////////////////////////////////////////////////////////int __init start_secondary(void *unused){ /* smp_callin() öncesine birşey koyma, en yapılması gereken şeyleri * yapmak için yapılacak şeyleri sınırlamak istediğimiz bu yerde * SMP önyüklemesi çok kırılgandır.*/ cpu_init(); smp_callin(); while (!atomic_read(&smp_commenced)) rep_nop(); /* düşük bellek adreslemesi temizlendi, * onları yerel TLB'lerden de çıkar (flush). */ local_flush_tlb(); return cpu_idle(); // asla dönmez, bakınız: }​

cpu_idle() -> init_idle() wait_init_idle içindeki ilgili biti temizler ve son olarak BSPnin smp_init()'i bitirmesini sağlar ve start_kernel() içindeki takip eden işlevle devam eder (örn. rest_init()).
 

Şu an konuyu görüntüleyenler (Toplam : 0, Üye: 0, Misafir: 0)

Geri
Üst