美文网首页LinuxPTP
linuxptp中的clock_adjtime如何影响到PHC

linuxptp中的clock_adjtime如何影响到PHC

作者: SnC_ | 来源:发表于2021-10-22 14:59 被阅读0次

    1. clock_adjtime映射到sys_clock_adjtime

    根据这个patch中的内容,首先,在x86平台中,user space函数clock_adjtime会被映射到__NR_clock_adjtime

    在32bit环境中:

    /* In /arch/x86/include/asm/unistd_32.h */
     #define __NR_rt_tgsigqueueinfo 335
     #define __NR_perf_event_open   336
     #define __NR_recvmmsg      337
    +#define __NR_clock_adjtime 338
    
    /* In /arch/x86/kernel/syscall_table_32.S */
    ENTRY(sys_call_table)
        ...
        .long sys_rt_tgsigqueueinfo /* 335 */
        .long sys_perf_event_open
        .long sys_recvmmsg
    +   .long sys_clock_adjtime
    

    在64bit环境中,

    /* In /arch/x86/include/asm/unistd_64.h */
     __SYSCALL(__NR_perf_event_open, sys_perf_event_open)
     #define __NR_recvmmsg              299
     __SYSCALL(__NR_recvmmsg, sys_recvmmsg)
    +#define __NR_clock_adjtime         300
    +__SYSCALL(__NR_clock_adjtime, sys_clock_adjtime)
    

    所以,在x86平台下,clock_adjtime被映射到sys_clock_adjtime
    在其他平台(如arm, powerpc等)中差不多也是如此。


    2. posix clock的描述

    根据这个patch里的内容,clock_adjtime函数,是用来操作posix clock的。它接收2个参数,一个是clock id,一个是struct timex。

    posix clock的生成需要k_clock。
    当posix clock通过dynamic clock creation method生成(如create_posix_clock())时,其clock id是动态生成的。
    若通过register_posix_clock()进行注册,则其clock id是自带的。

    无论通过哪种方式,它们的作用都是将clock放到posix_clocks[]全局变量中。

    int register_posix_clock(const clockid_t id, struct k_clock *clock)
    {
      struct k_clock *kc;
      int err = 0;
      mutex_lock(&clocks_mux);
      if (test_bit(id, clocks_map)) {
        pr_err("clock_id %d already registered\n", id);
        err = -EBUSY;
        goto out;
      }
      kc = &posix_clocks[id];
      *kc = *clock;
      kc->id = id;
      set_bit(id, clocks_map);
    }
    
    clockid_t create_posix_clock(struct k_clock *clock)
    {
        clockid_t id;
    
        mutex_lock(&clocks_mux);
        id = find_first_zero_bit(clocks_map, MAX_CLOCKS);
        mutex_unlock(&clocks_mux);
    
        if (id < MAX_CLOCKS) {
            register_posix_clock(id, clock);
            return id;
        }
        return CLOCK_INVALID;
    +}
    

    3. ptp clock中的一些操作

    根据这个patch中的一些内容,可以得知ptp clock相关的一些信息。

    /**
     * ptp_clock_register() - register a PTP hardware clock driver
     *
     * @info:  Structure describing the new clock.
     */
    struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info)
    {
        struct k_clock clk;
        struct ptp_clock *ptp;
        int err = 0, index, major = MAJOR(ptp_devt);
    
        /* Find a free clock slot and reserve it. */
        index = find_first_zero_bit(clocks.map, PTP_MAX_CLOCKS);
        set_bit(index, clocks.map);
    
        /* Initialize a clock structure. */
        ptp = kzalloc(sizeof(struct ptp_clock), GFP_KERNEL);
        ptp->info = info;
        ptp->devid = MKDEV(major, index);
        ptp->index = index;
    
        /* Create a new device in our class. */
        ptp->dev = device_create(ptp_class, NULL, ptp->devid, ptp,
                     "ptp%d", ptp->index);
        dev_set_drvdata(ptp->dev, ptp);
        ptp_register_chardev(ptp);
        ptp_populate_sysfs(ptp);
    
        /* Register a new PPS source. */
        if (info->pps) {
            struct pps_source_info pps;
            memset(&pps, 0, sizeof(pps));
            snprintf(pps.name, PPS_MAX_NAME_LEN, "ptp%d", index);
            pps.mode = PTP_PPS_MODE;
            pps.owner = info->owner;
            ptp->pps_source = pps_register_source(&pps, PTP_PPS_DEFAULTS);
    
        /* Create a posix clock. */
        memset(&clk, 0, sizeof(clk));
        clk.clock_getres    = ptp_clock_getres;
        clk.clock_set       = ptp_clock_set;
        clk.clock_get       = ptp_clock_get;
        clk.clock_adj       = ptp_clock_adj;
        clk.timer_create    = ptp_timer_create;
        clk.nsleep      = ptp_nsleep;
        clk.nsleep_restart  = ptp_nsleep_restart;
        clk.timer_set       = ptp_timer_set;
        clk.timer_del       = ptp_timer_del;
        clk.timer_get       = ptp_timer_get;
    
        snprintf(clk.name, KCLOCK_MAX_NAME, "ptp%d", index);
    
        ptp->clock_id = create_posix_clock(&clk);
    
        /* Prevent this module from unloading. */
        try_module_get(info->owner);
    
        /* Clock is ready, add it into the list. */
        list_add(&ptp->list, &clocks.list);
        clocks.data[ptp->clock_id] = ptp;
    
        return ptp;
    }
    

    可以看出,ptp_clock_register创建了ptp_clock以及k_clock,并将k_clock关联到ptp_clock。
    也可以看出,是先有的ptp_clock_info,再有的ptp_clock。

    而k_clock中注册了一些函数,但这些都是标准接口,最终是会调用到ptp_clock_info中注册的函数,比如下面这个

    /* In /drivers/ptp/ptp_clock.c */
    static int ptp_clock_adj(const clockid_t clkid, struct timex *tx)
    {
        struct ptp_clock *ptp;
        struct ptp_clock_info *ops;
    
        ptp = clockid_to_ptpclock(clkid);
        ops = ptp->info;
    
        if (tx->modes & ADJ_SETOFFSET) {
            struct timespec ts;
            ts.tv_sec = tx->time.tv_sec;
            ts.tv_nsec = tx->modes & ADJ_NANO ? tx->time.tv_usec :
                tx->time.tv_usec * 1000;
            err = ops->adjtime(ops->priv, &ts);
        } else if (tx->modes & ADJ_FREQUENCY) {
            s64 ppb = 1 + tx->freq;
            ppb *= 125;
            ppb >>= 13;
            err = ops->adjfreq(ops->priv, (s32)ppb);
        }
        return err;
    }
    

    ptp_clock_info,是在设备驱动的代码里被事先定义好的。
    它们要么是全局变量(如struct ptp_clock_info ptp_caps),要么在adapter初始化ptp功能时被赋值。

    /* In /drivers\net\ethernet\intel\igb\igb_ptp.c */
    void igb_ptp_init(struct igb_adapter *adapter)
    {
        struct e1000_hw *hw = &adapter->hw;
        struct net_device *netdev = adapter->netdev;
    
        switch (hw->mac.type)
        {
        case e1000_82576:
            snprintf(adapter->ptp_caps.name, 16, "%pm", netdev->dev_addr);
            adapter->ptp_caps.owner = THIS_MODULE;
            adapter->ptp_caps.max_adj = 1000000000;
            adapter->ptp_caps.n_ext_ts = 0;
            adapter->ptp_caps.pps = 0;
            adapter->ptp_caps.adjfreq = igb_ptp_adjfreq_82576;
            adapter->ptp_caps.adjtime = igb_ptp_adjtime_82576;
            adapter->ptp_caps.gettime = igb_ptp_gettime_82576;
            adapter->ptp_caps.settime = igb_ptp_settime_82576;
            adapter->ptp_caps.enable = igb_ptp_enable;
            adapter->cc.read = igb_ptp_read_82576;
            adapter->cc.mask = CLOCKSOURCE_MASK(64);
            adapter->cc.mult = 1;
            adapter->cc.shift = IGB_82576_TSYNC_SHIFT;
            /* Dial the nominal frequency. */
            wr32(E1000_TIMINCA, INCPERIOD_82576 | INCVALUE_82576);
            break;
       ...
        }
    
        adapter->ptp_clock = ptp_clock_register(&adapter->ptp_caps,
                                                &adapter->pdev->dev);
    }
    

    所以现在可以整理一下整个流程的顺序了:

    1. 设备启动,初始化struct ptp_clock_info,注册各种操作函数(adjtime, adjfreq等)。
    2. 调用ptp_clock_register(),通过ptp_clock_info创建k_clock以及ptp_clock,并将两者关联起来。同时也得知了clock_id。
    3. clock_adjtime操作posix_clock,也就是操作k_clock。

    现在的问题是,sys_clock_adjtime是如何调用到ptp_clock_adj的?


    以下是在linux kernel中找到的一些代码片段

    SYSCALL_DEFINE2(clock_adjtime, const clockid_t, which_clock,
            struct timex __user *, utx)
    {
        struct k_clock *kc = clockid_to_kclock(which_clock);
        struct timex ktx;
    
        if (copy_from_user(&ktx, utx, sizeof(ktx)))
            return -EFAULT;
    
        err = kc->clock_adj(which_clock, &ktx);
        return err;
    
    static struct k_clock *clockid_to_kclock(const clockid_t id)
    {
        if (id < 0)
            return (id & CLOCKFD_MASK) == CLOCKFD ?
                &clock_posix_dynamic : &clock_posix_cpu;
        return &posix_clocks[id];
    }
    

    相关文章

      网友评论

        本文标题:linuxptp中的clock_adjtime如何影响到PHC

        本文链接:https://www.haomeiwen.com/subject/lpexaltx.html