【liunxptp协议栈详解第一部分】

本文详细介绍了Linux PTP协议栈的工作流程,特别是在E2E延迟机制下的操作。从master发送sync和follow_up报文开始,经过slave接收并回应delay_req,再到master处理delay_req并发送delay_resp,最后slave接收到delay_resp计算延迟值。主要涉及协议栈的各个步骤及报文处理细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

liunxptp 代码学习

场景一

当我们配置时钟A为OC且一个端口为master,一个时钟B为BC,一个端口为slave,且延迟机制为E2E。

延迟机制的基本原理

延迟机制的算法方式

协议栈的过程

一、step 1: master->slave(sync报文的发送和sync的follow_up报文的发送)

int clock_poll(struct clock *c)
{
   
	int cnt, i;
	enum fsm_event event;
	struct pollfd *cur;
	struct port *p;

	clock_check_pollfd(c);
	cnt = poll(c->pollfd, (c->nports + 1) * N_CLOCK_PFD, -1);
	if (cnt < 0) {
   
		if (EINTR == errno) {
   
			return 0;
		} else {
   
			pr_emerg("poll failed");
			return -1;
		}
	} else if (!cnt) {
   
		return 0;
	}

	cur = c->pollfd;

	LIST_FOREACH(p, &c->ports, list) {
   
		/* Let the ports handle their events. */
		for (i = 0; i < N_POLLFD; i++) {
   
			if (cur[i].revents & (POLLIN|POLLPRI|POLLERR)) {
   
				if (cur[i].revents & POLLERR) {
   
					pr_err("port %d: unexpected socket error",
					       port_number(p));
					event = EV_FAULT_DETECTED;
				} else {
   
					/*引发事件,这是一个回调,当为0C/BC是回调bc_event,E2ETC时回e2e_event.P2PTC时回调p2p_event*/
					event = port_event(p, i);
				}
				/* 推荐状态事件是由STATE—DECISION—EVENT引发的执行最佳主时钟算法的结果*/
				if (EV_STATE_DECISION_EVENT == event) {
   
					c->sde = 1;
				}
				/* see 9.2.6 */
				if (EV_ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES == event) {
   
					c->sde = 1;
				}
				port_dispatch(p, event, 0);
				/* Clear any fault after a little while. */
				if (PS_FAULTY == port_state(p)) {
   
					clock_fault_timeout(p, 1);
					break;
				}
			}
		}

bc_event的代码

// bc_event
static enum fsm_event bc_event(struct port *p, int fd_index)
{
   
	enum fsm_event event = EV_NONE;
	struct ptp_message *msg;
	int cnt, fd = p->fda.fd[fd_index], err;
    /*时钟自己发送的请求*/
	switch (fd_index) {
   
	case FD_ANNOUNCE_TIMER:
	case FD_SYNC_RX_TIMER:
		pr_debug("port %hu: %s timeout", portnum(p),
			 fd_index == FD_SYNC_RX_TIMER ? "rx sync" : "announce");
		if (p->best) {
   
			fc_clear(p->best);
		}

		/*
		 * Clear out the event returned by poll(). It is only cleared
		 * in port_*_transition(). But, when BMCA == 'noop', there is no
		 * state transition. So, it won't be cleared anywhere else.
		 */
		if (p->bmca == BMCA_NOOP) {
   
			port_clr_tmo(p->fda.fd[FD_SYNC_RX_TIMER]);
		}

		if (p->inhibit_announce) {
   
			port_clr_tmo(p->fda.fd[FD_ANNOUNCE_TIMER]);
		} else {
   
			port_set_announce_tmo(p);
		}

		delay_req_prune(p);
		if (clock_slave_only(p->clock) && p->delayMechanism != DM_P2P &&
		    port_renew_transport(p)) {
   
			return EV_FAULT_DETECTED;
		}

		if (p->inhibit_announce) {
   
			return EV_NONE;
		}
		return EV_ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES;

	case FD_DELAY_TIMER:
		pr_debug("port %hu: delay timeout", portnum(p));
		port_set_delay_tmo(p);
		delay_req_prune(p);
		return port_delay_request(p) ? EV_FAULT_DETECTED : EV_NONE;

	case FD_QUALIFICATION_TIMER:
		pr_debug("port %hu: qualification timeout", portnum(p));
		return EV_QUALIFICATION_TIMEOUT_EXPIRES;

	case FD_MANNO_TIMER:
		pr_debug("port %hu: master tx announce timeout", portnum(p));
		port_set_manno_tmo(p);
		return port_tx_announce(p, NULL) ? EV_FAULT_DETECTED : EV_NONE;

	case FD_SYNC_TX_TIMER:
		pr_debug("port %hu: master sync timeout", portnum(p));
		port_set_sync_tx_tmo(p);
		/*处于master端口应周期性的发送sync报文,sync报文应多播方式发送*/
		return port_tx_sync(p, NULL) ? EV_FAULT_DETECTED : EV_NONE;

	case FD_UNICAST_SRV_TIMER:
		pr_debug("port %hu: unicast service timeout", portnum(p));
		return unicast_service_timer(p) ? EV_FAULT_DETECTED : EV_NONE;

	case FD_UNICAST_REQ_TIMER:
		pr_debug("port %hu: unicast request timeout", portnum(p));
		return unicast_client_timer(p) ? EV_FAULT_DETECTED : EV_NONE;

	case FD_RTNL:
		pr_debug("port %hu: received link status notification", portnum(p));
		rtnl_link_status(fd, p->name, port_link_status, p);
		if (p->link_status == (LINK_UP | LINK_STATE_CHANGED))
			return EV_FAULT_CLEARED;
		else if ((p->link_status == (LINK_DOWN | LINK_STATE_CHANGED)) ||
			 (p->link_status & TS_LABEL_CHANGED))
			return EV_FAULT_DETECTED;
		else
			return EV_NONE;
	}

	msg = msg_allocate();
	if (!msg)
		return EV_FAULT_DETECTED;

	msg->hwts.type = p->timestamping;
	/*时钟接收到的报文*/
	cnt = transport_recv(p->trp, fd, msg);
	if (cnt < 0) {
   
		pr_err("port %hu: recv message failed", portnum(p));
		msg_put(msg);
		return EV_FAULT_DETECTED;
	}
	err = msg_post_recv(msg, cnt);
	if (err) {
   
		switch (err) {
   
		case -EBADMSG:
			pr_err("port %hu: bad message", portnum(p));
			break;
		case -EPROTO:
			pr_debug("port %hu: ignoring message", portnum(p));
			break;
		}
		msg_put(msg);
		return EV_NONE;
	}
	port_stats_inc_rx(p, msg);
	if (port_ignore(p, msg)) {
   
		msg_put(msg);
		return EV_NONE;
	}
	if (msg_sots_missing(msg) &&
	    !(p->timestamping == TS_P2P1STEP && msg_type(msg) == PDELAY_REQ)) {
   
		pr_err("port %hu: received %s without timestamp",
		       portnum(p), msg_type_string(msg_type(msg)));
		msg_put(msg);
		return EV_NONE;
	}
	if (msg_sots_valid(msg)) {
   
		ts_add(&msg->hwts.ts, -p->rx_timestamp_offset);
		clock_check_ts(p->clock, tmv_to_nanoseconds(msg->hwts.ts));
	}
	/*对收到的报文进行处理*/
	switch (msg_type(msg)) {
   
	case SYNC:
		process_sync(p, msg);
		break;
	case DELAY_REQ:
		if (process_delay_req(p, msg))
			event = EV_FAULT_DETECTED;
		break;
	case PDELAY_REQ:
		if (process_pdelay_req(p, msg))
			event = EV_FAULT_DETECTED;
		break;
	case PDELAY_RESP:
		if (process_pdelay_resp(p, msg))
			event = EV_FAULT_DETECTED;
		break;
	case FOLLOW_UP:
		process_follow_up(p, msg);
		break;
	case DELAY_RESP:
		process_delay_resp(p, msg);
		break;
	case PDELAY_RESP_FOLLOW_UP:
		process_pdelay_resp_fup(p, msg);
		break;
	case ANNOUNCE:
		if (process_announce(p, msg))
			event = EV_STATE_DECISION_EVENT;
		break;
	case SIGNALING:
		if (process_signaling(p, msg)) {
   
			event = EV_FAULT_DETECTED;
		}
		break;
	case MANAGEMENT:
		if (clock_manage(p->clock, p, msg))
			event = EV_STATE_DECISION_EVENT;
		break;
	}

	msg_put(msg);
	return event;
}

bc_event中有两个switch(msg_type),第一个主动发出的报文,第二个对接收到的报文进行处理。

int port_tx_announce(struct port *p, struct address *dst)
{
   
	struct timePropertiesDS tp = clock_time_properties(p->clock);
	struct parent_ds *dad = clock_parent_ds(p->clock);
	struct ptp_message *msg;
	int err;

	if (p->inhibit_multicast_service && !dst) {
   
		return 0;
	}
	if (!port_capable(p)) {
   
		return 0;
	}
	msg = msg_allocate();
	if (!msg) {
   
		return -1;
	}

	msg->hwts.type = p->timestamping;

	msg->header.tsmt               = ANNOUNCE | p->transportSpecific;
	msg->header.ver                = PTP_VERSION;
	msg->header.messageLength      = sizeof(struct announce_msg);
	msg->header.domainNumber       = clock_domain_number(p->clock);
	msg->header.sourcePortIdentity = p->portIdentity;
	msg->header.sequenceId         = p->seqnum.announce++;
	msg->header.control            = CTL_OTHER;
	msg->header.logMessageInterval = p->logAnnounceInterval;

	msg->header.flagField[1] = tp.flags;

	if (dst) {
   
		msg->address = *dst;
		msg->header.flagField[0] |= UNICAST;
	}
	msg->announce.currentUtcOffset        = tp.currentUtcOffset;
	msg->announce.grandmasterPriority1    = dad->pds.grandmasterPriority1;
	msg->announce.grandmasterClockQuality = dad->pds.grandmasterClockQuality;
	msg->announce.grandmasterPriority2    = dad->pds.grandmasterPriority2;
	msg->announce.grandmasterIdentity     = dad->pds.grandmasterIdentity;
	msg->announce.stepsRemoved            = clock_steps_removed(p->clock);
	msg->announce.timeSource              = tp.timeSource;

	if (p->path_trace_enabled && path_trace_append(p, msg, dad)) {
   
		pr_err("port %hu: append path trace failed", portnum(p));
	}

	err = port_prepare_and_send(p, msg, TRANS_GENERAL)
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值