← Documents Documentation/networking/device_drivers/ethernet/ti/cpsw_switchdev.rst GitHub 원문 ↗

Linux 6.18.37 · Networking

TI CPSW Switchdev Ethernet Driver

CPSW dual-EMAC과 switch 모드의 차이, bridge/VLAN forwarding 구성 및 NFS 부팅 중 안전하게 네트워크를 재구성하는 예를 설명합니다.

Source pathDocumentation/networking/device_drivers/ethernet/ti/cpsw_switchdev.rst
Source versionLinux v6.18.37
TranslationDUJINLABS 전문 번역 + 해설

요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.

1. 요약·해설

원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.

요약·해설

cpsw_switchdev.rst:1-242

새 CPSW 드라이버는 기본 dual-EMAC 모드에서 두 포트를 분리하고 learning을 끕니다. switch 모드로 바꾸려면 내부 예약 VID와 bridge의 `default_pvid` 충돌을 먼저 해소해야 하며, NFS root를 사용하는 시스템은 최소 chroot 안에서 bridge를 재구성해야 연결 손실 위험을 줄일 수 있습니다.

CPSW 운용 모드 비교
항목dual-EMACswitch 모드
인터페이스독립 netdev 2개하나의 offloaded bridge 포트
Learning비활성화FDB/MDB와 switchdev 연동
Promiscuous`P0_UNI_FLOOD`와 ALLMULTIbridge 정책에 따름
내부 VID포트 번호에 해당하는 VID 2개 예약bridge VLAN 구성으로 관리
전환기본값devlink `switch_mode=1`

기본 dual-EMAC과 switchdev 모드의 동작 차이입니다.

NFS 환경의 구성 전환
NFS root최소 chroot 생성네트워크 도구 복사CPSW bridge 구성`br0` DHCP

root filesystem 연결을 끊지 않도록 별도 실행 환경을 준비합니다.

2. 영어 원문 전체

번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.

원문 전체 펼치기
1 .. SPDX-License-Identifier: GPL-2.0
2
3 ======================================================
4 Texas Instruments CPSW switchdev based ethernet driver
5 ======================================================
6
7 :Version: 2.0
8
9 Port renaming
10 =============
11
12 On older udev versions renaming of ethX to swXpY will not be automatically
13 supported
14
15 In order to rename via udev::
16
17 ip -d link show dev sw0p1 | grep switchid
18
19 SUBSYSTEM=="net", ACTION=="add", ATTR{phys_switch_id}==<switchid>, \
20 ATTR{phys_port_name}!="", NAME="sw0$attr{phys_port_name}"
21
22
23 Dual mac mode
24 =============
25
26 - The new (cpsw_new.c) driver is operating in dual-emac mode by default, thus
27 working as 2 individual network interfaces. Main differences from legacy CPSW
28 driver are:
29
30 - optimized promiscuous mode: The P0_UNI_FLOOD (both ports) is enabled in
31 addition to ALLMULTI (current port) instead of ALE_BYPASS.
32 So, Ports in promiscuous mode will keep possibility of mcast and vlan
33 filtering, which is provides significant benefits when ports are joined
34 to the same bridge, but without enabling "switch" mode, or to different
35 bridges.
36 - learning disabled on ports as it make not too much sense for
37 segregated ports - no forwarding in HW.
38 - enabled basic support for devlink.
39
40 ::
41
42 devlink dev show
43 platform/48484000.switch
44
45 devlink dev param show
46 platform/48484000.switch:
47 name switch_mode type driver-specific
48 values:
49 cmode runtime value false
50 name ale_bypass type driver-specific
51 values:
52 cmode runtime value false
53
54 Devlink configuration parameters
55 ================================
56
57 See Documentation/networking/devlink/ti-cpsw-switch.rst
58
59 Bridging in dual mac mode
60 =========================
61
62 The dual_mac mode requires two vids to be reserved for internal purposes,
63 which, by default, equal CPSW Port numbers. As result, bridge has to be
64 configured in vlan unaware mode or default_pvid has to be adjusted::
65
66 ip link add name br0 type bridge
67 ip link set dev br0 type bridge vlan_filtering 0
68 echo 0 > /sys/class/net/br0/bridge/default_pvid
69 ip link set dev sw0p1 master br0
70 ip link set dev sw0p2 master br0
71
72 or::
73
74 ip link add name br0 type bridge
75 ip link set dev br0 type bridge vlan_filtering 0
76 echo 100 > /sys/class/net/br0/bridge/default_pvid
77 ip link set dev br0 type bridge vlan_filtering 1
78 ip link set dev sw0p1 master br0
79 ip link set dev sw0p2 master br0
80
81 Enabling "switch"
82 =================
83
84 The Switch mode can be enabled by configuring devlink driver parameter
85 "switch_mode" to 1/true::
86
87 devlink dev param set platform/48484000.switch \
88 name switch_mode value 1 cmode runtime
89
90 This can be done regardless of the state of Port's netdev devices - UP/DOWN, but
91 Port's netdev devices have to be in UP before joining to the bridge to avoid
92 overwriting of bridge configuration as CPSW switch driver copletly reloads its
93 configuration when first Port changes its state to UP.
94
95 When both interfaces have joined the bridge - CPSW switch driver will enable
96 marking packets with offload_fwd_mark flag unless "ale_bypass=0"
97
98 All configuration is implemented via switchdev API.
99
100 Bridge setup
101 ============
102
103 ::
104
105 devlink dev param set platform/48484000.switch \
106 name switch_mode value 1 cmode runtime
107
108 ip link add name br0 type bridge
109 ip link set dev br0 type bridge ageing_time 1000
110 ip link set dev sw0p1 up
111 ip link set dev sw0p2 up
112 ip link set dev sw0p1 master br0
113 ip link set dev sw0p2 master br0
114
115 [*] bridge vlan add dev br0 vid 1 pvid untagged self
116
117 [*] if vlan_filtering=1. where default_pvid=1
118
119 Note. Steps [*] are mandatory.
120
121
122 On/off STP
123 ==========
124
125 ::
126
127 ip link set dev BRDEV type bridge stp_state 1/0
128
129 VLAN configuration
130 ==================
131
132 ::
133
134 bridge vlan add dev br0 vid 1 pvid untagged self <---- add cpu port to VLAN 1
135
136 Note. This step is mandatory for bridge/default_pvid.
137
138 Add extra VLANs
139 ===============
140
141 1. untagged::
142
143 bridge vlan add dev sw0p1 vid 100 pvid untagged master
144 bridge vlan add dev sw0p2 vid 100 pvid untagged master
145 bridge vlan add dev br0 vid 100 pvid untagged self <---- Add cpu port to VLAN100
146
147 2. tagged::
148
149 bridge vlan add dev sw0p1 vid 100 master
150 bridge vlan add dev sw0p2 vid 100 master
151 bridge vlan add dev br0 vid 100 pvid tagged self <---- Add cpu port to VLAN100
152
153 FDBs
154 ----
155
156 FDBs are automatically added on the appropriate switch port upon detection
157
158 Manually adding FDBs::
159
160 bridge fdb add aa:bb:cc:dd:ee:ff dev sw0p1 master vlan 100
161 bridge fdb add aa:bb:cc:dd:ee:fe dev sw0p2 master <---- Add on all VLANs
162
163 MDBs
164 ----
165
166 MDBs are automatically added on the appropriate switch port upon detection
167
168 Manually adding MDBs::
169
170 bridge mdb add dev br0 port sw0p1 grp 239.1.1.1 permanent vid 100
171 bridge mdb add dev br0 port sw0p1 grp 239.1.1.1 permanent <---- Add on all VLANs
172
173 Multicast flooding
174 ==================
175 CPU port mcast_flooding is always on
176
177 Turning flooding on/off on switch ports:
178 bridge link set dev sw0p1 mcast_flood on/off
179
180 Access and Trunk port
181 =====================
182
183 ::
184
185 bridge vlan add dev sw0p1 vid 100 pvid untagged master
186 bridge vlan add dev sw0p2 vid 100 master
187
188
189 bridge vlan add dev br0 vid 100 self
190 ip link add link br0 name br0.100 type vlan id 100
191
192 Note. Setting PVID on Bridge device itself working only for
193 default VLAN (default_pvid).
194
195 NFS
196 ===
197
198 The only way for NFS to work is by chrooting to a minimal environment when
199 switch configuration that will affect connectivity is needed.
200 Assuming you are booting NFS with eth1 interface(the script is hacky and
201 it's just there to prove NFS is doable).
202
203 setup.sh::
204
205 #!/bin/sh
206 mkdir proc
207 mount -t proc none /proc
208 ifconfig br0 > /dev/null
209 if [ $? -ne 0 ]; then
210 echo "Setting up bridge"
211 ip link add name br0 type bridge
212 ip link set dev br0 type bridge ageing_time 1000
213 ip link set dev br0 type bridge vlan_filtering 1
214
215 ip link set eth1 down
216 ip link set eth1 name sw0p1
217 ip link set dev sw0p1 up
218 ip link set dev sw0p2 up
219 ip link set dev sw0p2 master br0
220 ip link set dev sw0p1 master br0
221 bridge vlan add dev br0 vid 1 pvid untagged self
222 ifconfig sw0p1 0.0.0.0
223 udhchc -i br0
224 fi
225 umount /proc
226
227 run_nfs.sh:::
228
229 #!/bin/sh
230 mkdir /tmp/root/bin -p
231 mkdir /tmp/root/lib -p
232
233 cp -r /lib/ /tmp/root/
234 cp -r /bin/ /tmp/root/
235 cp /sbin/ip /tmp/root/bin
236 cp /sbin/bridge /tmp/root/bin
237 cp /sbin/ifconfig /tmp/root/bin
238 cp /sbin/udhcpc /tmp/root/bin
239 cp /path/to/setup.sh /tmp/root/bin
240 chroot /tmp/root/ busybox sh /bin/setup.sh
241
242 run ./run_nfs.sh
243

3. 한국어 전문 번역

영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.

dual-MAC bridge 구성과 switch 모드

59-99

Dual-MAC 모드의 bridging

dual-MAC 모드는 내부 용도로 두 VID를 예약해야 하며 기본적으로 CPSW port 번호와 같습니다. 따라서 bridge를 VLAN-unaware 모드로 구성하거나 `default_pvid`를 조정해야 합니다.

VLAN-unaware bridge 예

	ip link add name br0 type bridge
	ip link set dev br0 type bridge vlan_filtering 0
	echo 0 > /sys/class/net/br0/bridge/default_pvid
	ip link set dev sw0p1 master br0
	ip link set dev sw0p2 master br0

VLAN filtering을 끄고 `default_pvid`를 0으로 설정한 뒤 두 포트를 bridge에 가입시킵니다.

별도의 기본 PVID 100을 사용하는 예

	ip link add name br0 type bridge
	ip link set dev br0 type bridge vlan_filtering 0
	echo 100 > /sys/class/net/br0/bridge/default_pvid
	ip link set dev br0 type bridge vlan_filtering 1
	ip link set dev sw0p1 master br0
	ip link set dev sw0p2 master br0

처음에는 VLAN filtering을 끈 상태에서 `default_pvid`를 100으로 바꾸고, 다시 filtering을 켠 뒤 포트를 가입시킵니다.

switch 활성화

devlink 드라이버 매개변수 `switch_mode`를 1 또는 true로 설정해 switch 모드를 활성화합니다.

devlink dev param set platform/48484000.switch \
name switch_mode value 1 cmode runtime

이 설정은 포트 netdev가 UP인지 DOWN인지와 무관하게 적용할 수 있지만, bridge에 가입시키기 전에 포트를 UP으로 만들어야 합니다. 첫 포트가 UP으로 바뀔 때 CPSW switch 드라이버가 구성을 완전히 다시 로드하기 때문에 순서가 다르면 bridge 구성이 덮어써질 수 있습니다.

두 인터페이스가 bridge에 가입하면 드라이버는 `ale_bypass=0`인 경우를 제외하고 패킷에 `offload_fwd_mark`를 표시합니다. 모든 구성은 switchdev API로 구현됩니다.

CPSW dual-MAC에서 switch 모드로
dual-MAC: 독립 netdev 2개bridge PVID 전략 선택`switch_mode=1`포트를 UPbridge 가입switchdev offload

내부 VID 제약을 처리한 뒤 hardware forwarding을 활성화합니다.

Bridging in dual mac mode
=========================

The dual_mac mode requires two vids to be reserved for internal purposes,
which, by default, equal CPSW Port numbers. As result, bridge has to be
configured in vlan unaware mode or default_pvid has to be adjusted::

        ip link add name br0 type bridge
        ip link set dev br0 type bridge vlan_filtering 0
        echo 0 > /sys/class/net/br0/bridge/default_pvid
        ip link set dev sw0p1 master br0
        ip link set dev sw0p2 master br0

or::

        ip link add name br0 type bridge
        ip link set dev br0 type bridge vlan_filtering 0
        echo 100 > /sys/class/net/br0/bridge/default_pvid
        ip link set dev br0 type bridge vlan_filtering 1
        ip link set dev sw0p1 master br0
        ip link set dev sw0p2 master br0

Enabling "switch"
=================

The Switch mode can be enabled by configuring devlink driver parameter
"switch_mode" to 1/true::

        devlink dev param set platform/48484000.switch \
        name switch_mode value 1 cmode runtime

This can be done regardless of the state of Port's netdev devices - UP/DOWN, but
Port's netdev devices have to be in UP before joining to the bridge to avoid
overwriting of bridge configuration as CPSW switch driver copletly reloads its
configuration when first Port changes its state to UP.

When both interfaces have joined the bridge - CPSW switch driver will enable
marking packets with offload_fwd_mark flag unless "ale_bypass=0"

All configuration is implemented via switchdev API.

Bridge, VLAN, FDB/MDB와 access/trunk 구성

100-194

Bridge 설정

	devlink dev param set platform/48484000.switch \
	name switch_mode value 1 cmode runtime

	ip link add name br0 type bridge
	ip link set dev br0 type bridge ageing_time 1000
	ip link set dev sw0p1 up
	ip link set dev sw0p2 up
	ip link set dev sw0p1 master br0
	ip link set dev sw0p2 master br0

	[*] bridge vlan add dev br0 vid 1 pvid untagged self

	[*] if vlan_filtering=1. where default_pvid=1

	Note. Steps [*] are mandatory.

switch 모드를 활성화하고 `br0`를 만든 뒤 ageing time을 1000으로 설정합니다. 두 포트를 UP으로 만들고 bridge에 가입시킵니다. `vlan_filtering=1`과 `default_pvid=1` 조합에서는 CPU 포트를 VLAN 1에 `pvid untagged self`로 추가하는 단계가 필수입니다.

STP 켜기와 끄기

ip link set dev BRDEV type bridge stp_state 1/0

VLAN 구성

CPU 포트를 VLAN 1에 추가하는 다음 단계는 bridge/default_pvid 구성을 위해 필수입니다.

bridge vlan add dev br0 vid 1 pvid untagged self

VLAN 100 untagged 구성

	bridge vlan add dev sw0p1 vid 100 pvid untagged master
	bridge vlan add dev sw0p2 vid 100 pvid untagged master
	bridge vlan add dev br0 vid 100 pvid untagged self <---- Add cpu port to VLAN100

VLAN 100 tagged 구성

	bridge vlan add dev sw0p1 vid 100 master
	bridge vlan add dev sw0p2 vid 100 master
	bridge vlan add dev br0 vid 100 pvid tagged self <---- Add cpu port to VLAN100

FDB

FDB 항목은 감지 시 적절한 switch 포트에 자동으로 추가됩니다. 수동 추가 시 `vlan 100`을 붙이면 해당 VLAN만, 생략하면 모든 VLAN에 적용됩니다.

bridge fdb add aa:bb:cc:dd:ee:ff dev sw0p1 master vlan 100
bridge fdb add aa:bb:cc:dd:ee:fe dev sw0p2 master <---- Add on all VLANs

MDB

MDB 항목도 감지 시 적절한 switch 포트에 자동으로 추가됩니다. `vid 100`을 붙이면 해당 VLAN만, 생략하면 모든 VLAN에 multicast group을 추가합니다.

bridge mdb add dev br0 port sw0p1 grp 239.1.1.1 permanent vid 100
bridge mdb add dev br0 port sw0p1 grp 239.1.1.1 permanent <---- Add on all VLANs

Multicast flooding

CPU 포트의 `mcast_flooding`은 항상 켜져 있습니다. switch 포트에서는 다음 명령으로 flooding을 제어합니다.

bridge link set dev sw0p1 mcast_flood on/off

Access 및 trunk 포트

bridge vlan add dev sw0p1 vid 100 pvid untagged master
bridge vlan add dev sw0p2 vid 100 master


bridge vlan add dev br0 vid 100 self
ip link add link br0 name br0.100 type vlan id 100

`sw0p1`은 VLAN 100의 untagged access 포트, `sw0p2`는 tagged trunk 포트입니다. CPU 포트를 VLAN 100에 가입시키고 `br0.100` VLAN 인터페이스를 만듭니다. Bridge 장치 자체의 PVID 설정은 기본 VLAN인 `default_pvid`에만 적용됩니다.

CPSW bridge 객체별 VLAN 적용
대상플래그역할
switch access 포트`master pvid untagged`해당 VLAN의 untagged ingress/egress
switch trunk 포트`master`tagged VLAN 전달
bridge CPU 포트`self`호스트가 VLAN에 참여
VLAN netdev`ip link add link br0 ... type vlan`호스트의 tagged L3 인터페이스

switch 포트와 CPU 포트에 사용하는 플래그를 구분합니다.

Bridge setup
============

::

        devlink dev param set platform/48484000.switch \
        name switch_mode value 1 cmode runtime

        ip link add name br0 type bridge
        ip link set dev br0 type bridge ageing_time 1000
        ip link set dev sw0p1 up
        ip link set dev sw0p2 up
        ip link set dev sw0p1 master br0
        ip link set dev sw0p2 master br0

        [*] bridge vlan add dev br0 vid 1 pvid untagged self

        [*] if vlan_filtering=1. where default_pvid=1

        Note. Steps [*] are mandatory.


On/off STP
==========

::

        ip link set dev BRDEV type bridge stp_state 1/0

VLAN configuration
==================

::

  bridge vlan add dev br0 vid 1 pvid untagged self <---- add cpu port to VLAN 1

Note. This step is mandatory for bridge/default_pvid.

Add extra VLANs
===============

 1. untagged::

        bridge vlan add dev sw0p1 vid 100 pvid untagged master
        bridge vlan add dev sw0p2 vid 100 pvid untagged master
        bridge vlan add dev br0 vid 100 pvid untagged self <---- Add cpu port to VLAN100

 2. tagged::

        bridge vlan add dev sw0p1 vid 100 master
        bridge vlan add dev sw0p2 vid 100 master
        bridge vlan add dev br0 vid 100 pvid tagged self <---- Add cpu port to VLAN100

FDBs
----

FDBs are automatically added on the appropriate switch port upon detection

Manually adding FDBs::

    bridge fdb add aa:bb:cc:dd:ee:ff dev sw0p1 master vlan 100
    bridge fdb add aa:bb:cc:dd:ee:fe dev sw0p2 master <---- Add on all VLANs

MDBs
----

MDBs are automatically added on the appropriate switch port upon detection

Manually adding MDBs::

  bridge mdb add dev br0 port sw0p1 grp 239.1.1.1 permanent vid 100
  bridge mdb add dev br0 port sw0p1 grp 239.1.1.1 permanent <---- Add on all VLANs

Multicast flooding
==================
CPU port mcast_flooding is always on

Turning flooding on/off on switch ports:
bridge link set dev sw0p1 mcast_flood on/off

Access and Trunk port
=====================

::

 bridge vlan add dev sw0p1 vid 100 pvid untagged master
 bridge vlan add dev sw0p2 vid 100 master


 bridge vlan add dev br0 vid 100 self
 ip link add link br0 name br0.100 type vlan id 100

Note. Setting PVID on Bridge device itself working only for
default VLAN (default_pvid).

NFS 부팅 환경에서 switch 재구성

195-242

NFS

연결성에 영향을 주는 switch 구성이 필요할 때 NFS를 유지하는 유일한 방법은 최소 환경으로 chroot하여 작업하는 것입니다. 다음 예는 `eth1` 인터페이스로 NFS 부팅했다고 가정합니다. 스크립트는 임시 방편이며 NFS에서도 구성이 가능함을 보이기 위한 예입니다.

`setup.sh`

	#!/bin/sh
	mkdir proc
	mount -t proc none /proc
	ifconfig br0  > /dev/null
	if [ $? -ne 0 ]; then
		echo "Setting up bridge"
		ip link add name br0 type bridge
		ip link set dev br0 type bridge ageing_time 1000
		ip link set dev br0 type bridge vlan_filtering 1

		ip link set eth1 down
		ip link set eth1 name sw0p1
		ip link set dev sw0p1 up
		ip link set dev sw0p2 up
		ip link set dev sw0p2 master br0
		ip link set dev sw0p1 master br0
		bridge vlan add dev br0 vid 1 pvid untagged self
		ifconfig sw0p1 0.0.0.0
		udhchc -i br0
	fi
	umount /proc

스크립트는 chroot 안에서 `/proc`을 마운트하고 `br0`가 없으면 bridge와 VLAN filtering을 구성합니다. `eth1`을 내린 뒤 `sw0p1`로 이름을 바꾸고 두 포트를 올려 bridge에 가입시킵니다. CPU 포트를 VLAN 1에 추가하고 기존 포트 주소를 지운 다음 `br0`에서 DHCP client를 실행합니다. 마지막에 `/proc`을 unmount합니다.

`run_nfs.sh`

	#!/bin/sh
	mkdir /tmp/root/bin -p
	mkdir /tmp/root/lib -p

	cp -r /lib/ /tmp/root/
	cp -r /bin/ /tmp/root/
	cp /sbin/ip /tmp/root/bin
	cp /sbin/bridge /tmp/root/bin
	cp /sbin/ifconfig /tmp/root/bin
	cp /sbin/udhcpc /tmp/root/bin
	cp /path/to/setup.sh /tmp/root/bin
	chroot /tmp/root/ busybox sh /bin/setup.sh

실행 스크립트는 `/tmp/root` 아래에 최소 root 환경을 만들고 `/lib`, `/bin`과 필요한 `ip`, `bridge`, `ifconfig`, `udhcpc`, `setup.sh`를 복사합니다. 그다음 `chroot /tmp/root/ busybox sh /bin/setup.sh`로 격리된 환경에서 switch 구성을 수행합니다.

실행

./run_nfs.sh
NFS 연결을 유지하는 CPSW 재구성
NFS로 부팅`/tmp/root` 최소 환경 구성필수 도구와 library 복사chroot에서 bridge/switch 재구성`br0`에서 DHCPNFS 연결 계속

현재 root filesystem의 네트워크 의존성을 피하기 위해 최소 chroot에서 작업합니다.

NFS
===

The only way for NFS to work is by chrooting to a minimal environment when
switch configuration that will affect connectivity is needed.
Assuming you are booting NFS with eth1 interface(the script is hacky and
it's just there to prove NFS is doable).

setup.sh::

        #!/bin/sh
        mkdir proc
        mount -t proc none /proc
        ifconfig br0  > /dev/null
        if [ $? -ne 0 ]; then
                echo "Setting up bridge"
                ip link add name br0 type bridge
                ip link set dev br0 type bridge ageing_time 1000
                ip link set dev br0 type bridge vlan_filtering 1

                ip link set eth1 down
                ip link set eth1 name sw0p1
                ip link set dev sw0p1 up
                ip link set dev sw0p2 up
                ip link set dev sw0p2 master br0
                ip link set dev sw0p1 master br0
                bridge vlan add dev br0 vid 1 pvid untagged self
                ifconfig sw0p1 0.0.0.0
                udhchc -i br0
        fi
        umount /proc

run_nfs.sh:::

        #!/bin/sh
        mkdir /tmp/root/bin -p
        mkdir /tmp/root/lib -p

        cp -r /lib/ /tmp/root/
        cp -r /bin/ /tmp/root/
        cp /sbin/ip /tmp/root/bin
        cp /sbin/bridge /tmp/root/bin
        cp /sbin/ifconfig /tmp/root/bin
        cp /sbin/udhcpc /tmp/root/bin
        cp /path/to/setup.sh /tmp/root/bin
        chroot /tmp/root/ busybox sh /bin/setup.sh

        run ./run_nfs.sh