| The existing interfaces for getting network packages time stamped are: | |
| * SO_TIMESTAMP | |
| Generate time stamp for each incoming packet using the (not necessarily | |
| monotonous!) system time. Result is returned via recv_msg() in a | |
| control message as timeval (usec resolution). | |
| * SO_TIMESTAMPNS | |
| Same time stamping mechanism as SO_TIMESTAMP, but returns result as | |
| timespec (nsec resolution). | |
| * IP_MULTICAST_LOOP + SO_TIMESTAMP[NS] | |
| Only for multicasts: approximate send time stamp by receiving the looped | |
| packet and using its receive time stamp. | |
| The following interface complements the existing ones: receive time | |
| stamps can be generated and returned for arbitrary packets and much | |
| closer to the point where the packet is really sent. Time stamps can | |
| be generated in software (as before) or in hardware (if the hardware | |
| has such a feature). | |
| SO_TIMESTAMPING: | |
| Instructs the socket layer which kind of information is wanted. The | |
| parameter is an integer with some of the following bits set. Setting | |
| other bits is an error and doesn't change the current state. | |
| SOF_TIMESTAMPING_TX_HARDWARE: try to obtain send time stamp in hardware | |
| SOF_TIMESTAMPING_TX_SOFTWARE: if SOF_TIMESTAMPING_TX_HARDWARE is off or | |
| fails, then do it in software | |
| SOF_TIMESTAMPING_RX_HARDWARE: return the original, unmodified time stamp | |
| as generated by the hardware | |
| SOF_TIMESTAMPING_RX_SOFTWARE: if SOF_TIMESTAMPING_RX_HARDWARE is off or | |
| fails, then do it in software | |
| SOF_TIMESTAMPING_RAW_HARDWARE: return original raw hardware time stamp | |
| SOF_TIMESTAMPING_SYS_HARDWARE: return hardware time stamp transformed to | |
| the system time base | |
| SOF_TIMESTAMPING_SOFTWARE: return system time stamp generated in | |
| software | |
| SOF_TIMESTAMPING_TX/RX determine how time stamps are generated. | |
| SOF_TIMESTAMPING_RAW/SYS determine how they are reported in the | |
| following control message: | |
| struct scm_timestamping { | |
| struct timespec systime; | |
| struct timespec hwtimetrans; | |
| struct timespec hwtimeraw; | |
| }; | |
| recvmsg() can be used to get this control message for regular incoming | |
| packets. For send time stamps the outgoing packet is looped back to | |
| the socket's error queue with the send time stamp(s) attached. It can | |
| be received with recvmsg(flags=MSG_ERRQUEUE). The call returns the | |
| original outgoing packet data including all headers preprended down to | |
| and including the link layer, the scm_timestamping control message and | |
| a sock_extended_err control message with ee_errno==ENOMSG and | |
| ee_origin==SO_EE_ORIGIN_TIMESTAMPING. A socket with such a pending | |
| bounced packet is ready for reading as far as select() is concerned. | |
| If the outgoing packet has to be fragmented, then only the first | |
| fragment is time stamped and returned to the sending socket. | |
| All three values correspond to the same event in time, but were | |
| generated in different ways. Each of these values may be empty (= all | |
| zero), in which case no such value was available. If the application | |
| is not interested in some of these values, they can be left blank to | |
| avoid the potential overhead of calculating them. | |
| systime is the value of the system time at that moment. This | |
| corresponds to the value also returned via SO_TIMESTAMP[NS]. If the | |
| time stamp was generated by hardware, then this field is | |
| empty. Otherwise it is filled in if SOF_TIMESTAMPING_SOFTWARE is | |
| set. | |
| hwtimeraw is the original hardware time stamp. Filled in if | |
| SOF_TIMESTAMPING_RAW_HARDWARE is set. No assumptions about its | |
| relation to system time should be made. | |
| hwtimetrans is the hardware time stamp transformed so that it | |
| corresponds as good as possible to system time. This correlation is | |
| not perfect; as a consequence, sorting packets received via different | |
| NICs by their hwtimetrans may differ from the order in which they were | |
| received. hwtimetrans may be non-monotonic even for the same NIC. | |
| Filled in if SOF_TIMESTAMPING_SYS_HARDWARE is set. Requires support | |
| by the network device and will be empty without that support. | |
| SIOCSHWTSTAMP: | |
| Hardware time stamping must also be initialized for each device driver | |
| that is expected to do hardware time stamping. The parameter is defined in | |
| /include/linux/net_tstamp.h as: | |
| struct hwtstamp_config { | |
| int flags; /* no flags defined right now, must be zero */ | |
| int tx_type; /* HWTSTAMP_TX_* */ | |
| int rx_filter; /* HWTSTAMP_FILTER_* */ | |
| }; | |
| Desired behavior is passed into the kernel and to a specific device by | |
| calling ioctl(SIOCSHWTSTAMP) with a pointer to a struct ifreq whose | |
| ifr_data points to a struct hwtstamp_config. The tx_type and | |
| rx_filter are hints to the driver what it is expected to do. If | |
| the requested fine-grained filtering for incoming packets is not | |
| supported, the driver may time stamp more than just the requested types | |
| of packets. | |
| A driver which supports hardware time stamping shall update the struct | |
| with the actual, possibly more permissive configuration. If the | |
| requested packets cannot be time stamped, then nothing should be | |
| changed and ERANGE shall be returned (in contrast to EINVAL, which | |
| indicates that SIOCSHWTSTAMP is not supported at all). | |
| Only a processes with admin rights may change the configuration. User | |
| space is responsible to ensure that multiple processes don't interfere | |
| with each other and that the settings are reset. | |
| /* possible values for hwtstamp_config->tx_type */ | |
| enum { | |
| /* | |
| * no outgoing packet will need hardware time stamping; | |
| * should a packet arrive which asks for it, no hardware | |
| * time stamping will be done | |
| */ | |
| HWTSTAMP_TX_OFF, | |
| /* | |
| * enables hardware time stamping for outgoing packets; | |
| * the sender of the packet decides which are to be | |
| * time stamped by setting SOF_TIMESTAMPING_TX_SOFTWARE | |
| * before sending the packet | |
| */ | |
| HWTSTAMP_TX_ON, | |
| }; | |
| /* possible values for hwtstamp_config->rx_filter */ | |
| enum { | |
| /* time stamp no incoming packet at all */ | |
| HWTSTAMP_FILTER_NONE, | |
| /* time stamp any incoming packet */ | |
| HWTSTAMP_FILTER_ALL, | |
| /* return value: time stamp all packets requested plus some others */ | |
| HWTSTAMP_FILTER_SOME, | |
| /* PTP v1, UDP, any kind of event packet */ | |
| HWTSTAMP_FILTER_PTP_V1_L4_EVENT, | |
| /* for the complete list of values, please check | |
| * the include file /include/linux/net_tstamp.h | |
| */ | |
| }; | |
| DEVICE IMPLEMENTATION | |
| A driver which supports hardware time stamping must support the | |
| SIOCSHWTSTAMP ioctl and update the supplied struct hwtstamp_config with | |
| the actual values as described in the section on SIOCSHWTSTAMP. | |
| Time stamps for received packets must be stored in the skb. To get a pointer | |
| to the shared time stamp structure of the skb call skb_hwtstamps(). Then | |
| set the time stamps in the structure: | |
| struct skb_shared_hwtstamps { | |
| /* hardware time stamp transformed into duration | |
| * since arbitrary point in time | |
| */ | |
| ktime_t hwtstamp; | |
| ktime_t syststamp; /* hwtstamp transformed to system time base */ | |
| }; | |
| Time stamps for outgoing packets are to be generated as follows: | |
| - In hard_start_xmit(), check if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) | |
| is set no-zero. If yes, then the driver is expected to do hardware time | |
| stamping. | |
| - If this is possible for the skb and requested, then declare | |
| that the driver is doing the time stamping by setting the flag | |
| SKBTX_IN_PROGRESS in skb_shinfo(skb)->tx_flags , e.g. with | |
| skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; | |
| You might want to keep a pointer to the associated skb for the next step | |
| and not free the skb. A driver not supporting hardware time stamping doesn't | |
| do that. A driver must never touch sk_buff::tstamp! It is used to store | |
| software generated time stamps by the network subsystem. | |
| - As soon as the driver has sent the packet and/or obtained a | |
| hardware time stamp for it, it passes the time stamp back by | |
| calling skb_hwtstamp_tx() with the original skb, the raw | |
| hardware time stamp. skb_hwtstamp_tx() clones the original skb and | |
| adds the timestamps, therefore the original skb has to be freed now. | |
| If obtaining the hardware time stamp somehow fails, then the driver | |
| should not fall back to software time stamping. The rationale is that | |
| this would occur at a later time in the processing pipeline than other | |
| software time stamping and therefore could lead to unexpected deltas | |
| between time stamps. | |
| - If the driver did not set the SKBTX_IN_PROGRESS flag (see above), then | |
| dev_hard_start_xmit() checks whether software time stamping | |
| is wanted as fallback and potentially generates the time stamp. |