В статье «СКОРОСТЬ ЗАГРУЗКИ СЕРВЕРОВ ЭЛЬБРУС» мы сравнили время работы программы начального старта (ПНС) Эльбрус с длительностью загрузки ОС на различном оборудовании Intel.
Исследование показало, что даже при подкачке ОС с NVME, более высоких частотах ЦП, меньшем числе южных мостов и равных объемах ОЗУ, скорость загрузки Intel не превосходит по времени Эльбрус. В этой статье рассмотрим, как ПНС Эльбрус ускоряет загрузку. Ниже список основных инструментов, обеспечивающих ускорение:

  • Широковещательная настройка физуровней и контроллеров памяти DDR4
  • Параллельная инициализация ядер ЦП в режиме многопоточности — MultiThreading
  • Отключение вывода в консоль — режим SILENCE
  • Обмен сообщениями через регистры
  • Быстрый кооперативный планировщик без приоритетов
  • Простая спин-блокировка для синхронизации доступа к ресурсам
  • Расширенная настройка параметров работы
  • Отдельно можно выделить загрузку ОС с SSD. Но это уже больше аппаратный, чем программный подход. По перечню видно, что ПНС Эльбрус, по сути компактная ОС, где применяются общеизвестные механизмы построения операционных сред. Рассмотрим детально работу ключевых механизмов в контексте ускорения.

    РЕЖИМ BROADCAST ДЛЯ MC И PHY

    В архитектуре E2K.V6 реализована поддержка быстрой настройки ОЗУ путем рассылки широковещательных сообщений контроллерам памяти (MC) и физическим уровням (PHY) DDR4. Более полные сведения о специфике V6 можно почерпнуть из документации. Поддержка BroadCast реализована в файле ddr4bc.c. Ниже doxygen-заголовок этого файла.

     /**
     * @file        ddr4bc.c 
     * @note        DDR4 BroadCast (BC) - широковещательный режим конфигурирования и настройки DDR4: PHY и MC.
     * @brief       Реализация инициализации PHYs и MCs DDR4 в режиме BroadCast.
     * @internal    Implementation. ЦЕЛЬ - нужно добавить возможность инициализировать PHYs и MCs в режиме BC (MC_CH=0xf). 
     *              Разрешение включения данного режима - флаг yBroadCast в NVRAM, управляемый через верхнюю консоль BOOT. Запрещено включать режим, если:
     *                1) хотя бы один канал памяти не используется
     *                2) не заполнен 0-ой слот в каком-то канале
     *                3) каналы заполнены несимметрично по слотам или размерам
     *                4) в каналах установлены разные  модели планок памяти
     *                5) включен debug режим  при инициализации PHY
     *  @note       Надо учесть, что в BroadCast работают только конф.команды записи.
     *              ПОЭТОМУ при конфигурационном чтении нужно всегда заранее задавать № канала в регистре MC_CH. ОТСЮДА: 
     *               1) Опрос PHYs по статусу завершения тренировки остается локальным (последовательным, канал за каналом).
     *               2) Иниц. MC_TIM1/TIM2 надо оставить локальной, т.к. часть полей настраивается по результатам тренировки на основе инфы из PHY соответствующего MC. 
     *               3) Запись MCx_CTL.mcstart = 1 надо оставить локальной, иначе рефреши во всех MC будут выполняться синхронно с неизвестным эффектом для системы.
     * @bug         http://bugzilla.lab.sun.mcst.ru/bugzilla-mcst/show_bug.cgi?id=133704
     * @see         e16_mc_config.pdf    	   @author Igor Petrov 
     * @link        http://www.lab.sun.mcst.ru/~E16C/4 	    Конфигурация DDR4PHY
     * @link        http://www.lab.sun.mcst.ru/honey/elbrus_16c/doc/hw/mc/e16_MC.pdf
     * @author     Copyright (C) 2019, msct.ru
     * @addtogroup	DDR4BC
     * @{
     */
    

    Режим BroadCast опирается на технологию Cache-As-Ram. CAR позволяет использовать кэш процессора в качестве ОЗУ до инициализации самого ОЗУ. Оболочка BroadCast имплементирована в функции DDR4BC_init_channels. Её код приведен в ПРИЛОЖЕНИИ-1.

    АЛГОРИТМ ШИРОКОВЕЩАТЕЛЬНОЙ НАСТРОЙКИ RAM

    Обобщенно в функции DDR4BC_init_channels выполняем следующее. Широковещательно запрещаем работу всех каналов. В режиме отладки покажем частоту памяти. Запомним № MC плюс рабочая установка канала. Запускаем физические уровни DDR4 ЦП широковещательно. Если нет сбоя PHY, то по каждому MC локально ждем статуса завершения настройки PHY. Далее на лету регулируем уровень VREFDQ. Широковещательно настраиваем ряд регистров контроллеров DDR4: TIM0/2, CFG, PERF0, CTL и пр. После настройки PHYs широковещательно инициализируем контроллеры памяти — mc_channel_regs_init_all(node,MC_ALL). Далее в цикле локально по каждому MC ждем завершения инициализации. Если включен DEBUG, показываем состояния всех регистров PHY_0 и MC_0. Если нужно, отрабатываем широковещательно для MC режим selfrefresh. Как итог, выдаем полученную максимальную маску MC в режиме BC.

    МНОГОПОТОЧНАЯ ИНИЦИАЛИЗАЦИЯ

    Многопоточность не является чем-то сверхъестественным, а ее использование для повышения скорости работы стало нормой. В частности, ПНС E2K.V6 применяет многопоточность для сокращения времени запуска ядер ЦП. Базовый алгоритм — последовательный. В нем ядра ЦП настраиваются в цикле одно за другим. В многопоточном режиме кооперативный планировщик на BSP-ядре раздает AP-ядрам задания на запуск путем рассылки программных сообщений. В свою очередь, AP-ядра после самонастройки отправляют ему сообщения об окончании инициализации. Процесс заканчивается, когда планировщик дождался ответа от всех рецепиентов. Для ускорения загрузки сообщения доставляются через NBSR регистры. При этом каждому ядру соответствует свой регистр. Интерфейс многопоточности представлен в файле multi_thread.h и содержит ряд опорных функций:

    /// =============================================================================
    /// @interface
    /// =============================================================================
    MTHR_init();                    /// @~ Инициализация 
                                             /// @~ Сеттеры и Геттеры
    MTHR_setMSG( val,...);
    MTHR_getMSG(     ...);
    MTHR_putMSG( val,...);
    MTHR_letMSG(     ...);
    MTHR_set_cmos_default();           /// @~ Задать флаг в энергонезависимой (NVRAM,FRAM,EEPROM,FLASH) по умолчанию
    MTHR_invert_flag_via_console(); /// @~ Инвертировать флаг режима в энергонезависимой через консоль
    MTHR_exist ( void );		          /// @~ Проверка наличия режима
                                    /// @~ Обмен сообщениями
    MTHR_saveMSGs ( void );
    MTHR_undoMSGs ( void );
    MTHR_initMSG ( u8 cpu, u8 core, u32 val );
    MTHR_initMSGs( void );
    MTHR_showMSG ( u8 cpu, u8 core );
    MTHR_showMSGs( void );
    
    

    КАК ЭТО РАБОТАЕТ

    Обобщенный алгоритм многопоточной инициализации такой. После подачи питания настраивается BSP-ядро, которое детектирует материнку и запускает BISTы. Затем инициализирует южный мост, I2C-SPI, TTY, конфигурирует линки, нумерует ЦП, разрешает когерентность, подкачивает ROM_DATA и BSS, формирует стеки, определяет тип и размер RAM и потом широковещательно настраивает BSP ОЗУ. Далее BSP-ядро выполняет маршрутизацию, создает карту регионов памяти и выделяет место для размещения кучи. После настройки BSP наступает очередь SMP_deploy(). Вызывается планировщик, который ищет живые AP-ядра и многопоточно поднимает их. AP-ядра настраивают себя, выполняя действия схожие с теми, что делались на BSP. После развертывания SMP строятся PCI-деревья, устанавливаются драйвера и если надо загружается ОС. Доступ к общим ресурсам (RAM, TTY, FLASH и пр.) синхронизируется через SpinLock — низкоуровневый примитив синхронизации, применяемый в многопроцессорных системах для взаимного исключения исполнения на базе цикла активного ожидания. Спин-блокировка применяется, когда ожидание захвата предполагается недолгим или контекст выполнения не предполагает перехода в состояние пассивного ожидания.

    РЕЖИМ SILENCE

    Включение режима тишины в параметрах настройки ПНС Эльбрус приводит к тому, что функции TTY перестают генерировать выходные данные. Какой практический результат наличия данной опции? За счет запрета вывода в консоль удалось реально повысить скорость загрузки. Кроме того в режиме SILENCE все технологические паузы автоматом приводятся к минимуму вплоть до 0.

    НАСТРОЙКА ПАРАМЕТРОВ РАБОТЫ

    Режим тишины не единственный конфигурационный параметр. Конфигурирование в целом — это важнейший управляющий функционал среды ПНС Эльбрус. Благодаря ему становится возможной тонкая и точная настройка оборудования и ПО. Конфигурирование обеспечивает параметрический доступ к отдельным составляющим системы, что облегчает разработку и упрощает наладку. Как было показано в статье, время перезапуска напрямую зависит от аппаратной конфигурации. «Объем RAM играет тут первую скрипку. Увеличение числа ЦП, ядер ЦП и южных мостов замедляет загрузку. Частота ЦП и особенно скорость дисков существенно влияют на время загрузки». Для ускорения работы системы и/или оптимизации её работы на помощь оператору E2K-машин приходит конфигурирование. Что в этой связи предлагает МЦСТ в качестве возможностей по настройке E2K-машин? Конфигуратор ПНС Эльбрус версии 6 позволяет:

  • запретить/разрешить многопоточность
  • перейти в режим минимальной конфигурации
  • перейти в режим Verbose
  • сбросить/установить все настройки по умолчанию
  • назначить предельную частоту работы группе ЦП или нужному ЦП
  • ввести или вывести из конфигурации любые ЦП и ядра ЦП
  • задать допустимые частоты работы контроллерам памяти
  • установить тонкие настройки для физических уровней DDR4
  • ПНС поддерживает и автоматически определяет три типа модулей DDR4: RDIMM, LRDIMM, UDIMM.
          ПНС позволяет включить/выключить из конфигурации указанные модули памяти в заданных слотах
  • ПНС дает возможность ввести/вывести из конфигурации указанные южные мосты
  • ПНС предоставляет возможность запрета/разрешения поддержки работы аппаратных интерфейсов и драйверов
  • ПНС предоставляет возможность устанавливать допустимые технологические паузы
  • ПНС позволяет настроить режимы работы контроллеров IPCC3
  • ПНС предоставляет четыре штатных конфигурации PCIMEN и DMA и одну произвольную пользовательскую
  • ПНС на лету определяет тип материнской платы, на которой работает.
          Или позволяет оператору задать тип материнской платы для работы
  • ПНС автоматически переходит в режим АПМДЗ при детектировании его аппаратного наличия.
          ПНС позволяет отказаться от работы в режиме АПМДЗ
  • ПНС автоматически снимает питание со сбойного оборудования
  • ПНС автоматически переходит в режим мультилинка при детектировании его аппаратного наличия.
          ПНС позволяет оператору отключить режим мультилинка
  • ПНС позволяет оператору выбирать drive, partition, filename для загрузки ОС
  • ПНС позволяет оператору формировать command_string перед запуском Linux
  • ПНС позволяет управлять работой FANs и многое другое

    В ПЕРСПЕКТИВЕ

    Время работы ПНС Эльбрус на E2C3 около 7 секунд. В этом легко убедиться, включив любую такую машину. В дальнейшем разработчики МЦСТ предполагают реализовать параллельную загрузку драйверов ПНС и сократить аппаратные задержки в RC-цепочках при включении питания. Ожидаемый выигрыш тут 2-3 секунды. Поэтому в будущем ПНС Эльбрус станет работать еще быстрее …

    ПРИЛОЖЕНИЕ-1

    /** 
     *  @brief 	Функция DDR4BC_init_channels()
     *  @internal   Инициализация памяти в режиме  BroadCast (широковещательно)
     *  @note       В режиме BC общие SPD настройки берутся из канала 0. 
     *  @param[in]	u8 node
     *  @return     void 
     */
    u8	no_stk	DDR4BC_init_channels( u8 node )
    {										                  _PROC(""); /// @~ Покажем вход в функцию в режиме отладки   
    	int   micro_secs = -2 ;							      /// @~ Пауза две микросекунды
    	u8    trys       =  0 ;							      /// @~ Число попыток перетренировки DDR4 PHY
    	/// „„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„
    	/// @internal
    	/// ”””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””
    	MC_CTL_disable_channel( MC_ALL );					  /// @~ Disable all channels
    	{
            if ( is_watch() ) mc_show_freq( node, MC_0 );     /// @~ Покажем тек.freq работы MC  
    
    		DDR4BC_assign_mc( MC_ALL ) ;					  /// @~ Запомнить номер MC, плюс рабочая установка канала 
    										                  /// @~ Если нет сбоя тренера PHY тогда:
    		if ( OKEY == ddr4_init( MC_ALL) ) PHY_waitInitDoneForAll_MCs();	/// @~ по каждому MC - жду статуса завершения init
    		else                              HALT( TRUE, "ERR BC" );  		 
    					       
    		{  PWR_t pwr = { .v       =  MC_PWR_let( MC_0 ) } ;		/// @bug 131486: Низкая FreqMEM у чипов RCD от Montage
    		         pwr     .s.pdmod =  PWR_PDMOD_0          ;	    /// @~ Режим энергосбережения отключен
    		   MC_PWR_put( MC_ALL, pwr.v )                    ;
    		}    	               
    
    	    /// @~ Fly-Настройка уровня VREFDQ. 	
         		if ( IsDDR4_VREFDQ_CHANGE_EN() )				        /// @~  If в CMOS флаг DDR4_VREFDQ_CHANGE_EN 
    		{                 ODT_t odt_r   =  { MC_ODT_let( MC_0 ) };  /// @~    Rd val of SDRAM VREFDQ from MC_ODT
    		                  u8    vrefdq  =  odt_r.v  &  0x7f      ;
    		   if ( odt_r.s.rdodt2 & 0x1 )					            /// @~    Change SDRAM VREFDQ
    		   {	  							MC_TIM0_init        ( node, MC_ALL ) ;
    		      DDR4BC_init_mc_reg( node, MC_ALL, MC_TIM2_init );		
    			    							MC_CFG_init         ( node, MC_ALL ) ;
    			    							MC_PERF0_init       ( node, MC_ALL ) ;	
    			    							MC_CTL_start_channel(       MC_ALL ) ;
    		      CTL_t ctl_r    = {    MC_CTL_let( MC_0 )          };	/// @~ Читаем содержимое регистра CTL
    		      ctl_r.s.mrs_en = ON;  MC_CTL_put( MC_ALL, ctl_r.v ); 	/// @~ Set MC_CTL.mrs_en = 1
    
    		      MOPMd_t  mopmd = { vrefdq };  mopmd.s.VDQTRG = ON;  mopmd.s.RCDcw = 0x7;
    		      MC_MOPMd_put( MC_ALL, mopmd.v ); 				        /// @~ Set MC_MOPMd.VREFDQ	
    			    							ECHO2( "MC", MC_ALL, " VrefDq:", vrefdq )	
    		      MC_MOPMd_init( node, MC_ALL ) ;				        /// @~ Set correct MC_MOPMd.tCCD_L 
    
    		      ctl_r.s.mrs_en = 0;  MC_CTL_put( MC_ALL, ctl_r.v );	/// @~ Set MC_CTL.mrs_en = 0
    		   }
    		}  
            /// @~ END - Настройка на лету уровня VREFDQ
    		/// @~ После настройки PHY настраиваем МС
    		mc_channel_regs_init_all ( node, MC_ALL );
    		for ( u8 ch = 0; ch < ddr4.mc.max; ch++ ) 	
    		{ 								                                MC_CTL_enable_channel( ch );
    		    MC_CTL_start_channel( ch );	/// @~ Set CTL.mcstart = 1 последовательно каждому каналу 
    		              MC_STATUS_t mc_st = { .v = MC_STATUS_let( ch ) };	/// @~ Читаем последовательно статусы
    		    /// @~ Задана перетренировка каналов памяти
    		    if ( mc_st.s.phyccm_par_err || mc_st.s.dmem_par_err || mc_st.s.bridge_par_err ) 
    		         WARN3( "Need ban ERR MC", ch, " trys:", trys++, " sts=", mc_st.v );
    		    /// @~ Если в рег.статуса есть сбои то HALTs
    		    HALT( (u8)mc_st.s.phyccm_par_err, "SRAM PARITY" );
    		    HALT( (u8)mc_st.s.dmem_par_err  , "DMEM PARITY" );
    		    HALT( (u8)mc_st.s.bridge_par_err, "BRDG PARITY" ); 
    		}
    
    		if ( is_watch() ) { DDR4_show_all_regs( node, MC_0 ) ;   	/// @~ Show regs for PHY
    				            MC_show_all_regs  ( node, MC_0 ) ;      /// @~ Show regs for MC
    		                  }  
    		if ( IsGearDown() )	 		
    		{  CFG_t cfg_r =  { .v    = MC_CFG_let( MC_0 ) }      ;	    /// @~ Читаем MC_CFG в его в текущем состоянии
    			     cfg_r      .s.mt = TRUE                      ;    	/// @~ Включаем режим расширения фазы адреса
    			                    MC_CFG_put( MC_ALL, cfg_r.v ) ;  	/// @~ Устанавливаем MC_CFG.mt = ON
    		   _ECHO( "SelfRefresh MC:", MC_ALL )                 ;     /// @~ Выводим сообщение + № инициализированного MC
    
    		   PWR_t pwr_r = { .s.pdmod = PDMOD_SELFREFRESH       ,     /// @~ Режим self-refresh
                               .s.memhot_en     = TRUE            , 	/// @~ Прореживание запросов для защиты от перегрева
                               .s.memhot_sense  = 0               , 	/// @~ Интервал в тактах mc_clk, входного MEM_HOT
                               .s.pdg           = TRUE            , 	/// @~ Режим power-down одновременно ко всем Rankes
                               .s.pdtmr         = PWR_PDTMR_MIN   ,     /// @~ Таймер режима энергосбережения в Min 
       				   .s.memhot_throttle = 0                   } ; 	/// @~ Интервал в тактах для выдачи по одной команде
    
                       MC_PWR_put( MC_ALL, pwr_r.v ); clk_mdelay(-10); 	/// @~ Set SelfRefresh
    
    		   pwr_r.s.pdmod = PDMOD_NONE; MC_PWR_put( MC_ALL, pwr_r.v);/// @~ Exit selfrefresh
    		}  
    	}
    
    	return	   MC_get_max_mask() ; /// @~ Выдаем максимальную маску MC в режиме BC
    } /// @~ „„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„ End Function
    
    

  •  ▤  Самые опасные инфекции в мире. ТОП10
     ▤  Сколько км пролетела ракета КНДР?
     ▤  МС-21 — магистральный самолёт 21 века
     ▤  Самые быстрые процессоры vs Эльбрус