最近在排查一個(gè)網(wǎng)絡(luò)問題,ifconfig eth0 up 后,網(wǎng)卡link up比較慢。因此,分析了下從ifconfig up 到網(wǎng)絡(luò)驅(qū)動的調(diào)用流程。這里順便作個(gè)記錄。
ifconfig eth0 up 調(diào)用的是busybox 的命令,因此從busybox 源碼入手,逐步分析下調(diào)用流程。代碼介紹文件位于:networking/ifenslave.c
ifconfig eth0 up 和 ifconfig eth0 down 分別對應(yīng)busybox 的set_if_up()和set_if_down().
static?int?set_if_down(char?*ifname,?int?flags)
{
?int?res?=?set_if_flags(ifname,?flags?&?~IFF_UP);
?if?(res)
??bb_perror_msg("%s:?can't?down",?ifname);
?return?res;
}
static?int?set_if_up(char?*ifname,?int?flags)
{
?int?res?=?set_if_flags(ifname,?flags?|?IFF_UP);
?if?(res)
??bb_perror_msg("%s:?can't?up",?ifname);
?return?res;
}
比如,當(dāng)我們敲ifconfig eth0 down時(shí),實(shí)則就是調(diào)用:
set_if_down("eth0",?master_flags.ifr_flags);
set_if_flags()會將網(wǎng)卡名,up / down 標(biāo)志位flags通過ioctl命令SIOCSIFFLAGS 傳遞給內(nèi)核網(wǎng)卡驅(qū)動。
static?int?set_if_flags(char?*ifname,?int?flags)
{
?struct?ifreq?ifr;
?ifr.ifr_flags?=?flags;
?return?set_ifrname_and_do_ioctl(SIOCSIFFLAGS,?&ifr,?ifname);
}
接著深入到內(nèi)核代碼中,看下SIOCSIFFLAGS命令在哪里實(shí)現(xiàn)。代碼位于kernel\net\core\dev_ioctl.c。
static?int?dev_ifsioc(struct?net?*net,?struct?ifreq?*ifr,?unsigned?int?cmd)
{
?int?err;
?struct?net_device?*dev?=?__dev_get_by_name(net,?ifr->ifr_name);
?const?struct?net_device_ops?*ops;
?if?(!dev)
??return?-ENODEV;
?ops?=?dev->netdev_ops;
?switch?(cmd)?{
?case?SIOCSIFFLAGS:?/*?Set?interface?flags?*/
??return?dev_change_flags(dev,?ifr->ifr_flags);
?case?SIOCSIFMETRIC:?/*?Set?the?metric?on?the?interface
???????(currently?unused)?*/
??return?-EOPNOTSUPP;
...................
?}
?return?err;
}
dev_ifsioc()會調(diào)用__dev_get_by_name()根據(jù) 網(wǎng)卡名遍歷 net鏈表,如果匹配到則返回net_device結(jié)構(gòu)體指針。接著,SIOCSIFFLAGS會調(diào)用到dev_change_flags(),最后調(diào)用到__dev_change_flags()。
int?dev_change_flags(struct?net_device?*dev,?unsigned?int?flags)
{
?int?ret;
?unsigned?int?changes,?old_flags?=?dev->flags,?old_gflags?=?dev->gflags;
?ret?=?__dev_change_flags(dev,?flags);
?if?(ret?0)
??return?ret;
?changes?=?(old_flags?^?dev->flags)?|?(old_gflags?^?dev->gflags);
?__dev_notify_flags(dev,?old_flags,?changes);
?return?ret;
}
int?__dev_change_flags(struct?net_device?*dev,?unsigned?int?flags)
{
?unsigned?int?old_flags?=?dev->flags;
?int?ret;
?ASSERT_RTNL();
?/*
??*?Set?the?flags?on?our?device.
??*/
?dev->flags?=?(flags?&?(IFF_DEBUG?|?IFF_NOTRAILERS?|?IFF_NOARP?|
??????????IFF_DYNAMIC?|?IFF_MULTICAST?|?IFF_PORTSEL?|
??????????IFF_AUTOMEDIA))?|
???????(dev->flags?&?(IFF_UP?|?IFF_VOLATILE?|?IFF_PROMISC?|
????????IFF_ALLMULTI));
?/*
??*?Load?in?the?correct?multicast?list?now?the?flags?have?changed.
??*/
?if?((old_flags?^?flags)?&?IFF_MULTICAST)
??dev_change_rx_flags(dev,?IFF_MULTICAST);
?dev_set_rx_mode(dev);
?/*
??*?Have?we?downed?the?interface.?We?handle?IFF_UP?ourselves
??*?according?to?user?attempts?to?set?it,?rather?than?blindly
??*?setting?it.
??*/
?ret?=?0;
????/*?兩個(gè)標(biāo)識有一個(gè)是IFF_UP?*/
?if?((old_flags?^?flags)?&?IFF_UP)
??ret?=?((old_flags?&?IFF_UP)???__dev_close?:?__dev_open)(dev);?//?通過flags?判斷調(diào)用__dev_close?還是?__dev_open
?if?((flags?^?dev->gflags)?&?IFF_PROMISC)?{
??int?inc?=?(flags?&?IFF_PROMISC)???1?:?-1;
??unsigned?int?old_flags?=?dev->flags;
??dev->gflags?^=?IFF_PROMISC;
??if?(__dev_set_promiscuity(dev,?inc,?false)?>=?0)
???if?(dev->flags?!=?old_flags)
????dev_set_rx_mode(dev);
?}
?/*?NOTE:?order?of?synchronization?of?IFF_PROMISC?and?IFF_ALLMULTI
????is?important.?Some?(broken)?drivers?set?IFF_PROMISC,?when
????IFF_ALLMULTI?is?requested?not?asking?us?and?not?reporting.
??*/
?if?((flags?^?dev->gflags)?&?IFF_ALLMULTI)?{
??int?inc?=?(flags?&?IFF_ALLMULTI)???1?:?-1;
??dev->gflags?^=?IFF_ALLMULTI;
??__dev_set_allmulti(dev,?inc,?false);
?}
?return?ret;
}
在__dev_change_flags(dev, flags)函數(shù)中,通過判斷flag的IFF_UP位上的值是否相反,來實(shí)現(xiàn)是調(diào)用__dev_close()還是__dev_open()來開關(guān)eth0。
__dev_close中會將當(dāng)前的net_device加入到等待設(shè)備關(guān)閉列表中。
static?int?__dev_close(struct?net_device?*dev)
{
?int?retval;
?LIST_HEAD(single);
?list_add(&dev->close_list,?&single);
?retval?=?__dev_close_many(&single);
?list_del(&single);
?return?retval;
}
__dev_close_many通知設(shè)備正在關(guān)閉,等待未發(fā)送完的數(shù)據(jù)發(fā)送完,最后清除開啟標(biāo)記。
static?int?__dev_close_many(struct?list_head?*head)
{
?struct?net_device?*dev;
?ASSERT_RTNL();
?might_sleep();
?list_for_each_entry(dev,?head,?close_list)?{
??/*?Temporarily?disable?netpoll?until?the?interface?is?down?*/
??????????/*?禁用netpoll?*/
??netpoll_poll_disable(dev);
??/*?通知設(shè)備正在關(guān)閉?*/
??call_netdevice_notifiers(NETDEV_GOING_DOWN,?dev);
???/*?清除start標(biāo)志位?*/
??clear_bit(__LINK_STATE_START,?&dev->state);
??/*?Synchronize?to?scheduled?poll.?We?cannot?touch?poll?list,?it
???*?can?be?even?on?different?cpu.?So?just?clear?netif_running().
???*
???*?dev->stop()?will?invoke?napi_disable()?on?all?of?it's
???*?napi_struct?instances?on?this?device.
???*/
??smp_mb__after_atomic();?/*?Commit?netif_running().?*/
?}
??/*?未發(fā)送完的數(shù)據(jù)發(fā)送完?*/
?dev_deactivate_many(head);
?list_for_each_entry(dev,?head,?close_list)?{
??const?struct?net_device_ops?*ops?=?dev->netdev_ops;
??/*
???*?Call?the?device?specific?close.?This?cannot?fail.
???*?Only?if?device?is?UP
???*
???*?We?allow?it?to?be?called?even?after?a?DETACH?hot-plug
???*?event.
???*/
?????????/*?調(diào)用設(shè)備關(guān)閉操作?*/
??if?(ops->ndo_stop)
???ops->ndo_stop(dev);
??/*?標(biāo)記設(shè)備關(guān)閉?*/
??dev->flags?&=?~IFF_UP;
????????/*?啟用netpoll?*/
??netpoll_poll_enable(dev);
?}
?return?0;
}
ndo_stop為關(guān)閉網(wǎng)卡時(shí),不同網(wǎng)卡驅(qū)動注冊的不同的關(guān)閉函數(shù),我們以海思的網(wǎng)卡驅(qū)動為例,分析下ndo_stop函數(shù)的實(shí)現(xiàn)。代碼位于kernel\drivers\net\ethernet\hisilicon\hns\hns_enet.c。
static?int?hns_nic_net_stop(struct?net_device?*ndev)
{
?hns_nic_net_down(ndev);
?return?0;
}
static?void?hns_nic_net_down(struct?net_device?*ndev)
{
?int?i;
?struct?hnae_ae_ops?*ops;
?struct?hns_nic_priv?*priv?=?netdev_priv(ndev);
?
?if?(test_and_set_bit(NIC_STATE_DOWN,?&priv->state))
??return;
?(void)del_timer_sync(&priv->service_timer);
?netif_tx_stop_all_queues(ndev);
?netif_carrier_off(ndev);
?netif_tx_disable(ndev);
?priv->link?=?0;
?if?(priv->phy)
??phy_stop(priv->phy);
?ops?=?priv->ae_handle->dev->ops;
?if?(ops->stop)
??ops->stop(priv->ae_handle);
?netif_tx_stop_all_queues(ndev);
?for?(i?=?priv->ae_handle->q_num?-?1;?i?>=?0;?i--)?{
??hns_nic_ring_close(ndev,?i);
??hns_nic_ring_close(ndev,?i?+?priv->ae_handle->q_num);
??/*?clean?tx?buffers*/
??hns_nic_tx_clr_all_bufs(priv->ring_data?+?i);
?}
}
hns_nic_net_down()中會調(diào)用netif_carrier_off()通知內(nèi)核子系統(tǒng)網(wǎng)絡(luò)斷開。下面我們詳細(xì)分析下netif_carrier_off()的實(shí)現(xiàn)。
void?netif_carrier_off(struct?net_device?*dev)
{
?????/*?設(shè)置網(wǎng)卡為載波斷開狀態(tài)?即nocarrier狀態(tài),上行時(shí)軟中斷下半部讀到該狀態(tài)不會進(jìn)行網(wǎng)卡收包?*/
?if?(!test_and_set_bit(__LINK_STATE_NOCARRIER,?&dev->state))?{
??if?(dev->reg_state?==?NETREG_UNINITIALIZED)
???return;
????????/*?增加設(shè)備改變狀態(tài)?*/
??atomic_inc(&dev->carrier_changes);
????????/*?加入事件處理隊(duì)列進(jìn)行處理?*/
??linkwatch_fire_event(dev);
?}
}
linkwatch_fire_event()函數(shù)將設(shè)備加入到事件隊(duì)列,并且進(jìn)行事件調(diào)度,調(diào)度中會根據(jù)是否為緊急事件做不同處理。
void?linkwatch_fire_event(struct?net_device?*dev)
{
????/*?判斷是否是緊急處理的事件?*/
?bool?urgent?=?linkwatch_urgent_event(dev);
?/*?判斷是否是緊急處理的事件?*/
?if?(!test_and_set_bit(__LINK_STATE_LINKWATCH_PENDING,?&dev->state))?{
?????????/*?添加事件到事件列表?*/
??linkwatch_add_event(dev);
?}?else?if?(!urgent)
?????????/*?設(shè)備以前已經(jīng)設(shè)置了pending標(biāo)記,不是緊急事件,直接返回?*/
??return;
?/*?事件調(diào)度?*/
?linkwatch_schedule_work(urgent);
}
linkwatch_urgent_event()判斷是否是否需要緊急處理。
static?bool?linkwatch_urgent_event(struct?net_device?*dev)
{
????/*?設(shè)備未運(yùn)行,非緊急?*/
?if?(!netif_running(dev))
??return?false;
??/*?設(shè)備的索引號與連接索引號不等,緊急?*/
?if?(dev->ifindex?!=?dev_get_iflink(dev))
??return?true;
?/*?設(shè)備作為team?port,緊急?*/
?if?(dev->priv_flags?&?IFF_TEAM_PORT)
??return?true;
?/*?連接與否?&&?發(fā)送隊(duì)列排隊(duì)規(guī)則改變與否?*/
?return?netif_carrier_ok(dev)?&&?qdisc_tx_changing(dev);
}
linkwatch_add_event()將設(shè)備加入到事件處理鏈表。
static?void?linkwatch_add_event(struct?net_device?*dev)
{
?unsigned?long?flags;
?spin_lock_irqsave(&lweventlist_lock,?flags);
????/*?若未添加,則添加設(shè)備到事件列表?*/
?if?(list_empty(&dev->link_watch_list))?{
??list_add_tail(&dev->link_watch_list,?&lweventlist);
??dev_hold(dev);
?}
?spin_unlock_irqrestore(&lweventlist_lock,?flags);
}
linkwatch_schedule_work()對事件處理進(jìn)行調(diào)度,緊急事件立即執(zhí)行,非緊急事件延后執(zhí)行。
static?void?linkwatch_schedule_work(int?urgent)
{
?unsigned?long?delay?=?linkwatch_nextevent?-?jiffies;
?/*?已經(jīng)設(shè)置了緊急標(biāo)記,則返回?*/
?if?(test_bit(LW_URGENT,?&linkwatch_flags))
??return;
?/*?需要緊急調(diào)度?*/
?if?(urgent)?{
????????/*?之前設(shè)置了,則返回?*/
??if?(test_and_set_bit(LW_URGENT,?&linkwatch_flags))
???return;
????????/*?未設(shè)置緊急,則立即執(zhí)行?*/
??delay?=?0;
?}
?/*?如果大于1s則立即執(zhí)行?*/
?if?(delay?>?HZ)
??delay?=?0;
?/*?如果設(shè)置了緊急標(biāo)記,則立即執(zhí)行?*/
?if?(test_bit(LW_URGENT,?&linkwatch_flags))
??mod_delayed_work(system_wq,?&linkwatch_work,?0);
?else
????????/*?未設(shè)置緊急標(biāo)記,則按照delay執(zhí)行?*/
??schedule_delayed_work(&linkwatch_work,?delay);
}
__linkwatch_run_queue()完成對事件調(diào)度隊(duì)列中設(shè)備的處理。
static?void?__linkwatch_run_queue(int?urgent_only)
{
?struct?net_device?*dev;
?LIST_HEAD(wrk);
?/*
??*?Limit?the?number?of?linkwatch?events?to?one
??*?per?second?so?that?a?runaway?driver?does?not
??*?cause?a?storm?of?messages?on?the?netlink
??*?socket.??This?limit?does?not?apply?to?up?events
??*?while?the?device?qdisc?is?down.
??*/
????/*?已達(dá)到調(diào)度時(shí)間?*/
?if?(!urgent_only)
??linkwatch_nextevent?=?jiffies?+?HZ;
?/*?Limit?wrap-around?effect?on?delay.?*/
????/*
?????未到達(dá)調(diào)度時(shí)間,并且下一次調(diào)度在當(dāng)前時(shí)間的1s以后?
?????那么設(shè)置調(diào)度時(shí)間是當(dāng)前時(shí)間
?????*/
?else?if?(time_after(linkwatch_nextevent,?jiffies?+?HZ))
??linkwatch_nextevent?=?jiffies;
?/*?清除緊急標(biāo)識?*/
?clear_bit(LW_URGENT,?&linkwatch_flags);
?spin_lock_irq(&lweventlist_lock);
?list_splice_init(&lweventlist,?&wrk);
?/*?遍歷鏈表?*/
?while?(!list_empty(&wrk))?{
??/*?獲取設(shè)備?*/
??dev?=?list_first_entry(&wrk,?struct?net_device,?link_watch_list);
????????/*?從鏈表移除設(shè)備?*/
??list_del_init(&dev->link_watch_list);
??/*?未到達(dá)調(diào)度時(shí)間?&&??不需要緊急處理??*/
??if?(urgent_only?&&?!linkwatch_urgent_event(dev))?{
????????????/*?添加到鏈表尾部?*/
???list_add_tail(&dev->link_watch_list,?&lweventlist);
????????????/*?繼續(xù)處理?*/
???continue;
??}
??spin_unlock_irq(&lweventlist_lock);
????????/*?處理設(shè)備?*/
??linkwatch_do_dev(dev);
??spin_lock_irq(&lweventlist_lock);
?}
?/*?鏈表有未處理事件,則以非緊急狀態(tài)調(diào)度隊(duì)列?*/
?if?(!list_empty(&lweventlist))
??linkwatch_schedule_work(0);
?spin_unlock_irq(&lweventlist_lock);
}
linkwatch_do_dev()完成對某個(gè)設(shè)備的狀態(tài)改變處理。
static?void?linkwatch_do_dev(struct?net_device?*dev)
{
?/*
??*?Make?sure?the?above?read?is?complete?since?it?can?be
??*?rewritten?as?soon?as?we?clear?the?bit?below.
??*/
?smp_mb__before_atomic();
?/*?We?are?about?to?handle?this?device,
??*?so?new?events?can?be?accepted
??*/
????/*?清除pending標(biāo)記?*/
?clear_bit(__LINK_STATE_LINKWATCH_PENDING,?&dev->state);
?rfc2863_policy(dev);
?????/*?如果設(shè)備啟動狀態(tài)?*/
?if?(dev->flags?&?IFF_UP)?{
????????/*?鏈路連接?*/
??if?(netif_carrier_ok(dev))
????????????/*?啟用排隊(duì)規(guī)則?*/
???dev_activate(dev);
??else
????????????/*?關(guān)閉排隊(duì)規(guī)則?*/
???dev_deactivate(dev);
???/*?設(shè)備狀態(tài)改變處理,執(zhí)行netdev_chain上設(shè)備狀態(tài)變更回調(diào)?*/
??netdev_state_change(dev);
?}
?dev_put(dev);
}
最后,hns_nic_net_down()中會調(diào)用phy_stop()將網(wǎng)卡link down。
void?phy_stop(struct?phy_device?*phydev)
{
?mutex_lock(&phydev->lock);
?if?(PHY_HALTED?==?phydev->state)
??goto?out_unlock;
?if?(phy_interrupt_is_valid(phydev))?{
??/*?Disable?PHY?Interrupts?*/
??phy_config_interrupt(phydev,?PHY_INTERRUPT_DISABLED);
??/*?Clear?any?pending?interrupts?*/
??phy_clear_interrupt(phydev);
?}
?phydev->state?=?PHY_HALTED;
out_unlock:
?mutex_unlock(&phydev->lock);
?/*?Cannot?call?flush_scheduled_work()?here?as?desired?because
??*?of?rtnl_lock(),?but?PHY_HALTED?shall?guarantee?phy_change()
??*?will?not?reenable?interrupts.
??*/
}
phy_stop()將phydev->state設(shè)置為PHY_HALTED,將網(wǎng)卡關(guān)閉。
__dev_open為設(shè)備啟用核心函數(shù),該函數(shù)打開eth0,設(shè)置啟用標(biāo)記,并且設(shè)置接收模式,排隊(duì)規(guī)則等。
static?int?__dev_open(struct?net_device?*dev)
{
?const?struct?net_device_ops?*ops?=?dev->netdev_ops;
?int?ret;
?ASSERT_RTNL();
??/*?設(shè)備不可用?*/
?if?(!netif_device_present(dev))
??return?-ENODEV;
?/*?Block?netpoll?from?trying?to?do?any?rx?path?servicing.
??*?If?we?don't?do?this?there?is?a?chance?ndo_poll_controller
??*?or?ndo_poll?may?be?running?while?we?open?the?device
??*/
?????/*?禁用netpoll?*/
?netpoll_poll_disable(dev);
?/*?設(shè)備打開前通知?*/
?ret?=?call_netdevice_notifiers(NETDEV_PRE_UP,?dev);
?ret?=?notifier_to_errno(ret);
?if?(ret)
??return?ret;
??/*?設(shè)置設(shè)備打開標(biāo)記,設(shè)備將設(shè)置IFF_UP標(biāo)志位*/
?set_bit(__LINK_STATE_START,?&dev->state);
?/*?校驗(yàn)地址?*/
?if?(ops->ndo_validate_addr)
??ret?=?ops->ndo_validate_addr(dev);
??/*?執(zhí)行打開?*/
?if?(!ret?&&?ops->ndo_open)
??ret?=?ops->ndo_open(dev);
?/*?啟用netpoll?*/
?netpoll_poll_enable(dev);
?/*?失敗,清除打開標(biāo)記?*/
?if?(ret)
??clear_bit(__LINK_STATE_START,?&dev->state);
????/*?設(shè)備打開操作?*/
?else?{
?????????/*?設(shè)置打開標(biāo)記?*/
??dev->flags?|=?IFF_UP;
?????????/*?設(shè)置接收模式?*/
??dev_set_rx_mode(dev);
?????????/*?初始化排隊(duì)規(guī)則?*/
??dev_activate(dev);
????????/*?加入設(shè)備數(shù)據(jù)到熵池?*/
??add_device_randomness(dev->dev_addr,?dev->addr_len);
?}
?return?ret;
}
我們以海思的網(wǎng)卡驅(qū)動為例,分析下ndo_open()函數(shù)的實(shí)現(xiàn)。代碼位于kernel\drivers\net\ethernet\hisilicon\hns\hns_enet.c。
static?int?hns_nic_net_open(struct?net_device?*ndev)
{
?struct?hns_nic_priv?*priv?=?netdev_priv(ndev);
?struct?hnae_handle?*h?=?priv->ae_handle;
?int?ret;
?if?(test_bit(NIC_STATE_TESTING,?&priv->state))
??return?-EBUSY;
?priv->link?=?0;
?netif_carrier_off(ndev);
?/*設(shè)置tx?queue的個(gè)數(shù)*/
?ret?=?netif_set_real_num_tx_queues(ndev,?h->q_num);
?if?(ret?0)?{
??netdev_err(ndev,?"netif_set_real_num_tx_queues?fail,?ret=%d!\n",
??????ret);
??return?ret;
?}
?/*設(shè)置rx?queue的個(gè)數(shù)*/
?ret?=?netif_set_real_num_rx_queues(ndev,?h->q_num);
?if?(ret?0)?{
??netdev_err(ndev,
??????"netif_set_real_num_rx_queues?fail,?ret=%d!\n",?ret);
??return?ret;
?}
?/*啟動網(wǎng)卡*/
?ret?=?hns_nic_net_up(ndev);
?if?(ret)?{
??netdev_err(ndev,
??????"hns?net?up?fail,?ret=%d!\n",?ret);
??return?ret;
?}
?return?0;
}
static?int?hns_nic_net_up(struct?net_device?*ndev)
{
?struct?hns_nic_priv?*priv?=?netdev_priv(ndev);
?struct?hnae_handle?*h?=?priv->ae_handle;
?int?i,?j,?k;
?int?ret;
?/*初始化中斷,并設(shè)置中斷函數(shù)為hns_irq_handle,每個(gè)rx和tx?queue都對應(yīng)一個(gè)中斷*/
?ret?=?hns_nic_init_irq(priv);
?if?(ret?!=?0)?{
??netdev_err(ndev,?"hns?init?irq?failed!?ret=%d\n",?ret);
??return?ret;
?}
?for?(i?=?0;?i?q_num?*?2;?i++)?{
????????/*使能中斷,使能napi*/
??ret?=?hns_nic_ring_open(ndev,?i);
??if?(ret)
???goto?out_has_some_queues;
?}
?for?(k?=?0;?k?q_num;?k++)
??h->dev->ops->toggle_queue_status(h->qs[k],?1);
?/*設(shè)置mac地址*/
?ret?=?h->dev->ops->set_mac_addr(h,?ndev->dev_addr);
?if?(ret)
??goto?out_set_mac_addr_err;
?/*hns的start函數(shù)為null*/
?ret?=?h->dev->ops->start???h->dev->ops->start(h)?:?0;
?if?(ret)
??goto?out_start_err;
?if?(priv->phy)
????????/*啟動phy*/
??phy_start(priv->phy);
?clear_bit(NIC_STATE_DOWN,?&priv->state);
????/*修改time?每一秒到期一次*/
?(void)mod_timer(&priv->service_timer,?jiffies?+?SERVICE_TIMER_HZ);
?return?0;
out_start_err:
?netif_stop_queue(ndev);
out_set_mac_addr_err:
?for?(k?=?0;?k?q_num;?k++)
??h->dev->ops->toggle_queue_status(h->qs[k],?0);
out_has_some_queues:
?for?(j?=?i?-?1;?j?>=?0;?j--)
??hns_nic_ring_close(ndev,?j);
?set_bit(NIC_STATE_DOWN,?&priv->state);
?return?ret;
}
最后會調(diào)用到phy_start()啟動網(wǎng)卡。
void?phy_start(struct?phy_device?*phydev)
{
?bool?do_resume?=?false;
?int?err?=?0;
?mutex_lock(&phydev->lock);
?switch?(phydev->state)?{
?case?PHY_STARTING:
??phydev->state?=?PHY_PENDING;
??break;
?case?PHY_READY:
??phydev->state?=?PHY_UP;
??break;
?case?PHY_HALTED:
??/*?make?sure?interrupts?are?re-enabled?for?the?PHY?*/
??err?=?phy_enable_interrupts(phydev);
??if?(err?0)
???break;
??phydev->state?=?PHY_RESUMING;
??do_resume?=?true;
??break;
?default:
??break;
?}
?mutex_unlock(&phydev->lock);
?/*?if?phy?was?suspended,?bring?the?physical?link?up?again?*/
?if?(do_resume)
??phy_resume(phydev);
}
https://blog.csdn.net/qq_29044159/article/details/118030335
https://www.likecs.com/show-308571259.html
https://blog.csdn.net/Longyu_wlz/article/details/108026902
http://bbs.chinaunix.net/thread-2020457-1-1.html
https://blog.csdn.net/tiantao2012/article/details/75283527
https://blog.csdn.net/sinat_20184565/article/details/104353185


???????????????????????????????
? ??

分享

收藏

點(diǎn)贊

在看