Category Archives: network

Прерывания или что такое MSI и MSI-X

Недавно довелось разбираться с проблемой производительности сетевой подсистемы Linux. Как и предполагалось проблема была связана с обработкой сетевых прерываний. Однако выяснилось что термин MSI-X знаком не всем и далеко не все понимают преимущества этой технологии и покупают карты где она не поддерживается.
Более того, при беглом осмотре выяснилось что далеко не все вендоры продают адаптеры с поддержкой MSI-X.
Но начну я как всегда издалека.

Обработка hardware прерываний всегда была “дорогой” операцией. “Дороговизна” связана как с большим количеством прерываний, так и со сложностями по распределению их обработки.
Проблема с количеством прерываний в Linux была частично решена с появлением NAPI: вместо прерывания по каждому событию, теперь драйвер сетевой карты самостоятельно забирает пришедшие пакеты из очереди карточки.Полноценная же возможность по распределению прерываний по cpu появилась лишь с приходом PCI 3.0 и MSI-X.Но сначала немного теории.

Обработка PCI прерываний
Изначально PCI шина была ограничена 4 линиями, выделенными под прерывания. Теоретически один PCI адаптер может использовать все 4 прерывания, но так-как эти прерывания являются общими для всех карт, то большинство PCI адаптеров использую лишь 1 прерывание.
Из-за того что PCI прерывания были общими для всех устройств на шине, для корректной обработки прерывания ОС была вынужденна запускать все обработчики данного прерывания.
Если же PCI устройство писало в память, то возникала проблема синхронизации и обработчик прерывания был вынужден проверять что данные действительно записаны в память. Это было необходимо т.к. прерывание посылалось по выделенным линиям и могло быть отправлено одновременно с операцией записи а значит и “прийти” на процессор до того как данные реально будут записаны в память. Проблема синхронизации решалась на уровне драйверов: необходимо считать регистр, тем самым убедиться что операция записи завершена. Это добавляло накладных расходов для обработчика прерываний и не делало его более производительным.
Все эти проблемы был призван решить MSI.

MSI
Если ранее за каждым устройством закреплялся свой номер запроса на прерывание (IRQ), то прерывание в случае MSI это запись по заранее определенному адресу. Благодаря этому отпадает необходимость в выделенных линиях для обработки прерываний и упрощается схема самого адаптера. Кроме того решается проблема с синхронизацией т.к. операция записи в память и прерывание теперь используют общий канал и более не могут прийти на процессор одновременно.
MSI позволяет использовать десятки прерываний. Точнее до 32. Однако более 16 прерываний использовались крайне редко.

MSI-X
В PCI 3.0 появилось расширение MSI : MSI-X позволило увеличить кол-во прерываний от одного устройства до 2048. Правда использование более 64 я не встречал. Но что более важно, MSI-X использует различные адреса записи для каждого прерывания, что позволило назначить обработку отдельный прерываний на определенный процессор.

APIC и распределение прерываний между CPU
APIC (Advanced Programmable Interrupt Controller) отвечает за распределение прерываний по процессорам. APIC предусматривает несколько различных политик распределения прерываний. Так первоначально все прерывания обрабатывались одним ( нулевым ) ядром.
За распределение прерываний по CPU отвечает чип IO-APIC. Этот чип может работать в 2 режимах :
fixed/physical mode: прерывания от каждого конкретного устройства обрабатываются конкретным CPU
logical mode: прерывания одного устройства могут обрабатываться несколькими CPU.
Применение logical mode в большинстве случаев не оправдано т.к. возникают проблемы cache miss’ов в случае работы с одной сущностью несколькими CPU ( например обработке прерываний несколькими CPU для одной tcp сессии ).
Попробую описать эту проблему детальнее. Итак у вас прерывания разбрасываются round-robin по нескольким CPU. Когда приходит первый пакет tcp сессии и генерируется прерывание, то оно обрабатывается процессором X. Второе прерывание о пакете, в рамках того же tcp соединения попало на процессор Y. Так как данных о данной сессии до этого на процессоре Y не было то никаких данных о ней CPU кеше нет Имеем cache miss на процессоре Y. При этом данные были в кеше процессора X и если бы прерывание попало на процессор X то обо было бы обработано быстрее. Теперь же данные о этой tcp сессии удаляются из кеша процессора X для обеспечения когерентности кешей. Однако следующее прерывание о новом пакете в рамках этой же tcp сессии снова попадает на процессор X и мы снова получаем cache miss…
Получается что с одной стороны выгодно распределять прерывания по всем CPU, чтобы CPU не было узким место и была равномерная загрузка CPU.
С другой стороны распределение прерываний по разным CPU безо всякой логики повышает кол-во cache miss. А это “дорогая” операция также оказывает влияние на производительность.

IO-APIC и MSI
На самом деле через IO-APIC будут распределяться либо прерывания “старого” типа (INT), либо MSI/MSI-X прерывания, для устройств, драйвера которых не реализовали поддержку MSI/MSI-X.
Как я описал выше MSI/MSI-X генерирует прерывания, посылая операцию записи с определенным адресом. Соответственно если драйвер не поддерживает MSI/MSI-X то будет использоваться некий стандартный адрес и прерывания будут попадать на IO-APIC и далее уже распределяться по LAPIC ( Local APIC ) разных CPU.
В случае же если в драйвере реализована поддержка MSI/MSI-X то при инициализации адрес может быть изменен, а значит прерывания будут попадать на определенный CPU в обход IO-APIC.

Так в чем же плюс MSI-X?
Помните предыдущий пример про tcp сессию,А что если бы сетевая карта “знала” о tcp сессиях и генерировала бы прерывания для того CPU, который и занимается обработкой данной tcp сессии? Это позволило бы решить и проблему с распределением прерываний по нескольким CPU при этом обеспечивая локальность обработки пакетов определенного типа.
Именно этого и позволяет добиться MSI-X.

Теперь о командах. lspci -t показывает список PCI устройств и мостов.
После чего в файле /sys/bus/pci/devices/*/msi_bus можно увидеть включена ли поддержка MSI: 1 – включена 0 – выключена.
Распределение прерываний по ядрам CPU вы можете посмотреть в файле /proc/interrupts . Там же можно увидеть используется ли IO-APIC для распределения прерываний, или же прерывания идут напрямую через MSI.
Для каждого прерывания есть директория в /proc/irq/ . В файле smp_affinity находится bitmap, который определяет каким ядром будет обрабатываться это прерывание. Меняя этот bitmap мы можем менять привязку прерываний к ядрам процессора.

О том как обработка MSI и MSI-X прерываний реализована в Solaris отлично написано здесь : в блоге Saurabh Mishra
и частично в блоге Charlie Chen здесь и здесь

Настройка сети в Solaris 11

В отпуске наконец-то появилось время детальнее покопаться в новых фичах Solaris 11. В частности в том как устроена сетевая подсистема.
Сетевая подсистема претерпела большие изменения еще в 10 версии, но в 11 они приобрели законченный вид. Об изменениях на уроне ядра я постараюсь написать попозже. Сейчас же, коротко об изменениях в утилитах.
Если кто-либо считает, что настроить сеть в Solaris 10 было сложно, то он явно не делал этого в 11 версии.
В Solaris 10 для настройки сети использовалось несколько файлов и пара утилит. В крайнем случае, все можно было изменить динамически с помощью ifconfig. Оказывается, это было очень сложно. Чтобы упростить этот непростой процесс в Solaris 11, появились команды netadm, netcfg и ipadm и некоторые другие, а также пару демонов: netcfgd, nwamd.
Теперь по умолчанию для настройки сети используется новая фича NWAM ( Network Auto-Magic ), которая, в идеале, настроит сеть на сервере за Ва. А также автоматически перенастроит в случае каких-либо изменений. Здорово звучит не правда ли? Давайте разбираться

настройка NWAM Профиля

Посмотреть список профилей и локаций можно с помощью команды :

 netcfg list

Профиль описывает необходимые настройки сетевых адаптеров, сетевых интерфейсов и туннелей. Будьте внимательны: в 10 версии различие между адаптером и интерфейсом были столь незначительными, что многие объединяли эти понятия.
Профиль по умолчанию (Automatic) пытается получить адрес по dhcp по всем поднятым интерфейсам. Не очень типичная конфигурация для сервера. Давайте создадим профиль со статически настроенным адресом.
Сначала надо создать сам профиль и добавить в него физический интерфейс.

antony@sol11:~$ netcfg
netcfg> create ncp user
netcfg:ncp:user> create ncu phys net1
Created ncu 'net1'. Walking properties ...
activation-mode (manual) [manual|prioritized]> manual
link-mac-addr> 
link-autopush> 
link-mtu>
netcfg:ncp:user:ncu:net1> end
Committed changes

И только после того как в профиле есть интерфейс добавить в профиль ip адрес :

netcfg:ncp:user> create ncu ip net1
Created ncu 'net1'. Walking properties ...
ip-version (ipv4,ipv6) [ipv4|ipv6]> ipv4
ipv4-addrsrc (dhcp) [dhcp|static]> static
ipv4-addr> 192.168.56.2
ipv4-default-route>
netcfg:ncp:user:ncu:net1> list
 ncu:net1
 type interface
 class ip
 parent "user"
 enabled true
 ip-version ipv4
 ipv4-addrsrc static
 ipv4-addr "192.168.56.2"
 ipv6-addrsrc dhcp,autoconf
netcfg:ncp:user:ncu:net1> commit
Committed changes
netcfg:ncp:user:ncu:net1> end
netcfg:ncp:user> exit

Каждый профиль (NCP, Network configuration Profile ) должен содержать минимум 2 связанных NCU ( Network Configuration Unit ) :
– phys Описывающий настройки сетевого адаптера
– ip. Описывающий настройки интерфейса
Какую политику использовать задается в параметрах сервиса network/physical:default
Посмотреть, какая политика настроена сейчас :

svccfg -s network/physical:default listprop netcfg/active_ncp

Изменить :

svccfg -s network/physical:default setprop netcfg/active_ncp = user
svccfg -s network/physical:default refresh

При этом те интерфейсы, что были настроены профилем Automatic, не опустятся. Чтобы полностью перенастроить конфигурацию необходимо:

svcadm restart network/physical:default

И если все сделано правильно – то сеть поднимется. Очень просто, не правда ли? 😉
Все это описываю для того, чтобы было понимание, как именно настраивается сеть через NWAM. На самом деле, чтобы применить созданный Profile, достаточно одной команды:

netadm enable -p ncp user

Активный профиль можно также посмотреть в выводе этой команды:

antony@sol11:~$ netadm list
TYPE PROFILE STATE
ncp Automatic disabled
ncp user online
 ncu:phys net1 online
ncu:ip net1 online
loc Automatic offline
loc NoNet offline

С ключом -x можно посмотреть детальную информацию:

antony@sol11:~$ netadm list -x
 TYPE PROFILE STATE AUXILIARY STATE
ncp Automatic disabled disabled by administrator
ncp user online active
ncu:phys net1 online interface/link is up
 ncu:ip net1 online interface/link is up
loc Automatic online active
loc NoNet offline conditions for activation are unmet
NWAM Location

Надеюсь, с профилями более-менее разобрались. Теперь о Location’ах.
Если через профиль (NCP) настраивается ip адрес – то Location определяет остальные сетевые параметры, такие как: домен сервера, адреса DNS серверов, настройки IP filter и IPsec.
Вносить эти изменения через /etc/nsswitch.conf и прочие файлы теперь нельзя, так как содержимое этого файла автоматически генерируется из свойств сервиса. Сам же файл оставлен только для обратно совместимости.
Просмотреть настройки Locftion’а можно уже знакомой утилитой netcfg

antony@sol11:~$ netcfg list loc Automatic
loc:Automatic
 activation-mode system
 enabled false
 nameservices dns
 nameservices-config-file "/etc/nsswitch.dns"
 dns-nameservice-configsrc dhcp

Так как в данном location’е список dns сервров получается по dhcp, то в свойствах сервиса ничего нет.

antony@sol11:~$ svccfg -s svc:/network/dns/client listprop config
config application 
config/value_authorization astring solaris.smf.value.name-service.dns.client
antony@sol11:~$

Я не часто встречаю сервера, где все сетевые настройки получаются через dhcp.
Давайте создадим location, где dns сервера задаются статически :

antony@sol11:~$ netcfg 
netcfg> create loc user1
Created loc 'user1'. Walking properties ...
 activation-mode (manual) [manual|conditional-any|conditional-all]> manual
nameservices (dns) [dns|files|nis|ldap]> 
nameservices-config-file ("/etc/nsswitch.dns")> 
dns-nameservice-configsrc (dhcp) [manual|dhcp]> manual
 dns-nameservice-domain> pavlenko.net
dns-nameservice-servers> 8.8.8.8
dns-nameservice-search> 
dns-nameservice-sortlist> 
dns-nameservice-options> 
nfsv4-domain> 
 ipfilter-config-file> 
ipfilter-v6-config-file> 
ipnat-config-file> 
ippool-config-file> 
ike-config-file> 
ipsecpolicy-config-file> 
netcfg:loc:user1> list
loc:user1
 activation-mode manual
 enabled false
 nameservices dns
 nameservices-config-file "/etc/nsswitch.dns"
 dns-nameservice-configsrc manual
 dns-nameservice-domain "pavlenko.net"
 dns-nameservice-servers "8.8.8.8"
netcfg:loc:user1> commit
Committed changes
netcfg:loc:user1> quit
antony@sol11:~$

Теперь применяем данный location :

antony@sol11:~$ netadm enable -p loc user1
 Enabling loc 'user1'
antony@sol11:~$

В свойствах сервиса network/dns/client мы видим указанные в location’е настройки dns.

antony@sol11:~$ svccfg -s svc:/network/dns/client listprop config
 config application 
config/value_authorization astring solaris.smf.value.name-service.dns.client
config/domain astring pavlenko.net
 config/nameserver net_address 8.8.8.8
antony@sol11:~$

Location может быть активирован не только администратором, но и автоматически, при совпадении нужных условий. Например, можно настроить, чтобы при поднятии “внешнего” ip адреса автоматически включался ip filter с правильными параметрами.
Как настраивать сеть в Solaris 11 с помощью нового механизма NWAM – я вкратце описал.

Отключаем NWAM и делаем все по человечески

А теперь приз для тех, кто дочитал до этого места. Весь этот очень удобный механизм NWAM можно отключить.
Естественно, после этого вписать настройки в файлы, как и раньше в Solaris 10 все равно не получится.
Но настроить статическую конфигурацию можно достаточно быстро.
Существует специальный профиль: DefaultFixed. Применение данного профиля отключает NWAM. Конечно, документация советует отключать NWAM только в случае “если вы используете расширенные сетевые фичи, которые пока не поддерживаются NWAM”, но, на мой взгляд, все ровно наоборот. NWAM имеет смысл включать только в том случае, если вам действительно нужны эти фичи.
Итак отключаем NWAM :

netadm enable -p ncp DefaultFixed

Как проверить какой профиль используется – я писал выше. Еще не забыли?
Смотрим

svccfg -s network/physical:default listprop netcfg/active_ncp

После этого уже полюбившаяся нам команда netadm перестает наконец работать:

# netadm list
netadm: DefaultFixed NCP is enabled;
automatic network management is not available.
'netadm list' is only supported when automatic network management is active.

Ура!
Теперь смотрим, какие в системе есть сетевые интерфейсы и выбираем тот, что вам нужен.

antony@sol11:~$ dladm show-phys
LINK MEDIA STATE SPEED DUPLEX DEVICE
net0 Ethernet up 1000 full e1000g0
 net1 Ethernet up 1000 full e1000g1

Состояние интерфейсов можно посмотреть :

antony@sol11:~$ ipadm  show-if
IFNAME     CLASS    STATE    ACTIVE OVER
lo0        loopback ok       yes    --
net0       ip       down     no     --
net1       ip       ok       yes    --
antony@sol11:~$

Если нужный вам интерфейс в dladm есть, а в ipadm нет, то добавляем его командой

 ipadm create-ip net1

В моем случае интерфейс есть.
Чтобы посмотреть, что уже настроен:

antony@sol11:~$ ipadm show-addr
ADDROBJ TYPE STATE ADDR
lo0/v4 static ok 127.0.0.1/8
lo0/v6 static ok ::1/128
antony@sol11:~$

И наконец, добавляем нужный нам адрес на выбранном интерфейсе :

ipadm create-addr -T static -a 192.168.56.102 met1/v4

Если все сделано правильно, то адрес станет доступен и появится в выводе ipadm :

 antony@sol11:~$ ipadm show-addr
ADDROBJ TYPE STATE ADDR
lo0/v4 static ok 127.0.0.1/8
net1/v4 static ok 192.168.56.102/24
 lo0/v6 static ok ::1/128
antony@sol11:~$

Как настраивать default route’ер, я надеюсь, все знают. Команда routeadm в Solaris 11 не изменилась.
Вместо правки /etc/nsswitch.conf, теперь настраиваем name service switch через сервис

antony@sol11:~$ svccfg -s name-service/switch
svc:/system/name-service/switch> setprop config/host = "files dns"

Если используется ldap то

svc:/system/name-service/switch> setprop config/password = "files ldap"

И так далее – настраиваем все необходимые опции. После того как все необходимые опции настроены :

svc:/system/name-service/switch> end
antony@sol11:~$ svcadm refresh name-service/switch
antony@sol11:~$ svcadm restart name-service/switch

Для обратной совместимости внесенные изменения также попадают в файл /etc/nsswitch.conf
Привыкнуть к свойствам сервиса получается не сразу. По этому, советую после внесения проверять, что все правильно настроено уже через файл. И при необходимости дальше менять свойства сервиса.
Вместо правки /etc/resolv.conf настраиваем dns тоже через сервис:

antony@sol11:~$ svccfg -s dns/client
svc:/network/dns/client> listprop config
config                      application
config/value_authorization astring     solaris.smf.value.name-service.dns.client
config/domain              astring     ie.oracle.com
 svc:/network/dns/client> setprop config/nameserver = (8.8.8.8 217.118.66.243)
svc:/network/dns/client> setprop config/domain = pavlenko.net
svc:/network/dns/client> listprop config
 config application 
config/value_authorization astring solaris.smf.value.name-service.dns.client
config/nameserver net_address 8.8.8.8 217.118.66.243
config/domain astring pavlenko.net
 svc:/network/dns/client> exit
antony@sol11:~$ svcadm refresh dns/client
antony@sol11:~$ svcadm restart dns/client

После этого /etc/resolv.conf также изменится.
Ну, вот и все. Просто – не правда ли?

Балансировка сетевого трафика в Solaris

Про этот блог забыли все, даже Я сам 😉
Попробую опубликовать хотя-бы то что что было написано достаточно давно но не опубликовано.
Уже очень давно состоялась встреча Московской группы пользователей OpenSolaris где в частности рассказывалось о способах балансировки сетевого трафика в Linux и FreeBSD. Однако выяснилось что ни кто не знает что в OpenSolaris также есть балансировки трафика.
В Solaris 10 действительно подобного функционала не было, однако в OpenSolaris был добавлен ILB ( Integrated Load Balancer ). Сейчас ILB уже включен в репозиторий Solaris 11 Express и думаю он будет входить в Solaris 11.
Давайте попробуем разобраться что такое ILB и как его настраивать.
Для начала необходимо проверить установлен ли ILB и если нет, то установить :

root@solaris:~# pkg search ilbadm
INDEX      ACTION VALUE           PACKAGE
basename   file   usr/sbin/ilbadm pkg:/service/network/load-balancer/ilb@0.5.11-0.151.0.1
root@solaris:~# man pkg
root@solaris:~# pkg install pkg:/service/network/load-balancer/ilb@0.5.11-0.151.0.1
               Packages to install:     1
           Create boot environment:    No
               Services to restart:     1
DOWNLOAD                                  PKGS       FILES    XFER (MB)
Completed                                  1/1       11/11      0.2/0.2

PHASE                                        ACTIONS
Install Phase                                  38/38 

PHASE                                          ITEMS
Package State Update Phase                       1/1 
Image State Update Phase                         2/2 

PHASE                                          ITEMS
Reading Existing Index                           8/8 
Indexing Packages                                1/1
root@solaris:~# 

Теперь ILB установлен и мы можем приступить к настройке балансировки.
Для этого надо стартовать демон ilb. Но перед этим надо поднять ip forwarding. Если забыть это сделать то вывод ilbadm вам сразу же подскажет что не так :

root@solaris:~# ilbadm show-rule
ilbadm: socket() failed
root@solaris:~#

В чём ошибка очевидно, не правда ли? 😉
Итак стартуем ip forwarding :

root@solaris:~# svcadm enable svc:/network/ipv4-forwarding
root@solaris:~# svcadm enable svc:/network/loadbalancer/ilb:default

ILB поддерживает 3 режима балансировки :
– DST
– Полный NAT
– Частичный NAT
В режиме DSR ( Direct Server Return ) ILB балансирует входящий трафик между back-end серверами. При этом ответы от back-end серверов идут напрямую клиенту в обход балансировщика.
Режимы основанные на NAT балансировщик обрабатывает как входящий так и исходящий трафик меняя IP адреса в заголовках IP пакетов.
В случае Full-NAT происходит замена и source и destination адреса. Т.Е. заголовок IP пакета выглядит так, как будто back-end сервера получают трафик непосредственно от балансировщика.
Клиенты также будут получать ответы из заранее настроенного диапазона IP адресов. Например от самого балансировщика.
В случае с Half-NAT ILB меняет лишь destination IP.
Чтобы попробовать ILB я взял 3 сервера. Два из них я использовал как back-end сервера. Их адреса : 192.168.57.102 и 192.168.57.103. На них я поднял nginx. Третий сервер выступал балансировщиком. Его адрес : 192.168.57.101.
Чтобы ILB работать, ему надо указать на какие back-end сервера его балансировать. Для этого необходимо создать серверную группу :

root@solaris:~# ilbadm create-servergroup -s servers=192.168.57.102:80,192.168.57.103:80 websg
root@solaris:~# ilbadm show-servergroup
root@solaris:/tmp# ilbadm show-sg
SGNAME         SERVERID            MINPORT MAXPORT IP_ADDRESS
websg          _websg.0            80      80      192.168.57.102
websg          _websg.1            80      80      192.168.57.103

После создается само правило, по которому будет балансироваться трафик. Для начала я настроил Full-NAT:

root@solaris:/tmp# ilbadm create-rule -e -i vip=192.168.56.101,port=80 -m lbalg=rr,type=NAT,proxy-src=192.168.56.101 -o servergroup=websg webrule
root@solaris:/tmp# ilbadm show-rule
RULENAME            STATUS LBALG       TYPE    PROTOCOL VIP         PORT
webrule             E      roundrobin  NAT     TCP  192.168.56.101  80

и это всё!
Я настроил чтобы http трафик на to 192.168.56.101 ( виртуальный IP ) балансировался между двумя другими серверами. Запросы будут обрабатываться back-end серверами по очереди (round-robin). Ответы же будут приходить от адреса 192.168.56.101.
Теперь давайте поменяем правило с Full-NAT на Half-NAT :

ilbadm  delete-rule -a
ilbadm create-rule -e -i vip=192.168.56.101,port=80 -m lbalg=rr,type=h -o servergroup=websg webrule
root@solaris:/tmp# ilbadm show-rule
RULENAME            STATUS LBALG       TYPE    PROTOCOL VIP         PORT
webrule             E      roundrobin  HALF-NAT TCP 192.168.56.101  80

Всё это очень просто настраивается. При этом есть большое количество разнообразных опций. Так например можно изменить алгоритм балансировки запросов между back-end серверами. Кроме того средствами ILB можно настроить мониторинг back-end серверов Кроме того поддерживается Session persistence и Connection draining.