Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/mlx5/mlx5_en/mlx5_en_main.c
| Show First 20 Lines • Show All 1,129 Lines • ▼ Show 20 Lines | do { | ||||
| hw_h = ioread32be(&iseg->internal_timer_h); | hw_h = ioread32be(&iseg->internal_timer_h); | ||||
| hw_l = ioread32be(&iseg->internal_timer_l); | hw_l = ioread32be(&iseg->internal_timer_l); | ||||
| hw_h1 = ioread32be(&iseg->internal_timer_h); | hw_h1 = ioread32be(&iseg->internal_timer_h); | ||||
| } while (hw_h1 != hw_h); | } while (hw_h1 != hw_h); | ||||
| return (((uint64_t)hw_h << 32) | hw_l); | return (((uint64_t)hw_h << 32) | hw_l); | ||||
| } | } | ||||
| /* | /* | ||||
| * Seed the first calibration point so that base_prev and clbr_hw_prev | |||||
| * are always valid. Called once during attach before the first | |||||
| * calibration callout fires. | |||||
| */ | |||||
| static void | |||||
| mlx5e_seed_calibration(struct mlx5e_priv *priv) | |||||
| { | |||||
| struct mlx5e_clbr_point *cp; | |||||
| struct timespec ts; | |||||
| cp = &priv->clbr_points[0]; | |||||
| cp->clbr_hw_curr = mlx5e_hw_clock(priv); | |||||
| nanouptime(&ts); | |||||
| cp->base_curr = mlx5e_timespec2usec(&ts); | |||||
| cp->clbr_hw_prev = cp->clbr_hw_curr - 1; | |||||
| cp->base_prev = cp->base_curr - 1; | |||||
| } | |||||
| /* | |||||
| * The calibration callout, it runs either in the context of the | * The calibration callout, it runs either in the context of the | ||||
| * thread which enables calibration, or in callout. It takes the | * thread which enables calibration, or in callout. It takes the | ||||
| * snapshot of system and adapter clocks, then advances the pointers to | * snapshot of system and adapter clocks, then advances the pointers to | ||||
| * the calibration point to allow rx path to read the consistent data | * the calibration point to allow rx path to read the consistent data | ||||
| * lockless. | * lockless. | ||||
| */ | */ | ||||
| static void | static void | ||||
| mlx5e_calibration_callout(void *arg) | mlx5e_calibration_callout(void *arg) | ||||
| { | { | ||||
| struct mlx5e_priv *priv; | struct mlx5e_priv *priv; | ||||
| struct mlx5e_clbr_point *next, *curr; | struct mlx5e_clbr_point *next, *curr; | ||||
| struct timespec ts; | struct timespec ts; | ||||
| uint64_t hw_delta_new, hw_delta_old; | |||||
| uint64_t old_nsec, old_projected, old_sec; | |||||
| uint64_t res_n, res_s, res_s_mod, rt_delta_old; | |||||
| int clbr_curr_next; | int clbr_curr_next; | ||||
| priv = arg; | priv = arg; | ||||
| curr = &priv->clbr_points[priv->clbr_curr]; | curr = &priv->clbr_points[priv->clbr_curr]; | ||||
| clbr_curr_next = priv->clbr_curr + 1; | clbr_curr_next = priv->clbr_curr + 1; | ||||
| if (clbr_curr_next >= nitems(priv->clbr_points)) | if (clbr_curr_next >= nitems(priv->clbr_points)) | ||||
| clbr_curr_next = 0; | clbr_curr_next = 0; | ||||
| next = &priv->clbr_points[clbr_curr_next]; | next = &priv->clbr_points[clbr_curr_next]; | ||||
| Show All 12 Lines | if (((next->clbr_hw_curr - curr->clbr_hw_curr) >> MLX5E_TSTMP_PREC) == | ||||
| } | } | ||||
| atomic_store_rel_int(&curr->clbr_gen, 0); | atomic_store_rel_int(&curr->clbr_gen, 0); | ||||
| return; | return; | ||||
| } | } | ||||
| nanouptime(&ts); | nanouptime(&ts); | ||||
| next->base_curr = mlx5e_timespec2usec(&ts); | next->base_curr = mlx5e_timespec2usec(&ts); | ||||
| /* | |||||
| * Ensure monotonicity across calibration transitions. Compute | |||||
| * what the old calibration would extrapolate to at the new | |||||
| * hw_curr. If the new base_curr is less, clamp it so the new | |||||
| * slope is at least as steep as the old one. This prevents | |||||
| * packets from seeing time go backwards when the slope drops. | |||||
| * | |||||
| * Use the same split-seconds technique as mlx5e_mbuf_tstmp() | |||||
| * to avoid overflowing uint64_t in the multiplication. | |||||
| */ | |||||
| hw_delta_new = next->clbr_hw_curr - curr->clbr_hw_curr; | |||||
| rt_delta_old = curr->base_curr - curr->base_prev; | |||||
| hw_delta_old = curr->clbr_hw_curr - curr->clbr_hw_prev; | |||||
| old_sec = hw_delta_new / priv->cclk; | |||||
| old_nsec = hw_delta_new % priv->cclk; | |||||
| res_s = old_sec * rt_delta_old; | |||||
| res_n = old_nsec * rt_delta_old; | |||||
| res_s_mod = res_s % hw_delta_old; | |||||
| res_s /= hw_delta_old; | |||||
| res_s_mod *= priv->cclk; | |||||
| res_n += res_s_mod; | |||||
| res_n /= hw_delta_old; | |||||
| res_s *= priv->cclk; | |||||
| old_projected = curr->base_curr + res_s + res_n; | |||||
| if (next->base_curr < old_projected) | |||||
| next->base_curr = old_projected; | |||||
| curr->clbr_gen = 0; | curr->clbr_gen = 0; | ||||
| atomic_thread_fence_rel(); | atomic_thread_fence_rel(); | ||||
| priv->clbr_curr = clbr_curr_next; | priv->clbr_curr = clbr_curr_next; | ||||
| atomic_store_rel_int(&next->clbr_gen, ++(priv->clbr_gen)); | atomic_store_rel_int(&next->clbr_gen, ++(priv->clbr_gen)); | ||||
| if (priv->clbr_done < mlx5e_calibration_duration) | if (priv->clbr_done < mlx5e_calibration_duration) | ||||
| priv->clbr_done++; | priv->clbr_done++; | ||||
| mlx5e_reset_calibration_callout(priv); | mlx5e_reset_calibration_callout(priv); | ||||
| ▲ Show 20 Lines • Show All 3,696 Lines • ▼ Show 20 Lines | #endif | ||||
| SYSCTL_ADD_INT(&priv->sysctl_ctx, SYSCTL_CHILDREN(priv->sysctl_ifnet), | SYSCTL_ADD_INT(&priv->sysctl_ctx, SYSCTL_CHILDREN(priv->sysctl_ifnet), | ||||
| OID_AUTO, "rx_clbr_done", CTLFLAG_RD, | OID_AUTO, "rx_clbr_done", CTLFLAG_RD, | ||||
| &priv->clbr_done, 0, | &priv->clbr_done, 0, | ||||
| "RX timestamps calibration state"); | "RX timestamps calibration state"); | ||||
| callout_init(&priv->tstmp_clbr, 1); | callout_init(&priv->tstmp_clbr, 1); | ||||
| /* Pull out the frequency of the clock in hz */ | /* Pull out the frequency of the clock in hz */ | ||||
| priv->cclk = (uint64_t)MLX5_CAP_GEN(mdev, device_frequency_khz) * 1000ULL; | priv->cclk = (uint64_t)MLX5_CAP_GEN(mdev, device_frequency_khz) * 1000ULL; | ||||
| mlx5e_seed_calibration(priv); | |||||
| mlx5e_reset_calibration_callout(priv); | mlx5e_reset_calibration_callout(priv); | ||||
| pa.pa_version = PFIL_VERSION; | pa.pa_version = PFIL_VERSION; | ||||
| pa.pa_flags = PFIL_IN; | pa.pa_flags = PFIL_IN; | ||||
| pa.pa_type = PFIL_TYPE_ETHERNET; | pa.pa_type = PFIL_TYPE_ETHERNET; | ||||
| pa.pa_headname = if_name(ifp); | pa.pa_headname = if_name(ifp); | ||||
| priv->pfil = pfil_head_register(&pa); | priv->pfil = pfil_head_register(&pa); | ||||
| ▲ Show 20 Lines • Show All 262 Lines • Show Last 20 Lines | |||||