4.2【Q】

在simbrick中,要实现对nic的模拟,要完成哪些步骤?实现流程是怎样的?

复制代码
    def connect(self, c: base.Channel) -> None:
        # Note AK: a bit ugly, but I think we can't get around a rt check here
        utils_base.has_expected_type(c, EthChannel)
        super().connect(c)

注释当中,AK和rt检查是什么?

网卡的工作原理是什么?接口列表是什么意思?我理解的是一个网卡IP就对应了一个接口,网卡只有一个IP,那么应该只有一个接口?为什么会有接口列表的概念

super().add_if(0等super().意味着什么?

在simbricks的system当中,pcie.py与eth.py等都是对模拟接口的封装,那么在实现时,实现的代码是在哪里?实现的逻辑是怎样的?

在add(nic)方法中,断言self._components<1是什么意思?这是在判定什么?为什么要进行这个断言?

复制代码
    # helper method for simulators that do not support
    # multiple sync periods etc. Should become eventually
    # at some point in the future...
    @staticmethod
    def get_unique_latency_period_sync(
        channels: list[sim_chan.Channel],
    ) -> tuple[int, int, bool]:
        latency = None
        sync_period = None
        run_sync = False
        for channel in channels:
            sync_period = (
                min(sync_period, channel.sync_period) if sync_period else channel.sync_period
            )
            run_sync = run_sync or channel._synchronized
            latency = (
                max(latency, channel.sys_channel.latency)
                if latency
                else channel.sys_channel.latency
            )
        if latency is None or sync_period is None:
            raise Exception("could not determine eth_latency and sync_period")
        return latency, sync_period, run_sync

详细解释一下这段代码

复制代码
        if eth_run_sync != pci_run_sync:

            raise Exception(

                "currently using different synchronization values for pci and eth is not supported"

            )

        run_sync = eth_run_sync

        sync_period = min(pci_sync_period, eth_sync_period)

这里run_sync = eth_run_sync是什么意思?

复制代码
    def repo_base(self, relative_path: str | None = None, must_exist: bool = True) -> str:

        if relative_path is None:

            return self._simbricks_dir

        return utils_file.join_paths(

            base=self._simbricks_dir, relative_path=relative_path, must_exist=must_exist

        )

详细解释一下repo_base方法,cmd = f"{inst.env.repo_base(relative_path=self._executable)} "中,inst.env是什么?

复制代码
    def get_parameters_url(

        self,

        inst: inst_base.Instantiation,

        socket: inst_socket.Socket,

        channel: sim_chan.Channel | None = None,

        sync: bool | None = None,

        latency: int | None = None,

        sync_period: int | None = None,

    ) -> str:

        if not channel and (sync == None or latency == None or sync_period == None):

            raise ValueError(

                "Cannot generate parameters url if channel and at least one of sync, "

                "latency, sync_period are None"

            )

        if channel:

            if not sync:

                sync = channel._synchronized

            if not latency:

                latency = channel.sys_channel.latency

            if not sync_period:

                sync_period = channel.sync_period



        sync_str = "true" if sync else "false"



        if socket._type == inst_socket.SockType.CONNECT:

            return (

                f"connect:{socket._path}:sync={sync_str}:latency={latency}"

                f":sync_interval={sync_period}"

            )

        else:

            return (

                f"listen:{socket._path}:{inst.env.get_simulator_shm_pool_path(self)}:sync={sync_str}"

                f":latency={latency}:sync_interval={sync_period}"

            )

解释一下这个得到url的代码

复制代码
        nic_devices = self.filter_components_by_type(ty=sys_nic.SimplePCIeNIC)
        assert len(nic_devices) == 1
        nic_device = nic_devices[0]

        socket = inst.get_socket(interface=nic_device._pci_if)
        assert socket is not None and socket._type == inst_socket.SockType.LISTEN
        params_url = self.get_parameters_url(
            inst, socket, sync=run_sync, latency=pci_latency, sync_period=sync_period
        )
        cmd += f"{params_url} "

        socket = inst.get_socket(interface=nic_device._eth_if)
        assert socket is not None and socket._type == inst_socket.SockType.LISTEN
        params_url = self.get_parameters_url(
            inst, socket, sync=run_sync, latency=eth_latency, sync_period=sync_period
        )
        cmd += f"{params_url} "

这里拼接的参数都有什么用,参数是怎么得到的?为什么要这些参数,而不是其他的?

这是sims目录下的一些模拟器代码,py胶水代码用的是哪个文件?并解析一下这些文件,与作用?如果我要自己实现一个模拟器,该怎么做?

复制代码
    def get_socket(self, interface: sys_base.Interface) -> inst_socket.Socket | None:
        if self._opposing_interface_within_same_sim(interface=interface):
            return None

        if interface in self._socket_per_interface:
            return self._socket_per_interface[interface]

        # create socket
        sock_path = self._interface_to_sock_path(interface=interface)
        new_socket = inst_socket.Socket(path=sock_path, ty=self.get_interface_socktype(interface))
        self._socket_per_interface[interface] = new_socket
        return new_socket

这个get_socket代码是什么逻辑?socket是什么?模拟nic,在nic中模拟了ipv4地址,和eth接口,怎么有了socket的模拟?

复制代码
    def _interface_to_sock_path(self, interface: sys_base.Interface) -> str:
        channel = interface.get_chan_raise()
        queue_ident = f"{channel.a._id}.{channel._id}.{channel.b._id}"

        queue_type = None
        match interface:
            case sys_pcie.PCIeHostInterface() | sys_pcie.PCIeDeviceInterface():
                queue_type = "pci"
            case sys_mem.MemDeviceInterface() | sys_mem.MemHostInterface():
                queue_type = "mem"
            case sys_eth.EthInterface():
                queue_type = "eth"
            case _:
                raise Exception("cannot create socket path for given interface type")

        assert queue_type is not None
        return self.env.shm_base(f"{queue_type}-{queue_ident}")

详细解释一下这段代码?

复制代码
simulation = sim_helpers.simple_simulation(
    sys,
    compmap={
        system.FullSystemHost: sim.QemuSim,
        system.IntelI40eNIC: sim.I40eNicSim,
        system.EthSwitch: sim.SwitchNet,
    },
)

这是模拟器胶水py对象nic构建时的代码,在胶水语言中,先是进行了self.get_channels(),这个get_channels()得到的是什么?为什么会得到pci和eth两个通道?以及通道连接的不就应该是接口if吗,为什么后续生成socket时,还是通过self的nic_device来得到的,直接用channels行吗?

nic_devices = self.filter_components_by_type(ty=sys_nic.SimplePCIeNIC)

assert len(nic_devices) == 1

nic_device = nic_devices[0]

在NICSim中,要得到自身模拟的对象nic_device,要通过filter_components方法,但是模拟器构建时

复制代码
def simple_simulation(
    system: system.System,
    sync=False,
    compmap=dict[type[system.Component], type[sim_base.Simulator]]
):
  """Create simple simulation from system. Uses a map from component type to
  simulator type and then creates one simulator per component."""
  simulation = sim_base.Simulation(
      name=f"simulation-{system.name}", system=system
  )

  for comp in system._all_components.values():
    if comp in simulation._sys_sim_map:
      continue

    for (ct,st) in compmap.items():
      if isinstance(comp, ct):
        simulator = st(simulation)
        simulator.add(comp)
        if comp.name:
          simulator.name = comp.name

    if not sync:
      disalbe_sync_simulation(simulation=simulation)

  return simulation

再重新解释一遍整个模拟构建的过程,尤其是模拟对象如何转化到实际的模拟器的,各模拟器的component什么时候开始有值的?

在模拟过程中,channels和socket是什么关系?有什么区别?

复制代码
#include <iostream>
#include <stdarg.h>

#include <simbricks/nicbm/nicbm.h>
#include "sims/nic/e1000_gem5/i8254xGBe.h"

static nicbm::Runner *runner;
static bool debug_enable = false;

class Gem5DMAOp : public nicbm::DMAOp, public nicbm::TimedEvent {
  public:
    EventFunctionWrapper &ev_;
    Gem5DMAOp(EventFunctionWrapper &ev) : ev_(ev) {}
    virtual ~Gem5DMAOp() = default;
};


/******************************************************************************/
/* nicbm callbacks */

void IGbE::SetupIntro(struct SimbricksProtoPcieDevIntro &di)
{
  di.bars[0].len = 128 * 1024;
  di.bars[0].flags = SIMBRICKS_PROTO_PCIE_BAR_64;

  di.pci_vendor_id = 0x8086;
  di.pci_device_id = 0x1075;
  di.pci_class = 0x02;
  di.pci_subclass = 0x00;
  di.pci_revision = 0x00;
}

void IGbE::RegRead(uint8_t bar, uint64_t addr, void *dest,
             size_t len)
{
    read(addr, len, dest);
    // TODO delay!
}

void IGbE::RegWrite(uint8_t bar, uint64_t addr, const void *src,
              size_t len)
{
    write(addr, len, src);
    // TODO delay!
}

void IGbE::DmaComplete(nicbm::DMAOp &op)
{
    Gem5DMAOp *dma = dynamic_cast <Gem5DMAOp *>(&op);
    if (dma->write_) {
        delete[] ((uint8_t *) dma->data_);
    } else {
        // schedule callback event. THis is at the current time, but can't call
        // directly to ensure event priorities are respected.
        dma->ev_.sched = true;
        dma->ev_.time_ = runner_->TimePs();
        runner_->EventSchedule(dma->ev_);
    }
    delete dma;
}

void IGbE::EthRx(uint8_t port, const void *data, size_t len)
{
    EthPacketPtr pp = std::make_shared<EthPacketData>(len);
    pp->length = len;
    memcpy(pp->data, data, len);

    ethRxPkt(pp);
}

void IGbE::Timed(nicbm::TimedEvent &te)
{
    if (Gem5DMAOp *dma = dynamic_cast <Gem5DMAOp *>(&te)) {
        runner_->IssueDma(*dma);
    } else if (EventFunctionWrapper *evw =
            dynamic_cast <EventFunctionWrapper *>(&te)) {
        evw->sched = false;
        evw->callback();
    } else {
        abort();
    }
}


/******************************************************************************/
/* gem5-ish APIs */

Tick IGbE::clockEdge(Tick t)
{
    if (t % 1000 != 0)
        t += 1000 - (t % 1000);
    t += 1000;
    return t;
}

void IGbE::schedule(EventFunctionWrapper &ev, Tick t)
{
    if (ev.sched) {
        fprintf(stderr, "schedule: already scheduled\n");
        abort();
    }
    ev.time_ = t;
    ev.sched = true;
    runner_->EventSchedule(ev);
}

void IGbE::reschedule(EventFunctionWrapper &ev, Tick t, bool always)
{
    if (ev.sched) {
        runner_->EventCancel(ev);
        ev.sched = false;
    } else if (!always) {
        fprintf(stderr, "reschedule: not yet scheduled\n");
        abort();
    }
    schedule(ev, t);
}

void IGbE::deschedule(EventFunctionWrapper &ev)
{
    if (!ev.sched) {
        fprintf(stderr, "deschedule: not scheduledd\n");
        abort();
    }
    runner_->EventCancel(ev);
    ev.sched = false;
}

void IGbE::intrPost()
{
    runner_->IntXIssue(true);
}

void IGbE::intrClear()
{
    runner_->IntXIssue(false);
}

void IGbE::dmaWrite(Addr daddr, size_t len, EventFunctionWrapper &ev,
    const void *buf, Tick delay)
{
    Gem5DMAOp *op = new Gem5DMAOp(ev);
    op->data_ = new uint8_t[len];
    memcpy(op->data_, buf, len);
    op->len_ = len;
    op->write_ = true;
    op->dma_addr_ = daddr;
    op->priority_ = 1;
    op->time_ = runner_->TimePs() + delay;
    runner_->EventSchedule(*op);

    ev.time_ = runner_->TimePs() + delay;
    runner_->EventSchedule(ev);
}

void IGbE::dmaRead(Addr saddr, size_t len, EventFunctionWrapper &ev,
    void *buf, Tick delay)
{
    ev.sched = true;

    Gem5DMAOp *op = new Gem5DMAOp(ev);
    op->data_ = buf;
    op->len_ = len;
    op->write_ = false;
    op->dma_addr_ = saddr;
    op->time_ = runner_->TimePs() + delay;
    op->priority_ = 2;
    runner_->EventSchedule(*op);

}

bool IGbE::sendPacket(EthPacketPtr p)
{
    runner_->EthSend(p->data, p->length);
    ethTxDone();
    return true;
}

void warn(const char *fmt, ...)
{
    fprintf(stderr, "warn: ");
    va_list va;
    va_start(va, fmt);
    vfprintf(stderr, fmt, va);
    va_end(va);
}

void panic(const char *fmt, ...)
{
    fprintf(stderr, "panic: ");
    va_list va;
    va_start(va, fmt);
    vfprintf(stderr, fmt, va);
    va_end(va);

    abort();
}

static void debug_init()
{
#ifdef DEBUG_E1000
    char *debug_env = getenv("E1000_DEBUG");
    if (debug_env &&
        (!strcmp(debug_env, "1") ||
         !strcmp(debug_env, "y") ||
         !strcmp(debug_env, "Y")))
    {
        warn("enabling debug messages because E1000_DEBUG envar set\n");
        debug_enable = true;
    }
#endif
}

void debug_printf(const char *fmt, ...)
{
    if (debug_enable) {
        va_list val;
        va_start(val, fmt);
        fprintf(stderr, "%lu: ", runner->TimePs());
        vfprintf(stderr, fmt, val);
        va_end(val);
    }
}

/******************************************************************************/

int main(int argc, char *argv[])
{
    debug_init();

    IGbEParams params;
    params.rx_fifo_size = 384 * 1024;
    params.tx_fifo_size = 384 * 1024;
    params.fetch_delay = 10 * 1000;
    params.wb_delay = 10 * 1000;
    params.fetch_comp_delay = 10 * 1000;
    params.wb_comp_delay = 10 * 1000;
    params.rx_write_delay = 0;
    params.tx_read_delay = 0;
    params.pio_delay = 0; // TODO
    params.rx_desc_cache_size = 64;
    params.tx_desc_cache_size = 64;
    params.phy_pid = 0x02A8;
    params.phy_epid = 0x0380;

    IGbE *dev = new IGbE(&params);

    runner = new nicbm::Runner(*dev);
    if (runner->ParseArgs(argc, argv))
        return EXIT_FAILURE;

    dev->init();
    return runner->RunMain();
}
相关推荐
头疼的程序员2 小时前
计算机网络:自顶向下方法(第七版)第八章 学习分享(一)
网络·学习·计算机网络
Brookty2 小时前
HTTP应用数据组织、HTML/CSS/JS数据描述及应用层数据处理介绍
css·网络·http·html
小小说( ̄(エ) ̄)2 小时前
VRRP协议---虚拟路由器冗余协议
网络·智能路由器
Oll Correct2 小时前
实验十三:IPv4子网划分与基础路由配置实验——基于Cisco Packet Tracer的跨网段通信验证
网络·笔记
dashizhi20152 小时前
服务器共享管理之设置共享文件访问权限、记录共享文件访问行为日志?
运维·网络·stm32·安全·电脑
观测云2 小时前
观测云3月产品升级报告 | 网络设备自动发现、数据库深度分析上线,故障中心、仪表板、APM及管理能力等持续优化
网络·数据库·apm
xingyuzhisuan3 小时前
给4090服务器配电源:8卡并行需要多少瓦才稳定?
服务器·网络·云计算·gpu算力
Chuncheng's blog3 小时前
Ubuntu 24.04如何配置静态IP
网络·tcp/ip·ubuntu
虎皮辣椒小怪兽3 小时前
OSPF基础
网络·智能路由器