← Documents Documentation/networking/sfp-phylink.rst 영어 원문 ↗

Linux 6.18.37 · Networking

phylink

PHY, fixed link, SFP와 in-band negotiation을 하나의 link 관리 계층으로 묶는 phylink의 동작 mode와 network driver 전환 절차를 설명하는 원문을 한국어로 옮겼습니다.

Source path Documentation/networking/sfp-phylink.rst
Source version Linux v6.18.37
SPDX GPL-2.0

이 페이지는 위 원문 경로의 전문 번역입니다. callback과 type 이름, Kconfig symbol, Device Tree property, code block은 원문 표기를 유지했습니다.

개요

sfp-phylink.rst:7-15

phylink는 MAC에 직접 연결된 hot-pluggable network module을 지원하는 기구다. module을 hot-plug할 때마다 adapter를 다시 초기화할 필요가 없도록 한다.

현재 phylink는 전통적인 phylib 기반 구성, fixed link 구성, SFP (Small Formfactor Pluggable) module을 지원한다.

동작 mode

sfp-phylink.rst:17-67

phylink에는 firmware 설정에 따라 달라지는 여러 동작 mode가 있다.

1. PHY mode

PHY mode에서는 phylib로 PHY의 현재 link 설정을 읽고 MAC driver에 전달한다. MAC driver는 전달받은 mode를 정확히 설정해야 하며 link에서 별도의 negotiation을 활성화해서는 안 된다.

2. Fixed mode

MAC driver의 관점에서 fixed mode는 PHY mode와 동일하다.

3. In-band mode

in-band mode는 802.3z, SGMII와 비슷한 interface mode에서 사용한다. SerDes channel을 통해 전달되는 in-band negotiation 또는 control word를 사용하고 그 결과를 따라야 한다.

예를 들어 다음 설정은 in-band SGMII signalling을 사용하지 않는다.

&eth {
    phy = <&phy>;
    phy-mode = "sgmii";
};

PHY는 mac_config()에서 전달받은 설정을 정확히 따라야 한다. mac_link_up()mac_link_down()은 상황에 맞춰 link를 강제로 up 또는 down시켜야 한다.

다음 설정은 in-band mode를 사용한다.

&eth {
    managed = "in-band-status";
    phy = <&phy>;
    phy-mode = "sgmii";
};

PHY의 negotiation 결과가 SGMII control word를 통해 MAC으로 전달되고 MAC은 이 control word에 응답해야 한다. 이 mode에서는 mac_link_up()mac_link_down()이 MAC 쪽 link를 강제로 up 또는 down시키면 안 된다.

network driver를 SFP/phylink로 전환하는 절차

sfp-phylink.rst:69-405

다음 절차는 network driver를 phylib에서 SFP/phylink 지원 방식으로 전환하는 방법을 간략히 설명한다. 이 문서를 개선하는 patch를 보내도 된다.

1. link-down과 link-up 처리 분리

sfp-phylink.rst:76-83

선택 사항으로 network driver의 phylib update 함수를 link-down 처리와 link-up 처리의 두 부분으로 나눈다. 별도의 준비 commit으로 수행할 수 있다.

이 준비 작업의 예전 사례는 git commit fc548b991fb0에서 볼 수 있다. 해당 commit은 세 부분으로 나눴지만, 현재는 link-up 부분에 link 설정에 맞춘 MAC 구성까지 포함한다. 자세한 내용은 mac_link_up() 설명을 참고한다.

2. Kconfig dependency 교체

sfp-phylink.rst:85-94

driver의 Kconfig 항목에서 다음을

select FIXED_PHY
select PHYLIB

다음으로 바꾼다.

select PHYLINK

3. phylink header 추가

sfp-phylink.rst:96-100

driver의 header file 목록에 다음 include를 추가한다.

#include <linux/phylink.h>

4. driver private data에 phylink 상태 추가

sfp-phylink.rst:102-109

driver private data structure에 다음 member를 추가한다.

struct phylink *phylink;
struct phylink_config phylink_config;

이후 설명에서는 driver의 private data pointer를 priv, private data structure를 struct foo_priv라고 부른다.

5. 기존 phylib 함수 교체

sfp-phylink.rst:111-135
기존 함수 교체할 함수
phy_start(phydev) phylink_start(priv->phylink)
phy_stop(phydev) phylink_stop(priv->phylink)
phy_mii_ioctl(phydev, ifr, cmd) phylink_mii_ioctl(priv->phylink, ifr, cmd)
phy_ethtool_get_wol(phydev, wol) phylink_ethtool_get_wol(priv->phylink, wol)
phy_ethtool_set_wol(phydev, wol) phylink_ethtool_set_wol(priv->phylink, wol)
phy_disconnect(phydev) phylink_disconnect_phy(priv->phylink)

이 함수 중 일부는 rtnl lock을 잡은 상태에서 호출해야 하며, 그렇지 않으면 warning을 출력한다. 보통은 이 조건이 충족되지만 driver의 suspend/resume path에서 호출하는 경우에는 그렇지 않을 수 있다.

6. ethtool ksettings get/set method 교체

sfp-phylink.rst:137-155

ksettings get/set method를 다음과 같이 추가하거나 교체한다.

static int foo_ethtool_set_link_ksettings(
        struct net_device *dev,
        const struct ethtool_link_ksettings *cmd)
{
        struct foo_priv *priv = netdev_priv(dev);

        return phylink_ethtool_ksettings_set(priv->phylink, cmd);
}

static int foo_ethtool_get_link_ksettings(
        struct net_device *dev,
        struct ethtool_link_ksettings *cmd)
{
        struct foo_priv *priv = netdev_priv(dev);

        return phylink_ethtool_ksettings_get(priv->phylink, cmd);
}

7. PHY 연결 경로 교체

sfp-phylink.rst:157-188

다음 호출과 그에 연관된 코드를

phy_dev = of_phy_connect(dev, node, link_func, flags, phy_interface);

다음 호출로 바꾼다.

err = phylink_of_phy_connect(priv->phylink, node, flags);

대부분 flags는 0으로 둘 수 있다. DT node node에 PHY가 지정되어 있으면 이 flag는 함수 내부의 phy_attach_direct()로 전달된다.

node는 network PHY property와 fixed link property를 가진 DT node여야 하며 SFP property도 같은 node에 들어간다.

fixed link 설정 코드는 제거해야 한다. 이 부분은 phylink가 내부에서 처리한다. of_phy_connect()에 넘기던 link update 함수 pointer도 더 이상 사용하지 않고 8단계에서 설명하는 다른 형태의 MAC update로 교체한다.

PHY의 supported/advertised mode 조정은 8단계의 validate callback을 기준으로 phylink 내부에서 수행한다. driver가 phy_interface를 따로 저장할 필요도 없다. phy_interface는 speed나 duplex와 마찬가지로 동적으로 바뀌는 property가 된다.

마지막으로 MAC driver는 PHY에 직접 접근하지 않게 된다. phylink model에서는 PHY가 동적으로 바뀔 수 있기 때문이다.

8. phylink_mac_ops 구현

sfp-phylink.rst:190-232

함수 pointer table인 struct phylink_mac_ops instance를 driver에 추가하고 그 함수들을 구현한다. of_phy_connect()에서 쓰던 예전 link update 함수는 mac_link_up(), mac_link_down(), mac_config()의 세 method로 나뉜다. 1단계를 수행했다면 기능이 이미 분리되어 있을 것이다.

in-band negotiation을 사용할 때 mac_link_up()mac_link_down()이 negotiation 완료를 방해하지 않는 것이 중요하다. 이 함수는 in-band link state가 바뀔 때 호출된다. 여기에서 negotiation을 막으면 link는 절대로 올라오지 않는다.

mac_get_caps()는 선택 사항이다. 구현했다면 전달받은 interface mode에서 MAC이 지원하는 phylink MAC capability를 반환해야 한다. 일반적으로 이 method를 구현할 필요는 없다. phylink는 이 capability와 interface에서 허용되는 capability를 조합해 사용 가능한 ethtool link mode를 결정한다.

mac_link_state()는 MAC에서 link state를 읽어 현재 MAC이 사용하는 설정을 보고한다. 1000base-X나 SGMII처럼 in-band negotiation을 사용하는 mode에서 특히 중요하다.

mac_link_up()은 link가 올라왔음을 MAC에 알린다. 호출에 포함되는 negotiation mode와 interface는 참고용이다. 최종 결정된 speed, duplex, flow-control/pause enable 설정도 전달된다. MAC과 PCS가 단단히 결합되어 있지 않거나 in-band negotiation에서 설정을 얻지 않는 경우에는 이 값을 사용해 MAC을 구성해야 한다.

mac_config()는 요청된 state로 MAC을 갱신한다. MAC 설정을 바꿀 때 불필요하게 link를 내리지 않도록 해야 한다. state를 수정하되 MAC configuration을 바꾸기 위해 정말 필요한 경우에만 link를 내려야 한다. 구현 예는 drivers/net/ethernet/marvell/mvneta.cmvneta_mac_config()에서 볼 수 있다.

이 method의 자세한 내용은 struct phylink_mac_ops의 inline documentation을 참고한다.

9. phylink_config와 MAC capability 채우기

sfp-phylink.rst:234-247

struct net_device에 연결된 struct device를 참조하도록 struct phylink_config field를 채운다.

priv->phylink_config.dev = &dev.dev;
priv->phylink_config.type = PHYLINK_NETDEV;

MAC이 처리할 수 있는 speed, pause, duplex mode를 채운다.

priv->phylink_config.mac_capabilities =
        MAC_SYM_PAUSE | MAC_10 | MAC_100 | MAC_1000FD;

10. 내부 PCS 구성

sfp-phylink.rst:249-294

일부 Ethernet controller는 PCS(Physical Coding Sublayer) block과 함께 동작한다. PCS는 encoding/decoding, link establishment 감지, auto-negotiation 등을 처리할 수 있다. 내부 PCS 동작이 투명한 MAC도 있지만, link가 동작하려면 전용 PCS 설정이 필요한 MAC도 있다. 이 경우 phylink는 struct phylink_pcs를 통해 PCS abstraction을 제공한다.

driver가 내부 PCS block을 하나 이상 갖는지, 또는 controller 내부에 연결된 외부 PCS block을 사용할 수 있는지 확인한다. 내부 PCS가 없다면 11단계로 간다.

Ethernet controller에 PCS block이 하나 이상 있다면 driver private data structure에 PCS block마다 struct phylink_pcs instance를 하나씩 만든다.

struct phylink_pcs pcs;

PCS를 구성할 struct phylink_pcs_ops의 관련 callback을 채운다. in-band link state를 보고하는 pcs_get_state(), phylink가 전달한 parameter에 맞춰 PCS를 구성하는 pcs_config(), PCS가 받아들일 수 있는 모든 설정 parameter를 phylink에 보고하는 pcs_validate()를 구현한다.

struct phylink_pcs_ops foo_pcs_ops = {
        .pcs_validate = foo_pcs_validate,
        .pcs_get_state = foo_pcs_get_state,
        .pcs_config = foo_pcs_config,
};

PCS link state interrupt는 다음 호출로 phylink에 전달되게 한다.

phylink_pcs_change(pcs, link_is_up);

현재 link가 up이면 link_is_up은 true, 그렇지 않으면 false다. PCS가 이 interrupt를 제공할 수 없다면 PCS 생성 시 pcs->pcs_poll = true;로 설정해야 한다.

11. 외부 PCS 연결

sfp-phylink.rst:296-318

controller가 별도 driver로 제어되는 외부 PCS에 의존하거나 외부 PCS를 사용할 수 있다면, driver private data structure에 struct phylink_pcs pointer를 추가한다.

struct phylink_pcs *pcs;

실제 PCS instance를 얻는 방법은 platform에 따라 다르다. 어떤 PCS는 MDIO bus에 있고, 해당 struct mii_bus pointer와 bus 상의 PCS address를 넘겨 가져온다. 다음 예는 controller가 Lynx PCS instance에 연결된다고 가정한다.

priv->pcs = lynx_pcs_create_mdiodev(bus, 0);

firmware 정보로 가져올 수 있는 PCS도 있다.

priv->pcs = lynx_pcs_create_fwnode(of_fwnode_handle(node));

12. mac_select_pcs callback 구현

sfp-phylink.rst:320-340

mac_select_pcs() callback을 구현해 struct phylink_mac_ops에 추가한다. 이 함수는 요청된 link 설정에 사용할 struct phylink_pcs pointer를 반환해야 한다.

static struct phylink_pcs *foo_select_pcs(
        struct phylink_config *config,
        phy_interface_t interface)
{
        struct foo_priv *priv = container_of(config, struct foo_priv,
                                             phylink_config);

        if ( /* 'interface' 동작에 PCS가 필요함 */ )
                return priv->pcs;

        return NULL;
}

내부 PCS를 여러 개 가진 driver의 예는 mvpp2_select_pcs()를 참고한다.

13. 지원 interface mode 지정

sfp-phylink.rst:342-354

MAC이 출력할 수 있는 모든 phy_interface_t, 즉 MAC-PHY link mode를 채운다. 다음 예는 모든 RGMII mode, SGMII, 1000BaseX를 처리할 수 있는 MAC 구성이다. 사용하려는 interface 하나만 적는 것이 아니라, MAC과 그에 연결된 모든 PCS의 전체 capability에 맞춰 조정해야 한다.

phy_interface_set_rgmii(
        priv->phylink_config.supported_interfaces);
__set_bit(PHY_INTERFACE_MODE_SGMII,
          priv->phylink_config.supported_interfaces);
__set_bit(PHY_INTERFACE_MODE_1000BASEX,
          priv->phylink_config.supported_interfaces);

14. probe에서 phylink 생성과 제거 구성

sfp-phylink.rst:356-377

probe 함수에서 PHY를 찾는 of_parse_phandle(), fixed link를 등록하는 of_phy_register_fixed_link() 등의 호출을 제거하고 다음으로 바꾼다.

struct phylink *phylink;

phylink = phylink_create(&priv->phylink_config, node,
                         phy_mode, &phylink_ops);
if (IS_ERR(phylink)) {
        err = PTR_ERR(phylink);
        fail probe;
}

priv->phylink = phylink;

상황에 맞춰 probe failure path와 remove path에서 phylink를 파괴하도록 구성한다.

phylink_destroy(priv->phylink);

15. MAC link state interrupt 전달

sfp-phylink.rst:379-387

MAC link state interrupt는 다음 호출로 phylink에 전달되게 한다.

phylink_mac_change(priv->phylink, link_is_up);

현재 link가 up이면 link_is_up은 true, 그렇지 않으면 false다.

16. netif_carrier 직접 제어 제거

sfp-phylink.rst:389-405

driver가 다음 함수를 호출하지 않는지 확인한다.

netif_carrier_on()
netif_carrier_off()

이 함수들은 phylink의 link state 추적을 방해하고, phylink가 mac_link_up()mac_link_down() method 호출을 생략하게 만들 수 있다.

network driver는 suspend/resume path에서 phylink_stop()phylink_start()을 호출해야 한다. 그래야 필요에 따라 적절한 struct phylink_mac_ops method가 호출된다.

Device Tree에서 SFP cage를 기술하는 방법은 kernel source tree의 Documentation/devicetree/bindings/net/sff,sfp.yaml binding 문서를 참고한다.