Solaris OS 네트워킹 -- 마술의 비밀 공개Sunay Tripathi(수석 엔지니어, Solaris Core Technology 그룹), 2006년 1월 요약: 이 기사에서는 Solaris 10 OS에서의 네트워크의 진보뿐만 아니라, 구 릴리스에서의 네트워크의 진화에 대해서도 설명합니다. TCP, UDP, IP, 장치 드라이버 프레임워크, 성능 조정을 위한 조정 등을 주제로 다루고 있습니다. 목차
1.0 배경Solaris 1.x 운영 체제의 네트워크 스택은 BSD 형식으로 BSD Reno를 설치했을 때와 유사합니다. BSD 스택은 저사양 시스템에는 적합했지만, 저사양을 원하는 고객 뿐만이 아니라 기업 고객의 요구도 만족 시키기 위해 Solaris OS를 AT&T SVR4 구조로 변경한 것이 바로 Solaris 2.x 플랫폼입니다. Solaris 2.x OS로 업그레이드되면서 네트워크 스택은 BSD 스타일의 스택에서 STREAMS를 기반으로 하는 스택으로 변경되었습니다. STREAMS 프레임워크는 간단한 메시지를 주고받는 인터페이스를 갖추고 있으므로 STREAMS 모듈 사이에서 유연한 메시지 교환이 가능했습니다. STREAMS의 내부 및 외부 경계를 사용하여 모듈의 개발자의 입장에서는 내부 구성을 복잡하지 않으며 상호 배타 실현을 가능케 했습니다. STREAMS 하나를 설정하는 비용이 높긴 했지만, 대개 접속 유지 시간이 길기 때문에 초당 몇 개의 접속을 설정했는지는 그리 중요하지 않습니다. NFS나 FPT 등과 같이 장기간을 두고 보면 접속 시간이 길어질수록 새로운 STREAMS의 설정 비용은 상환될 것입니다. 1990년대 후반에 들어서면서 서버는 다수의 CPU를 실행하는 SMP 기반을 갖추기 시작했습니다. 중급 사양 시스템에서 고급 사양 시스템이 NUMA의 주류를 이루기 시작하면서 CPU 전환 처리 비용이 증가하였습니다.STREAMS는 설계상 CPU와 밀접하게 관계가 없기 때문에 특정 접속의 패킷이 다양한 CPU 사이를 이동합니다. Solaris 제품이 STREAMS 구조를 탈피할 필요가 있는 것은 명백했습니다. 1990년대 후반은 World Wide Web이 폭발적으로 확대되었던 시기였습니다. 처리 능력이 향상됨에 따라 단시간 접속이 폭주하였으며, 접속 설정 시간도 그만큼 중요하게 되었습니다. Solaris 10 플랫폼에서는 네트워크 스택이 한층 더 변화하였으며, 코어 부분(소켓 층, TCP, UPD, IP, 장치 드라이버 등)에서 IP Classifier와 직렬화된 큐를 사용함으로써 접속 설정 시간, 확장성 및 패킷 처리 능력을 향상시켰습니다. ISV가 추가 기능을 갖추기 위해 필요한 유연성을 제공하기 위해서 STREAMS 구조는 계속 사용되고 있습니다. 2.0 Solaris 10 OS의 스택새로운 프레임워크와 그 주요 부분들의 동작 원리에 대해서 살펴봅시다. Solaris 10 OS 이전의 스택은 STREAMS 경계와 커널 적응형의 상호 배타적인 성질을 이용해 멀티 스레딩에 대처하고 있습니다. TCP는 STREAMS QPAIR 경계를 사용하고, UDP는 STREAMS QPAIR 와 PUTSHARED를 사용하며, IP는 PERMOD 경계와 PUTSHARED를 사용합니다. 또, 상호 배타로 보호되는 각종의 TCP, UDP 및 IP 전역 데이터 구조가 사용됩니다. 스택은 다양한 시스템 호출, 네트워크 장치 드라이버의 읽기 인터럽트 또는 장치 드라이버의 work-thread를 실행하는 사용자측의 thread와 STREAMS 프레임워크의 work-thread 양쪽 모두에 의해서 실행됩니다. 현재의 경계는 모듈 단위, 프로토콜 스택 레이어 단위 또는 수평 경계를 제공합니다. Solaris 9 이하 릴리스의 STREAMS 기반 스택에서는 각각의 프로토콜 레이어에 대해서 수평 경계가 제공되기 때문에 패킷이 여러 개의 CPU 상에서 프로토콜 레이어 사이에서 큐잉되는 여러 개의 스레드에 의해서 처리되는 경우가 있었습니다. 그 결과 컨텍스트가 빈번히 변환되어 접속 고유의 데이터 구조에 대해서 데이터의 국소성이 저하되었습니다. "FireEngine"방식은 모든 프로토콜 레이어를 완전하게 멀티 스레딩된 하나의 STREAMS 모듈로 병합합니다. 병합된 모듈 내에서는 데이터 구조 단위의 잠금이 아니라 "수직 경계"라고 불리는 CPU 단위의 동기화 메커니즘이 사용됩니다. "수직 경계"는 "squeue"라고 불리는 직렬화된 큐 추상화를 사용해 마운트됩니다. 각 squeue는 CPU에 바인드되어 각 접속은 접속 고유의 데이터 구조에서 필요한 동기화와 상호 배타를 제공하는 squeue에 바인드됩니다. 인바운드 패킷의 접속(또는 컨텍스트) 조회는 패킷이 IP에 도달하는 것과 동시에 경계의 외측에서 IP 접속 분류자를 사용하여 이루어집니다. 등급설정에 근거하여 접속 구조가 식별됩니다. 조회는 경계의 외부에서 발생하기 때문에 접속을 초기화할 때 수직 경계 또는 "squeue"의 임시 접속을 바인드하여, 대상 바인드의 squeue로 해당 접속의 모든 패킷을 처리하는 것으로 캐시의 국소성이 향상됩니다. 수직 경계와 분류자의 자세한 내용은 다른 섹션에서 설명합니다. 분류자는 모든 인바운드 및 아웃바운드 패킷에 필요한 일련의 함수 호출을 저장하는 데이터베이스의 역할을 하기도 합니다. 이로써 Solaris 네트워크 스택은 현재 메시지를 주고받는 인터페이스에서 BSD 스타일의 함수 호출 인터페이스로 바뀝니다. 접속 패킷 처리용 온더 플라이로 작성되는 함수의 문자열(이벤트 리스트)이 최종적인 새로운 프레임워크의 기초이며, 다른 모듈이나 Sun 이외의 고성능 모듈이 이 프레임워크에 참가할 수 있습니다.
squeue에 의해 항상 하나의 스레드만이 특정 접속을 처리하기 위해 통합된 TCP/IP 모듈 내의 여러 스레드(읽기 측과 쓰기 측)에 의한 TCP 접속 구조에 대한 액세스가 직렬화됩니다. 이는 STREAMS QPAIR 경계와 유사하지만, 모듈 인스턴스를 보호할 뿐만 아니라 IP에서 수직 경계나 squeue 자체는 패킷의 직렬화와 데이터 구조의 상호 배타를 제공할 뿐이지만, CPU 단위의 경계를 작성하여 CPU 처리 중에 접속된 인스턴스에 대한 접속을 바인드함으로써 데이터의 국소성을 높일 수 있습니다. 접속 단위의 경계 또는 CPU 단위의 경계라는 선택사항이 있습니다. 즉, 접속별 인스턴스 또는 CPU별 인스턴스라는 선택사항입니다. 접속 단위의 경계에 수반하는 오버헤드와 스레드의 경합에 의해 성능이 저하되는 것을 고려하고, CPU 단위의 인스턴스가 채용되었습니다. CPU 단위의 인스턴스인 경우에는 접속구조를 큐잉해서 처리하는 방법 또는 패킷 자체를 큐잉하는 대신 접속 구조의 포인터를 패킷에 저장하는 방법이 고려되었습니다. 전자의 방법일 경우 접속용 패킷이 끊임없이 도착하는 상황에서는 모든 패킷을 처리하지 못할 우려가 있습니다. 이러한 상황을 회피하기 위한 오버헤드로 인해 성능이 감소했습니다. 패킷의 큐잉에서는 순서가 지켜지며 처리도 매우 간단하기 때문에 FireEngine에서는 이 방법이 채용되었습니다. 전술한 바와 같이 각 접속 인스턴스는 한 개의 squeue에 할당되어 수직 경계 안에서만 처리됩니다. squeue는 한 번에 1개의 스레드에 의해 처리되기 때문에 경계 내에서 특정 접속을 처리하는데 사용되는 모든 데이터 구조는 잠겨있지 않으므로 쉽게 액세스할 수 있습니다. 이 결과 접속 메타데이터, 패킷 메타데이터 및 패킷 유효 부하 데이터의 접속, CPU와 스레드 컨텍스트 데이터의 국소성이 향상됩니다. 게다가 장치 단위의 드라이버 워커 스레드 방식을 사용하지 않아도 됩니다. 이 방식으로는 시스템 전체의 리소스 문제를 해결할 수는 없습니다. 또한 새로운 방침의 알고리즘을 채택하여 네트워크 인터페이스의 작업 처리량 및 시스템 작업 처리량에 근거하여 특정 네트워크 인터페이스를 적절히 처리할 수 있습니다(예를 들어 접속 단위의 패킷 처리를 CPU의 그룹으로 분산합니다). squeue에 들어가는 스레드가 패킷을 곧바로 처리하는 경우도 있습니다. 혹은 그것을 큐잉하고 나중에 다른 스레드 또는 워커 스레드로 처리하는 경우도 있습니다. 그 선택은 squeue의 진입점과 직렬화의 상태에 따라 달라집니다. 즉시 처리는 같은 squeue에 다른 스레드가 들어가 있지 않은 경우에게만 가능합니다. squeue는 다음의 추상화로 나타낼 수 있습니다.
typedef struct squeue_s {
int_t sq_flag; /* Flags tells squeue status */
kmutex_t sq_lock; /* Lock to protect the flag etc */
mblk_t *sq_first; /* First Packet */
mblk_t *sq_last; /* Last Packet */
thread_t sq_worker; /* the worker thread for squeue */
} squeue_t;
squeue는 코어나 하이퍼 스레드 등 H/W 실행 파이프 라인을 기준으로 작성된다는 점에 주의하십시오. 직렬화 큐(및 H/W 실행 파이프 라인)의 스택 처리는 한 번에 1개의 스레드로 한정되지만, 새로운 스택이 메모리나 수직 경계 내의 잠금 등의 리소스가 없어도 되기 때문에 실제로 성능이 개선됩니다. 또한 여러 개의 커널 스레드가 H/W 실행 파이프 라인을 시간 공유하는 것이 한 개의 스레드만이 인터럽트를 당하지 않고 실행되는 것보다 오버헤드를 수반합니다.
워커 스레드는 항상 큐 전체를 드레인할 수 있습니다. 적절한 드레인 모델을 선택하는 것은 매우 어렵지만 아래에서 몇 가지 예를 들어 보겠습니다.
이러한 옵션은 읽기 스레드와 쓰기 스레드에 개별적으로 적용할 수 있습니다. 통상 인터럽트 스레드에 의한 드레인은 항상 기한부 "드레인과 처리"인데 반하여 쓰기 스레드는 "자체 패킷을 처리 "또는 기한부 "처리와 드레인" 중 하나입니다. Solaris 10 릴리스에서는 쓰기 스레드의 동작은 튜닝 가능한 변수로 지정되어 기본값은 "자체 패킷을 처리"이지만, 읽기 측은 "기한부 처리와 드레인"으로 고정되어 있습니다. 이와는 별도로 워커 스레드의 통지는 반드시 검토해야 하는 중요한 옵션입니다. 패킷의 도착율이 낮고 스레드가 자체 패킷을 큐잉하도록 설정되어 있는 경우 실행할 작업이 있으면 입력 스레드가 squeue의 처리 종료와 더불어 동시에 워커 스레드의 실행을 허가해야 합니다. 한편 패킷의 도착율이 높은 경우는 워커 스레드 기동을 늦추고, 드레인을 완료한 후 곧바로 패킷이 도착하여 인터럽트가 발생하는 것을 기대하는 편이 바람직할 경우가 있습니다. 패킷의 도착율이 높은 상태에서 워커 스레드를 곧바로 기동하면 워커 스레드와 인터럽트 스레드 사이에 불필요한 경합이 생깁니다. Solaris 10 OS에서는 워커 스레드가 늦게 기동하는 것이 기본값으로 설정되어 있습니다. 이용 가능한 서버에서 최초로 시험해 본 결과 10 분 늦게 워커 스레드를 기동했을 때에 최적의 결과를 얻을 수 있었습니다. squeue에 요청을 넣으려면 squeue 단위의 잠금으로 큐 상태를 보호할 필요가 있지만, CPU에 분산되고 있어 그 시간도 짧기 때문에 확장성 문제는 발생하지 않습니다. 최적화도 이용하고 있고 이로써 squeue 처리에 있어서의 단일 스레드의 의미를 유지한 채로 컨텍스트의 전환을 회피할 수 있습니다. 시스템 안의 CPU별로 squeue의 인스턴스를 작성하여 해당 CPU에 워커 스레드를 바인드합니다. 그리고 각 접속은 특정 squeue 나아가 특정 CPU에 바인드됩니다. squeue와 CPU의 바인드는 변경할 수 있지만 squeue를 보호하는 의미에서 접속과 squeue의 바인드를 변경할 수 없습니다. 통합된 TCP/IP의 경우 수직 경계가 각 접속의 TCP 상태를 보호합니다. 각 접속이 사용하는 squeue 인스턴스는 아웃바운드 접속의 경우 "오픈", "바인드" 또는 "접속" 시간에 선택되고, 인바인드 접속의 경우 "접속 요청 생성 시간"에 선택됩니다. squeue 인스턴스의 선택은 시스템 안의 CPU와 NIC의 상대속도로 결정됩니다. 2가지 경우가 있습니다.
Solaris 10 OS인 경우 NIC가 CPU보다 고속인지 저속인지는 시스템 관리자가 전역변수
squeue_t *squeue_create(squeue_t *, uint32_t, processorid_t, void (*)(), \
void *, clock_t, pri_t);
void squeue_bind(squeue_t *, processorid_t);
void squeue_unbind(squeue_t *);
void squeue_enter(squeue_t *, mblk_t *, void (*)(), void *);
void squeue_fill(squeue_t *, mblk_t *, void (*)(), void *);
IP 접속의 팬 아웃 메커니즘은 3개의 해시 테이블로 구성됩니다.
조회의 일부로서 접속 구조(모든 접속 정보의 슈퍼 세트)가 반환됩니다. 이 접속 정보는
typedef struct conn_s {
kmutex_t conn_lock; /* Lock for conn_ref */
uint32_t conn_ref; /* Reference counter */
uint32_t conn_flags; /* Flags */
struct ill_s *conn_ill; /* The ill packets are coming on */
struct ire_s *conn_ire; /* ire cache for outbound packets */
tcp_t *conn_tcp; /* Pointer to tcp struct */
void *conn_ulp /* Pointer for upper layer*/
edesc_pf conn_send; /* Function to call on read side */
edesc_pf conn_recv; /* Function to call on write side */
squeue_t *conn_sqp; /* Squeue for processing */
/* Address and Ports */
struct {
in6_addr_t connua_laddr; /* Local address */
in6_addr_t connua_faddr; /* Remote address. */
} connua_v6addr;
#define conn_src V4_PART_OF_V6(connua_v6addr.connua_laddr)
#define conn_rem V4_PART_OF_V6(connua_v6addr.connua_faddr)
#define conn_srcv6 connua_v6addr.connua_laddr
#define conn_remv6 connua_v6addr.connua_faddr
union {
/* Used for classifier match performance */
uint32_t conn_ports2;
struct {
in_port_t tcpu_fport; /* Remote port */
in_port_t tcpu_lport; /* Local port */
} tcpu_ports;
} u_port;
#define conn_fport u_port.tcpu_ports.tcpu_fport
#define conn_lport u_port.tcpu_ports.tcpu_lport
#define conn_ports u_port.conn_ports2
uint8_t conn_protocol; /* protocol type */
kcondvar_t conn_cv;
} conn_t;
여기서 주목해야 할 구성원은 squeue 또는 수직 경계로의 포인터입니다. 조회는 경계의 외부에서 행해지고 패킷은 접속처의 squeue로 처리 또는 큐잉됩니다. 또한
또한 접속 팬 아웃 메커니즘에는 IP Classifier API는 다음과 같습니다.
conn_t *ipcl_conn_create(uint32_t type, int sleep);
void ipcl_conn_destroy(conn_t *connp);
int ipcl_proto_insert(conn_t *connp, uint8_t protocol);
int ipcl_proto_insert_v6(conn_t *connp, uint8_t protocol);
conn_t *ipcl_proto_classify(uint8_t protocol);
int *ipcl_bind_insert(conn_t *connp, uint8_t protocol, ipaddr_t src,
uint16_t lport);
int *ipcl_bind_insert_v6(conn_t *connp, uint8_t protocol,
const in6_addr_t * src, uint16_t lport);
int *ipcl_conn_insert(conn_t *connp, uint8_t protocol, ipaddr_t src,
ipaddr_t dst, uint32_t ports);
int *ipcl_conn_insert_v6(conn_t *connp, uint8_t protocol,
in6_addr_t *src, in6_addr_t *dst, uint32_t ports);
void ipcl_hash_remove(conn_t *connp);
conn_t *ipcl_classify_v4(mblk_t *mp);
conn_t *ipcl_classify_v6(mblk_t *mp);
conn_t *ipcl_classify(mblk_t *mp);
함수의 이름은 해당 내용을 있는 그대로 나타내고 있습니다.
수직 경계에 의해서 실현되는 CPU 단위의 직렬화를 제외하면 스택은 완전하게 멀티 스레딩되어 있기 때문에 참조 기반의 방식을 사용하고 필요한 때에 접속 인스턴스를 사용할 수 있도록 하고 있습니다. 참조 카운트는 설정된 TCP 접속에는 3개의 참조가 있습니다. 각 프로토콜 레이어는 인스턴스(TCP 와 IP에 대해 각 1개)에 대한 참조를 가집니다. 또한 분류자 자체도 설정된 접속이기 때문에 참조를 가집니다. 패킷이 접속처에 도착하여 분류자가 접속 인스턴스를 검색할 때마다 새로운 참조가 발생하지만, 프로토콜 레이어가 해당 패킷의 처리를 종료하면 폐기됩니다. 마찬가지로 접속 인스턴스 상에서 실행되는 타이머는 타이머가 기동했을 때에 언제라도 인스턴스가 동작할 수 있도록 참조를 갖추고 있습니다. 접속 인스턴스가 관련된 메모리는 마지막 참조가 폐기되면 해방됩니다. 3.0 TCP
Solaris 10 OS에 있어서의 TCP를 다루는 방법은 이전 릴리스와 같습니다. 즉 TCP는 클론 장치처럼 보이지만 실제로는 TCP 및 IP 코드가 1 개의 D_MP STREAMS 모듈로 통합된 복합물입니다. 통합된 TCP/IP 모듈의 열기와 닫기의 STREAMS 진입점은 IP의 진입점, 다시 말하자면
TCP의 조작 부분은 그림 1 에 나타나고 있듯이
그림 1 수직 경계가 사용하는 TCP 진입점: tcp_input - All inbound data packets and control messages tcp_output - All outbound data packets and control messages tcp_close_output - On user close tcp_timewait_output - timewait expiry tcp_rsrv_input - Flow control relief on read side tcp_timer - All tcp timers
FireEngine에서는 TCP와 IP 사이의 인터페이스가 제어 경로와 데이터 경로 양쪽 모두 STREAMS 기반의 메시지 입출력 인터페이스에서 함수 호출 기반의 인터페이스로 변경되었습니다. 아웃바운드 측에서는 수직 경계의 내부에 있으면서 TCP는
마찬가지로 제어 메시지도 함수 인수로서 직접 전달됩니다. 기본적인 프로토콜 처리 코드는 변경되지 않았습니다. 일반적인 소켓 호출 및 프레임워크와의 상호작용에 관해 살펴봅시다.
TCP의 소켓 개방 또는
tcp_connect 에서의 변경은 tcp_bind와 유사합니다. 완전한 bind() 요청은 TPI 메시지로서 준비되어, 함수 인수로서 ip_bind_v{4, 6}에게 전달됩니다. IP 는 분류자를 호출하여 접속된 해시 테이블에 접속을 삽입합니다. TCP 내의 conn_ hash 테이블은 사용되지 않습니다.
이 경로는 tcp_bind의 일부입니다. tcp_bind는 로컬 바인드 TPI 메시지를 준비하고 이를 함수 인수로서 ip_bind_v{4, 6}에 전달합니다. IP는 분류자를 호출해서 해당 접속을 연결된 해시 테이블에 삽입합니다.
Solaris 10 이하 릴리스에서 accept마운트에서는 수신기 컨텍스트 내에서 접속 설정을 처리하는 경우가 많았습니다. 3선 핸드셰이크는 수신기의 경계에서 완료되고 접속 지시가 수신기의 STREAMS에 송신됩니다. 수락을 실행하는데 필요한 메시지가 수신기의 STREAMS에 송신되고 수신기는 T_CONN_RES 메시지를 TCP에 송신하는 시점부터 sockfs가 긍정적인 응답을 수신할 때까지 싱글 스레드화되어 있었습니다. Solaris 10 이하 릴리스에서는 접속 도착율이 높은 경우 새로운 접속을 받아 들이는 스택의 능력은 크게 저하되었습니다.
게다가 TCP의 오버헤드가 추가되면 수락율이 더욱 떨어졌습니다.
FireEngine 모델에서는 SYN 패킷의 도착과 동시에 해당 경계 내에서 "접속 요청"을 설정함으로써(들어 오는 접속은 수락이 완료할 때까지 "접속 요청"이라고 불립니다) 해당 패킷은 항상 올바른 접속처를 찾아갑니다. 그 결과 TCP 전역 큐를 완전하게 제거하는 것이 가능합니다. 접속 지시는 계속해 수신기의 STREAM에 송신되지만 새롭게 작성된 수신 측의 STREAM에서 수락이 발생하여(이 때문에 이 STREAMS의 데이터 구조를 할당할 필요가 없습니다) 확인 응답을 수신 측의 STREAMS에서 송신할 수 있습니다. 그 때문에 새로운 들어 오는 접속(요청)이 존재하는 것은 해당 수신기가 있기 때문일 뿐이며 접속 요청이나 수신기도 접속 요청이 초기화를 수신하거나 수신기를 닫으면 수락 처리 중 임의의 시점에서 소멸할 가능성이 있기 때문에 새로운 모델에서는 신중을 기하여 구현했습니다.
수신기를 닫았을 경우에서도 수신기에 대한 접속 요청 참조가 항상 유효가 되도록 수신기에게 참조를 배치함으로써 접속 요청이 개시됩니다. 3선 핸드셰이크 완료 후에 접속 지시를 송신할 필요가 있는 경우에 접속 요청은 초기화를 수신하여 닫더라도 참조가 계속해 유효하도록 자신에게 참조를 배치합니다. 접속 요청은 접속 지시 메시지의 일부로서 자신에게 포인터를 송신하지만, 이것은 수신기가 닫지 않았다는 것을 확인하고 나서 수신기의 STREAMS를 이용해 송신됩니다. 닫는 큐와 TCP에 대한 참조가 분리되었기 때문에 TCP 내의 닫기 처리는 참조 카운트가 제로가 될 때까지 기다릴 필요가 없어졌습니다. 닫는 큐에 대한 모든 참조가 없어지는 것과 동시에 닫기는 종료됩니다. TCP 데이터 구조 자체는 대부분의 경우 분리된 TCP로서 계속해서 존재할 수 있습니다. TCP에 대한 마지막 참조가 해제되면 TCP 데이터 구조가 해방됩니다.
사용자가 실행시키는 닫기는 스트림만을 닫습니다. 기본이 되는 TCP 구조는 계속 존재합니다. TCP는 모든 사용자 데이터를 전송한 후에 Peer와의 TCP는 IRE에 액세스할 수 있다면 대부분의 경우 아웃바운드 패킷을 송신하는데 IP를 호출할 필요도 없습니다. 통합된 TCP/IP 에서는 접속용으로 캐시된 IRE에 액세스할 수 있다는 장점이 있고, TCP는 IRE 내의 정보에 기반하여 데이터를 링크 레이어 드라이버에 직접 배치할 수 있습니다. FireEngine의 처리 방법은 전술한 바와 같습니다.
TCP Fusion는 Solaris 10 OS에서의 루프백 TCP 접속용 프로토콜리스 데이터 경로입니다. 2개의 로컬 TCP 종단점의 융합은 접속이 설정될 때 발생합니다. 기본값으로는 모든 루프백 TCP 접속이 융합됩니다. 이 동작은 튜닝 가능한 시스템 변수
융합이 실패했을 경우는 통상의 TCP 데이터 경로를 사용하고, 성공했을 경우는 양쪽 모두의 종단점을 송신 경로로서
동기 STREAMS가 유효한 경우는 후자의 경로를 취합니다. 모듈의 삽입 또는 삭제가 원인으로 TCP 모듈의 상위에서
TCP Fusion 안에 있는 잠금은 squeue와 상호 배타적인 동기 스트림 모드의 TCP Fusion에 대하여 작은 쓰기 흐름 제어의 속도 제한은 각각 다른 제한으로 설정되어 있는 수신 버퍼의 크기와 데이터 블록의 수를 확인하는 것에 의해서 실현됩니다. 이는 누적 크기의 확인이 데이터 블록 카운트의 확인보다 우선하는 표준 STREAMS flow control와는 다릅니다(STREAMS 큐의 고위 경계치는 보편적으로 바이트를 의미합니다). 각 큐잉에 의해서 수신 프로세스에 통지가 보내집니다. 데이터 블록의 증가는 수신 측이 저속인 것을 나타내고 있어 송신 측은 시스템 리소스를 더 이상 낭비하지 않기 위해 가능한 한 빠른 단계에서 블록 또는 통지될 필요가 있습니다. 실제 이것은 시스템 내의 미처리 세그먼트 수를 제한하는 것과 같습니다.
큐잉 가능한 데이터 블록의 최소수의 기본값은 8입니다. 이 값은 시스템에 걸쳐 조정 가능한 4.0 UDPSolaris 10 OS에서는 프레임워크의 개선과는 별도로 스택 내에서의 UDP 패킷의 이동에 변경이 추가되었습니다. 프로젝트의 내부 코드명은 "Yosemite"였습니다. Solaris 10 이하 릴리스의 UDP 처리 비용은 패킷 단위의 처리 비용과 단위의 처리 비용이 균등하게 나뉘어 있었습니다. 통상 패킷 처리 비용은 STREAMS, 스트림 헤드 처리 및 스택과 드라이버 내에서의 패킷 폐기에 의해 발생했습니다. 바이트당 처리 비용은 H/W 체크섬의 결여와 네트워크 스택 전반에 걸쳐 최적화되지 않은 코드 분기에 의해 발생했습니다. UDP는 신뢰성이 낮다고 알려져 있지만 로컬 영역 네트워크는 신뢰성이 매우 높아지고 있고 어플리케이션은 LAN 환경 내에서의 패킷 손실이 없다고 여겨지고 있는 추세입니다. 이 가정에 대한 논란은 거의 없지만 Solaris 10 이하 릴리스에서 스택은 UDP 과부하 처리에 관해서 그다지 효율적이지 못했고 스택 자체 안에서 패킷을 폐기하는 경향이 있었습니다.인바운드에서는 전체적인 수신 경로에서 여러 개의 레이어에서 패킷이 폐기되고 있었습니다. UDP의 경우 패킷이 폐기되는 가장 일반적이고 분명한 장소는 패킷을 큐잉하는 리소스가 부족한 IP 레이어였습니다. 패킷의 폐기가 발생하는 또 하나의 중요하고 분명한 장소는 네트워크 어댑터 레이어입니다. 이런 종류의 폐기는 대체적으로 시스템에서 착신율이 높은 패킷을 처리하고 있을 때 발생합니다.
UDP
이것은 IP와 같은 보호 도메인 아래에서 실행하는 완전 멀티 스레딩 UDP 모듈입니다. 이 모듈에 의해 트랜스포트(UDP)가 상하의 레이어와 밀접하게 통합됩니다. 또한
종단점의 상태를 변경하는 함수를 실행하는 경우 UDP에서는 종단점 단위에서의 배타적인 처리가 필요합니다. Solaris 10 모델은 STREAMS에 의존하지 않는 내부 경계를 사용해 전술한 동기화를 실현합니다. 그 자세한 내용은 다음과 같습니다.
상위 또는 하위로부터 UDP에 들어가려면
이것을 지원하기 위해서 새로운 UDP 모델은 UDP MT HOT 모드 및 UDP SQUEUE 모드라고 하는 2개의 조작 모드를 도입하였습니다. UDP MT HOT 모드에서는 여러 개의 스레드가 동시에 UDP 종단점에 들어갈 가능성이 있습니다. 이는 표준 데이터의 송수신에 사용되어 putshared STREAMS 진입점과 유사합니다. 제어 조작과 그 외의 특수한 경우에
안정 모드에 있는 동안 UDP는 종단점에서 동작하는 스레드의 수를 추적합니다. UDP와 IP는 같은 보호 도메인에서 실행되지만 이것들은 전혀 다른 STREAMS 모듈입니다. 이 때문에 STREAMS의 플러밍은 그대로 보관 유지되어 UDP 모듈 인스턴스는 항상 IP 상에 배치됩니다. 이는 모든 UDP 종단점에 대해 여분의 열기와 닫기를 발생시키지만, 스트림에 I POP를 발행해 IP9 에 직접 액세스 하는 등 특정 처리를 이러한 플러밍 기하학에 의존하여 실시하는 어플리케이션에 대해서 하위 호환성을 제공합니다. 실제의 UDP 처리는 IP 인스턴스 내에서 이루어집니다. UDP 모듈 인스턴스는 종단점에 관한 상태를 가지고 있는 것이 아니라 더미 모듈로서 동작하는 것에 지나지 않습니다. 그 목적은 STREAMS 플러밍의 모양을 바꾸지 않는다는데 있습니다. Solaris 10 플랫폼에서는 다음의 플러밍 모드를 사용할 수 있습니다.
이러한 모드는 IP와 UDP 사이의 중간 모듈이 지원되어 있지 않은 것을 의미합니다. IP와 트랜스포트 모듈 사이의 레이어간 통신의 의미론은 비공개이므로 실제 Solaris 기술에서는 지금까지 이러한 시나리오를 지원하지 않았습니다.
트랜스포트 모듈의 경우 동기 STREAMS는 메시지를 주고 받거나 처리하는 것을 목적으로 하는 기존의 STREAMS 인터페이스의 확장 기능입니다. 처음에는 복사와 체크섬을 조합한 기능의 일부로서 추가되었습니다. 이 기능에 의해 모듈 또는 드라이버의 진입점을 사용자의 I/O 요청에 대해 동기 형식에서 호출할 수 있습니다. 기존의 STREAMS에서는 이러한 요청에 대해서 스트림 헤드가 동기 배리어입니다. 동기 STREAMS는 이 배리어를 스트림 헤드로부터 아래의 모듈로 이동하는 메커니즘을 제공합니다.
동기 STREAMS의 TCP 마운트는 다양한 이유로 Solaris 10 이하 릴리스에서는 복잡했습니다. 가장 큰 이유 중 하나는 체크섬과
이에 비하여 Solaris 10 OS의 경우는 TCP는
데이터가 도착할 때마다 트랜스포트 모듈은 어플리케이션에 의한 데이터 취득을 스케줄합니다. 읽기 처리 시에 어플리케이션이 블록 상태인 경우(sleeve 상태)는 블록이 해제되고 실행이 재개됩니다. 이것은 스트림 상에서
어플리케이션은 읽기 이벤트가 발생할 때까지 읽기 처리의 일부로서 트랜스포트 모듈은 읽기 측의 동기 STREAMS 진입점으로부터 데이터를 돌려주는 형식으로 어플리케이션에 데이터를 전달합니다. 루프백 TCP의 경우 동기 STREAMS의 읽기 진입점은 해당 수신 큐의 내용 전체(바이트 스트림)를 스트림 헤드에 반환합니다. 나머지 데이터는 다음의 읽기를 기다리는 스트림 헤드로 큐잉됩니다. UDP의 경우 읽기 진입점은 한 번에 1개의 메시지(데이터 그램)만을 반환합니다.
디폴트에서는
(소켓 종단점에서는 I INSERT 또는 I REMOVE
폴백이 필요한 경우 5.0 IP전술한 바와 같이 모든 트랜스포트 레이어는 STREAMS 모듈과 마찬가지로 완전하게 멀티 스레딩되어 유사 장치 드라이버로서 동작하는 IP 모듈에 통합되어 있습니다. IP에 있어서의 주요한 변경은 IP 클라이언트 기능의 삭제와 인바운드 패킷 스트림의 다중화였습니다. 새로운 IP Classifier(여전히 IP 모듈의 일부)의 역할은 인바운드 패킷을 적절한 접속 인스턴스로 분류하는 것입니다. IP 모듈은 계속해서 네트워크 레이어 프로토콜의 처리와 네트워크 인터페이스의 플러밍 및 관리를 취급합니다. 새로운 스택 내에서의 네트워크 인터페이스, 다중 경로 및 멀티 캐스트의 플러밍의 동작 시스템에 대해 살펴 봅시다.
플러밍은 IP, ARP 및 장치 드라이버간에서 메시지 교환에 관련된 일련의 긴 처리를 말합니다. IOCTL SET의 대부분은 통상 플러밍 처리와 관계가 있습니다. 보통 모델에서는 이러한 IOCTL를 ILL(IP Lower Level)마다 1씩 직렬화합니다. 예를 들어 한층 더 꼼꼼한 방법을 사용하여 IIL마다가 아니라 IPIF마다 처리를 직렬화한다는 방법도 생각할 수 있습니다. 이 방법은 많은 IPIF가 ILL 상에서 호스트되어 있고, 다른 IPIF 상의 처리가 서로 간섭하지 않는 경우만 효과가 있습니다. 게다가 표준의 Solaris MT 수법을 사용하여 모든 IOCTL를 완전하게 멀티 스레딩하는 방법도 생각할 수 있습니다. 그러나 이 방법은 필요이상으로 복잡하고 그만큼의 부가가치는 없습니다. 수신 및 드라이버나 다른 모듈과의 메시지 교환이 포함되는 플러밍 시퀀스 전체적으로 잠금을 유지하는 것은 곤란합니다. 순수한 반복적이지 않은 제어 처리를 이루어지기 때문에 IPIF 상에 여러 개의 IOCTL SET을 동시에 허가하는 것은 성능적으로도 기능적으로도 장점이 없습니다. 브로드캐스트 IRE는 IPIF 단위가 아닌 IIL 단위로 작성됩니다. 이 때문에 IIL 상에서 여러 개의 IPIF를 동시에 기동하려고 하면 IRE 작성 논리가 매우 복잡하게 됩니다. 한편 IIL 단위로의 플러밍 처리의 직렬화는 기존의 IP 코드 기반에 간단하게 도움이 됩니다. 플러밍 중에서 IP는 장치 드라이버 및 ARP와 메시지를 교환합니다. 기본이 되는 장치 드라이버로부터 수신한 메시지도 IP에서 배타적으로 처리됩니다. 쓰기 입 측과 읽기 측의 처리 과정 사이에 상호 배타를 제공하는 가운데 putnext에서는 표준의 상호 배타 잠금을 유지할 수 없기 때문에 이 방법은 편리합니다. 전배타적인 PERMOD syncq가 아니라 ILL 단위의 직렬화 큐를 사용함으로써 이 효과는 간단하게 실현될 수 있습니다.
IPMP 처리는 모두 IPMP 그룹 개념에 근거하여 이루어집니다. fail over 처리와 fail back 처리는 통상은 같은 IPMP 그룹에 포함되는 2개의 IIL 사이에서 이루어집니다. IPIF와 ILM는 IIL사이를 이동합니다. 이것에는 발신지 ILL가 하위로 이동하는 것이 포함되고, 경우에 따라서는 송신지 ILL가 상위로 이동하는 것이 포함됩니다. IIL의 상하에의 이동은 브로드캐스트 IRE에 영향을 줍니다. 브로드캐스트 IRE 는 브로드캐스트 패킷이 중복하여 수신되지 않도록 IPMP 그룹마다 그룹화할 필요가 있습니다. 이 때문에 브로드캐스트 IRE의 조작은 IPMP 그룹의 모든 멤버에게 영향을 줍니다.
멀티 캐스트 조인은 ILG 구조와 ILM 구조의 양쪽 모두에서 동작합니다. 멀티 캐스트 조인을 실행하고자 하는 IPC(소켓) 상에서 동작하고 있는 여러 개의 스레드는 ILG로 동작할 경우에는 동기화할 필요가 있습니다. 멀티 캐스트 조인을 실행하고자 하는 다른 IPC(소켓 종단점) 상에서 동작하고 있을 가능성이 있는 여러 개의 스레드는 최종적으로 ILM을 동시에 조작하는 경우가 있으므로 ILM에 대한 액세스를 동기화할 필요가 있습니다. 두 경우 모두 표준적인 Solaris MT 수법에 따릅니다. 지금까지 언급한 플러밍, IPMP 및 멀티 캐스트를 모두 고려했을 경우 그 공통점은 IPMP 그룹 단위로 모든 배타적 연산을 직렬화하는 것입니다. IPMP가 유효하지 않은 경우는 6.0 Solaris 10 장치 드라이버 프레임워크Solaris 10 OS 이전의 네트워크 장치 드라이버의 마운트 방법과 이것을 새로운 Solaris 10 스택으로 왜 변경해야 하는지에 대해서 간단하게 설명합니다. 6.1 GLDv2 및 모노리식 드라이버(Solaris 9 이하 릴리스) Solaris 10 이하 릴리스에서는 네트워크 스택은 통상 2개의 방법 중 어느 하나가 마운트되는 DLPI1 공급자를 경유합니다. 다음 그림(그림 2)은 이른바 모노리식 Data Link Provider Interface(DLPI) 드라이버에 근거하는 스택과 Generic LAN Driver(GLDv2) 모듈을 이용하는 드라이버에 근거하는 스택을 나타내고 있습니다.
그림 2 GLDv2 모듈은 기본적으로 라이브러리로서 동작합니다. 클라이언트는 계속 장치에 바인드되어 있는 드라이버 인스턴스와 교환하지만, DLPI 프로토콜 처리는 GLDv2 모듈을 호출함으로써 실행되는데 이것이 다음에 드라이버를 호출하여 하드웨어에 액세스합니다. GLD 모듈을 사용함으로써 드라이버의 개발자에게 거의가 범용적인 대량의 DLPI 프로토콜 처리를 재마운트하지 않아도 되는 분명한 이점이 있습니다. 802.1q 가상 LAN(VLAN) 등의 층 2(데이터 링크)개의 기능도 주로 GLD 모듈에 마운트할 수 있도록 모든 드라이버에서 이를 이용할 수 있습니다. 다만 802.3ad 링크 집적(trunking) 등 네트워크 인터페이스와 장치가 1:1로 대응하지 않는 기능을 마운트하는 경우 아키텍처에 문제가 생깁니다. GLDv2 및 모노리식 드라이버는 양쪽 모두 DLPI 메시지에 의존하고 STREAMS 프레임워크를 통해서 상위 레이어와 통신합니다. 이 메커니즘은 링크 집적이나 10Gb NIC에서는 별로 효율적이 못했습니다. 새로운 스택에서는 데이터의 국소성을 실현하여 스택이 치밀하게 장치 드라이버를 제어해 인터럽트를 처리할 수 있는 뛰어난 메커니즘이 필요했습니다. Solaris 10 소프트웨어에는 새로운 스택과 함께 GLDv3(내부명 "project Nemo")라고 하는 새로운 장치 드라이버 프레임워크가 도입되었습니다. 주요한 장치 드라이버의 대부분은 이 프레임워크에 이식되어 향후의 모든 장치 드라이버나 10Gb 장치 드라이버는 이 프레임워크를 따르게 됩니다. 이 프레임워크에는(외부의 비 IP 모듈이 계속해 동작할 수 있도록) 하위 호환성용으로 STREAMS 기반의 DLPI 레이어도 준비되어 있습니다. GLDv3 아키텍처는 네트워크 스택의 레이어 2를 가상화합니다. 네트워크 인터페이스와 장치 사이에 1:1 관계는 이미 존재하지 않습니다. 다음 그림(그림 3)은 MAC 서비스 모듈(MAC)에 등록된 여러 개의 장치를 나타내고 있습니다. 또한 DLPI를 통해서 데이터 링크 드라이버(DLD)와 통신하는 기존의 클라이언트와 커널 기반에서 데이터 링크 서비스 모듈(DSL)에 대하여 함수 호출을 직접 실시하는 클라이언트도 나타나고 있습니다.
그림 3 6.2.1 GLDv3 드라이버
GLDv3 드라이버는 GLD 드라이버와 많이 닮았습니다. 드라이버는 misc/mac. 및 misc/dld. 의 의존관계에 링크하고 있을 필요가 있습니다. 아래 구조의 인스턴스에 대한 포인터를 지정하여
typedef struct mac {
const char *m_ident;
mac_ext_t *m_extp;
struct mac_impl *m_impl;
void *m_driver;
dev_info_t *m_dip;
uint_t m_port;
mac_info_t m_info;
mac_stat_t m_stat;
mac_start_t m_start;
mac_stop_t m_stop;
mac_promisc_t m_promisc;
mac_multicst_t m_multicst;
mac_unicst_t m_unicst;
mac_resources_t m_resources;
mac_ioctl_t m_ioctl;
mac_tx_t m_tx;
} mac_t;
이 구조는 등록의 유효기간 중 존속할 필요가 있습니다. 예를 들어
이
typedef uint64_t (*mac_stat_t)(void *, mac_stat_t);
이 진입점은
typedef int (*mac_start_t)(void *);
인터페이스가 등록되었을 때에 리셋/휴지 상태인 장치를 그 상태로부터 해제하기 위해서 이 진입점이 호출됩니다. 이 호출을 이루어질 때까지는 MAC 모듈이 전송용 패킷을 송신하지 않고 드라이버는 수신용의 패킷을 송신할 수 없습니다. 이 함수가 정상 종료하면 제로가 반환됩니다. 이상종료(ABEND)되면 해당하는
typedef void (*mac_stop_t)(void *); 이 진입점은 인터페이스의 등록을 해제할 수 있도록 장치를 정지해 리셋/휴지 상태로 합니다. 이 호출이 실행되면 MAC 모듈이 전송용 패킷을 송신하는 경우는 없고, 이 호출이 완료되면 드라이버는 수신용의 패킷을 송신할 수 없습니다.
typedef int (*mac_promisc_t)(void *, boolean_t);
이 진입점은 장치의 혼재를 설정하기 위해 사용됩니다. 2번째의 인수가
typedef int (*mac_multicst_t)(void *, boolean_t, const uint8_t *);
이 진입점은 장치가 패킷을 수신하는 멀티 캐스트 주소 세트에 주소를 추가 또는 삭제하기 위해서 사용됩니다. 2번째의 인수가
typedef int (*mac_unicst_t)(void *, const uint8_t *); 이 진입점은 새로운 장치 uni-cast 주소를 설정하기 위해 사용됩니다. 이 호출을 실행되면 장치가 휴지 모드인 경우를 제외하고 새로운 주소 및 미디어 브로드캐스트 주소를 가지는 패킷만을 수신합니다.
typedef void (*mac_resources_t)(void *, boolean_t); 이 진입점은 드라이버가 개개의 수신 리소스 또는 Rx 링을 등록하는 것을 요구하기 위해서 호출됩니다.
typedef mblk_t *(*mac_tx_t)(void *, mblk_t *);
이 진입점은 장치가 전송용의 패킷을 송신하기 위해서 사용됩니다. 2번째의 인수는
typedef struct mac_info {
uint_t mi_media;
uint_t mi_sdu_min;
uint_t mi_sdu_max;
uint32_t mi_cksum;
uint32_t mi_poll;
boolean_t mi_stat[MAC_NSTAT];
uint_t mi_addr_length;
uint8_t mi_unicst_addr[MAXADDRLEN];
uint8_t mi_brdcst_addr[MAXADDRLEN];
} mac_info_t;
typedef enum {
MAC_STAT_IFSPEED = 0,
MAC_STAT_MULTIRCV,
MAC_STAT_BRDCSTRCV,
MAC_STAT_MULTIXMT,
MAC_STAT_BRDCSTXMT,
MAC_STAT_NORCVBUF,
MAC_STAT_IERRORS,
MAC_STAT_UNKNOWNS,
MAC_STAT_NOXMTBUF,
MAC_STAT_OERRORS,
MAC_STAT_COLLISIONS,
MAC_STAT_RBYTES,
MAC_STAT_IPACKETS,
MAC_STAT_OBYTES,
MAC_STAT_OPACKETS,
MAC_STAT_ALIGN_ERRORS,
MAC_STAT_FCS_ERRORS,
MAC_STAT_FIRST_COLLISIONS,
MAC_STAT_MULTI_COLLISIONS,
MAC_STAT_SQE_ERRORS,
MAC_STAT_DEFER_XMTS,
MAC_STAT_TX_LATE_COLLISIONS,
MAC_STAT_EX_COLLISIONS,
MAC_STAT_MACXMT_ERRORS,
MAC_STAT_CARRIER_ERRORS,
MAC_STAT_TOOLONG_ERRORS,
MAC_STAT_MACRCV_ERRORS,
MAC_STAT_XCVR_ADDR,
MAC_STAT_XCVR_ID,
MAC_STAT_XVCR_INUSE,
MAC_STAT_CAP_1000FDX,
MAC_STAT_CAP_1000HDX,
MAC_STAT_CAP_100FDX,
MAC_STAT_CAP_100HDX,
MAC_STAT_CAP_10FDX,
MAC_STAT_CAP_10HDX,
MAC_STAT_CAP_ASMPAUSE,
MAC_STAT_CAP_PAUSE,
MAC_STAT_CAP_AUTONEG,
MAC_STAT_ADV_CAP_1000FDX,
MAC_STAT_ADV_CAP_1000HDX,
MAC_STAT_ADV_CAP_100FDX,
MAC_STAT_ADV_CAP_100HDX,
MAC_STAT_ADV_CAP_10FDX,
MAC_STAT_ADV_CAP_10HDX,
MAC_STAT_ADV_CAP_ASMPAUSE,
MAC_STAT_ADV_CAP_PAUSE,
MAC_STAT_ADV_CAP_AUTONEG,
MAC_STAT_LP_CAP_1000FDX,
MAC_STAT_LP_CAP_1000HDX,
MAC_STAT_LP_CAP_100FDX,
MAC_STAT_LP_CAP_100HDX,
MAC_STAT_LP_CAP_10FDX,
MAC_STAT_LP_CAP_10HDX,
MAC_STAT_LP_CAP_ASMPAUSE,
MAC_STAT_LP_CAP_PAUSE,
MAC_STAT_LP_CAP_AUTONEG,
MAC_STAT_LINK_ASMPAUSE,
MAC_STAT_LINK_PAUSE,
MAC_STAT_LINK_AUTONEG,
MAC_STAT_LINK_DUPLEX,
MAC_STAT_LINK_STATE,
MAC_NSTAT /* must be the last entry */
} mac_stat_t;
매크로 6.2.2 MAC 서비스(MAC) 모듈 몇 개의 중요한 드라이버 지원 함수는 다음을 포함합니다.
extern mac_resource_handle_t mac_resource_add(mac_t *, mac_resource_t *); 다양한 구성원이 다음과 같이 정의됩니다.
typedef void (*mac_blank_t)(void *, time_t, uint_t);
typedef mblk_t *(*mac_poll_t)(void *, uint_t);
typedef enum {
MAC_RX_FIFO = 1
} mac_resource_type_t;
typedef struct mac_rx_fifo_s {
mac_resource_type_t mrf_type; /* MAC_RX_FIFO */
mac_blank_t mrf_blank;
mac_poll_t mrf_poll;
void *mrf_arg;
time_t mrf_normal_blank_time;
uint_t mrf_normal_pkt_cnt;
} mac_rx_fifo_t;
typedef union mac_resource_u {
mac_resource_type_t mr_type;
mac_rx_fifo_t mr_fifo;
} mac_resource_t;
이 함수는 각각의 수신 리소스(일반적으로는 DMA 기술자의 링 버퍼)를 MAC 모듈에 등록하기 위해서
이
그 외의 필드의
인터럽트율은 다른 인수를 지정하여
extern void mac_resource_update(mac_t *); 사용 가능한 리소스가 변경되었을 경우에 드라이버로부터 호출됩니다.
extern void mac_rx(mac_t *, mac_resource_handle_t, mblk_t *);
이 함수는 6.2.3 데이터 링크 서비스(DLS) 모듈 DLS 모듈은 DLPI와 유사한 데이터 링크 서비스 인터페이스를 제공합니다. DLPI로 지정되는 STREAMS 메시지 기반의 인터페이스에 대해서 DLS 인터페이스는 커널 수준의 함수 인터페이스입니다. 이 모듈은 상위 레이어가 데이터 링크 서비스를 작성 및 삭제하는데 필요한 인터페이스와 NIC를 플러밍 및 언플러밍하는데 필요한 인터페이스도 제공합니다. GLDv3 기반의 장치 드라이버에 있어서 NIC의 플러밍 및 언플러밍은 이전의 GLDv2 또는 모노리식 DLPI 장치 드라이버 때부터 변경되지 않았습니다. 주요한 변경은 직접 소환, 패킷 체인 및 NIC에 대한 치밀한 제어가 가능한 데이터 경로에 있습니다. 6.2.4 데이터 링크 드라이버(DLD) 데이터 링크 드라이버는 DLS 및 MAC 모듈이 제공하는 인터페이스를 사용하는 DLPI를 지원합니다. 드라이버는 제어 노드에게 건네지는 IOCTL를 사용해 구성됩니다. 이러한 IOCTL가 각각의 DLPI 공급자 노드를 작성 및 삭제합니다. 이 모듈은 NIC를 플러밍/언플러밍하는데 필요한 DLPI 메시지를 취급하며 GLDv3 를 지원하지 않는 클라이언트용으로 STREAMS를 경유하는 데이터 경로에 대해서 하위 호환성을 제공합니다. GLDv3 프레임워크는 IEEE 802.3ad로 정의되는 링크 집적을 지원합니다. 이 기능을 설계하는데 있어서 주요한 설계 원칙은 다음과 같습니다.
GLDv3의 링크 집적은
그림 4
GLDv3 Solaris 10 플랫폼에서는 H/W 체크섬 오프로드 기능이 개선되어 거의 어플리케이션으로 전체의 성능이 향상했습니다. 일정 시기에 16 비트의 1의 보수 체크섬 오프로드 프레임워크가 Solaris 소프트웨어에 존재하고 있었습니다. 이것은 처음에 Solaris 2.6 OS의 Zero copy TCP/IP 의 요건으로서 추가되었지만 최근까지 다른 프로토콜을 처리할 수 있도록 확장되지 않았습니다. Solaris 기술에서는 2개 클래스의 체크섬 오프로드를 다음과 같이 정의하고 있습니다.
현재의 대부분의 네트워크 어댑터는 인터페이스에 약간의 차이는 있지만, 어느 것이든 하나의 클래스 체크섬 오프로드를 지원하기 때문에 단편화 되어 있지 않은 IPv4의 케이스(uni-cast 또는 멀티 캐스트)의 지원을 추가하는 것은 송신과 수신의 양쪽 모두에 있어서 중요하지는 않습니다. IPv4/IPv6 상의 TCP/UDP 패킷에 대해 체크섬 계산을 처리할 수 있는 완전 체크섬 네트워크 어댑터는 거의 없기 때문에 IPv6의 경우는 그다지 간단하지는 않습니다. 단편화된 IP의 경우도 이와 같은 제약이 있습니다. 송신 시 체크섬은 단편화되어 있지 않은 데이터 그램에 적용됩니다. 어댑터가 체크섬 오프로드를 지원하기 위해서는 최종적으로 체크섬을 계산하여 회선상에서 단편(fragment)을 송신하기 전에 모든 IP 단편(fragment)을 버퍼링할 수 있어야(또는 하드웨어 내에서 단편화를 실행할 수 있어야) 합니다. 그 때까지 아웃바운드 IP 단편(fragment)의 체크섬 오프로드를 실행할 수 없습니다. 이것에 대해서 대부분의 완전 체크섬(및 모든 부분 체크섬) 네트워크 어댑터는 체크섬 값을 계산하여 그 값을 네트워크 스택에 제공할 수 있기 때문에 수신 단편(fragment)의 리어셈블리는 유연하게 이루어집니다. 단편(fragment)의 리어셈블리의 단계에서 네트워크 스택은 모든 값을 결합함으로써 단편화되어 있지 않은 데이터그램의 체크섬 상태를 요구할 수 있습니다. IP 옵션이 있는 경우는 체크섬을 오프로드 하지 않게 되어 처리가 간단해졌습니다. 부분 체크섬 오프로드의 경우 일부의 어댑터에서는 개시 오프셋을 단순한 IP 패킷에 충분한 폭에 제한합니다. 옵션이 있기 때문에 프로토콜 헤더의 길이가 이 제한을 넘는 경우 개시 오프셋이 되돌아 오기 때문에 계산이 올바르게 실행되지 않습니다. 완전 체크섬 오프로드의 경우 해당의 어댑터로 IPV4 소스 루팅 옵션을 올바르게 처리할 수 있는 것은 없습니다. 송신의 체크섬 오프로드가 발생하면 네트워크 스택은 해당의 패킷을 드라이버가 체크섬 계산을 하드웨어에 오프로드하는 데 필요한 부속 정보에 연관짓습니다. 인바운드의 경우 드라이버는 하드웨어로 계산되는 체크섬치에 관련지을 수 있는 패킷을 완전하게 제어할 수 있습니다. 드라이버가 DL CAPAB HCKSUM을 통해서 그 기능을 통지하면 네트워크 스택은 IPv4 및 IPv6 패킷의 완전 또는 부분 체크섬 정보를 받아들입니다. 이 프로세스는 단편화되어 있지 않은 유효 하중과 단편화된 유효 하중의 양쪽 모두에 대해 발생합니다. 체크섬 검증은 완전하게 리어셈블리 된 데이터 그램에 대해서 발생하기 때문에 단편화된 패킷은 최초로 리어셈블리 프로세스를 경유할 필요가 있습니다. 리어셈블리 중에 네트워크 스택은 하드웨어로 계산된 각 단편(fragment)의 체크섬 값을 결합합니다. 6.4..
집적의 키는 1~65535의 정수입니다. 장치에 따라서는 구성 가능한 데이터 링크 또는 집적을 지원하지 않습니다. 이러한 장치로 제공되는 고정된 데이터 링크는 GLDv3 프레임워크에서 사용자는 집적을 구성하는 한편 집적의 각종 구성원에 걸친 아웃바운드 로드 균형 조정 정책을 선택할 수 있습니다. 정책에서는 패킷 송신에 사용되는 dev 객체를 지정합니다. 정책은 1이상의 레이어 지정자를 콤마(,)로 구분한 목록으로 구성됩니다. 레이어 지정자는 다음 중 하나입니다.
예를 들어 상위 레이어의 프로토콜 정보를 사용하려면 다음의 정책을 사용합니다. -P L4 발신지 및 송신지 IP 주소에 덧붙여 발신지 및 송신지 MAC 주소를 사용하려면 다음의 정책을 사용합니다. -P L2,L3
프레임워크는 새로운 장치가 시스템에 삽입되면 재구성 부팅 또는 DR 시에 해당 장치에 대해서 기본값의 비 VLAN 데이터 링크가 작성됩니다. 모든 객체의 구성은 재부팅해도 지속됩니다.
장기적으로 볼 때는 7.0 성능 조정
Solaris 10 스택의 조정은 사용 중인 하드웨어에 관계없이 뛰어난 혁신적인 성능을 실현하도록 설계되었습니다. 그 비결은 인터럽트 모드와 폴링 모드의 동적인 변환이나 부하가 매우 높은 경우에 폴링 모드로 전환하고 처리량을 높이거나 적절한 대기 시간을 정하는 등의 방법을 사용하는 것입니다. NIC에 의한 패킷 단위의 인터럽트를 허가하는 것으로 부하가 관리 가능한 경우 인터럽트 모드와 폴링 모드의 동적인 변환에 의해 대기 시간이 크게 향상됩니다. 기본값도 하드웨어 구성에 근거하여 신중하게 선택하였습니다. 예를 들어 Solaris 10 이하 릴리스에서는 조정 가능한 변수 이러한 기능에도 불구하고 경우에 따라서는 조정 가능한 몇 개의 변수를 조정하여 극단적인 경우나 특정 작업의 부하량에 대처할 필요가 있습니다. 다음 섹션에서는 스택의 동작을 제어하는 조정 가능 변수에 대해 설명합니다. 그 영향을 충분히 이해하는 것이 중요합니다. 내용을 충분히 이해하지 못한 상태에서 조작하면 시스템이 불안정해 질 수 있습니다. 대부분의 어플리케이션과 작업 부하량은 기본값에서 최적의 결과를 얻을 수 있다는 것에 유의하십시오.
set ip:ip_squeue_fanout=1
set ip:ip_squeue_bind=0
기본값은 2 이지만 set ip:tcp_squeue_wput=1 CPU의 수가 활성화된 NIC의 수보다 훨씬 많아 플랫폼의 메모리 대기 시간이 기본적으로 길기 때문에 어플리케이션 스레드가 squeue의 드레인을 실시하여 움직일 수 없게 될 가능성이 높은 경우는 이 값을 1로 설정합니다.
ip:ip_squeue_wait=0
또한 특히 대량의 메모리를 마운트한 시스템에서는 프로토콜 수준의 조정( 8.0 향후의 전망Solaris 네트워크 스택은 계속해서 레이어간의 수직통합을 기초로 하기 때문에 국소성과 성능면에서 더욱 더 개선될 것이라고 예상됩니다. tip 멀티 스레딩 및 멀티 코어 CPU가 출현함에 따라 저사양 시스템에서도 병렬 실행 파이프 라인의 수가 계속 증가할 것이라고 예상됩니다. 현대의 일반적인 2 CPU 시스템은 듀얼 코어이며 실행 파이프 라인의 수는 4로 대부분의 경우 하이퍼 스레드에도 대응하고 있습니다. NIC도 성능이 향상되어 MSI-X에 의한 여러 개의 인터럽트, 세세한 분류기능, 여러 개의 DMA 채널, 대규모 세그먼트 오프로드와 같은 각종 Stateless Offload 등을 제공합니다. 향후, TCP 오프로드 엔진, Remote Direct Memory Access(RDMA), iSCSI 의 지원을 포함하여 이러한 하드웨어의 동향을 활용해 나가는 것이 기대됩니다. 현재 다루고 있는 그 밖의 특별한 분야는 다음과 같습니다.
9.0 감사의 말씀이 기사의 일부를 기고해 준 Thirumalai Srinivasan, Adi Masputra, Nicolas Droux 및 Eric Cheng께 감사의 말씀을 드립니다. Solaris 네트워크 커뮤니티에 계신 모든 회원님들의 아낌없는 지원에도 감사드립니다. 여기서 채택한 모든 기술 자료 내의 코드(기사, FAQ, 샘플 포함)는 특히 다른 라이센스에 대한 언급이 없는 한 이라이센스를 통해 제공됩니다. |
BigAdmin SubscriptionsBigAdmin Areas
BigAdmin Sun Center
BigAdmin Topics |