adjtimex.2: Update a detail in adjtimex return value description

Starting with Linux 3.4, the time_state change is now asynchronous
and the call returns the time_state before it has been altered,
rather than after it was altered as the previous code did.

======================================================

Notes from Petr Gajdos

attached program behaves differently on 2.6 and 3.0 kernels:

*******************************************************
2.6.32.59-0.7-default

Insert LS
adjtimex() return code 1: insert leap second (TIME_INS)
Kernel leap second flag: STA_INS

Delete LS
adjtimex() return code 2: delete leap second (TIME_DEL)
Kernel leap second flag: STA_DEL

Insert LS again
adjtimex() return code 1: insert leap second (TIME_INS)
Kernel leap second flag: STA_INS

Delete LS again
adjtimex() return code 2: delete leap second (TIME_DEL)
Kernel leap second flag: STA_DEL

*******************************************************
3.0.101-0.35-default

sbsvt101:~/tmp # ./adjt
Insert LS
adjtimex() return code 0: clock synchronized (TIME_OK)
Kernel leap second flag: STA_INS

Delete LS
adjtimex() return code 1: insert leap second (TIME_INS)
Kernel leap second flag: STA_DEL

Insert LS again
adjtimex() return code 2: delete leap second (TIME_DEL)
Kernel leap second flag: STA_INS

Delete LS again
adjtimex() return code 1: insert leap second (TIME_INS)
Kernel leap second flag: STA_DEL

*******************************************************

The explanation provided by William Preston (in CC):

"""
in both cases you get the actual value of the global kernel variable
time_state.

The code changed between the two kernels in the way it handled this.

In 2.6.32.59 the adjtimex call starts a timer immediately with the
function ntp_start_leap_timer(), which sets the value of time_state
before returning to the user.

This means you will get the value TIME_INS / TIME_DEL back
immediately.

In 3.0.101, the timer has been removed and the value of time_state is
set when the microsecond field overflows. The function
second_overflow() handles this. This means the value of time_state
will not have been set when the syscall adjtimex returns, and so you
will almost certainly get the old state.

Incidentally if you remove the sleep() calls in the code for 3.0.101
you may instead see the state of TIME_OK when switching between
STA_INS / STA_DEL.

All this is completely normal and expected behaviour, since both
return the current state, however I agree it is not intuitive.

I would recommend calling adjtimex without a mode to get the actual
state of the system after waiting a second.

At the same time, William suggests the patch in the attachement.

Petr

=============== adjt.c ================

void adjtm(int);

int main()
{
    printf("Insert LS\n");
    adjtm(STA_INS);

    sleep(2);
    printf("\nDelete LS\n");
    adjtm(STA_DEL);

    sleep(2);
    printf("\nInsert LS again\n");
    adjtm(STA_INS);

    sleep(2);
    printf("\nDelete LS again\n");
    adjtm(STA_DEL);

    printf("\nRestoring initial state\n");
    adjtm(0);
}

/*************************************/
/*  Modify LS flag and print status  */
/*************************************/
void adjtm(int ls)
{
    struct timex tx;
    int rc;

    tx.modes = ADJ_STATUS;
    tx.status = ls;

    if ((rc = adjtimex(&tx)) == -1) {
	perror("adjtimex()");
	exit(1);
    }

    sleep(2);

    tx.modes = 0;
    rc = adjtimex(&tx);

    printf("adjtimex() return code %d: ", rc);
    switch (rc) {
    case TIME_OK:
	printf("clock synchronized (TIME_OK)\n");
	break;

    case TIME_INS:
	printf("insert leap second (TIME_INS)\n");
	break;

    case TIME_DEL:
	printf("delete leap second (TIME_DEL)\n");
	break;

    case TIME_OOP:
	printf("leap second in progress (TIME_OOP)\n");
	break;

    case TIME_WAIT:
	printf("leap second has occurred (TIME_WAIT)\n");
	break;

    case TIME_BAD:
	printf("clock not synchronized (TIME_BAD)\n");
	break;

    default:
	printf("Unknown return code: %i\n", rc);
	break;

    }

    printf("Kernel leap second flag: ");
    if (tx.status & STA_INS)
	printf("STA_INS\n");
    else if (tx.status & STA_DEL)
	printf("STA_DEL\n");
    else
	printf("not set\n");
}
=======================================

Reported-by: Petr Gajdos <pgajdos@suse.cz>
Signed-off-by: Michael Kerrisk <mtk.manpages@gmail.com>
This commit is contained in:
William Preston 2015-01-19 15:39:52 +01:00 committed by Michael Kerrisk
parent e3548e1d64
commit f3910b4712
1 changed files with 5 additions and 0 deletions

View File

@ -362,6 +362,11 @@ The symbolic name
is a synonym for
.BR TIME_ERROR ,
provided for backward compatibility.
Note that starting with Linux 3.4,
.\" commit 6b43ae8a619d17c4935c3320d2ef9e92bdeed05d changed to asynchronous
.\" operation, so we can no longer rely on the return code.
the call operates asynchronously and the return value usually will
not reflect a state change caused by the call itself.
.PP
On failure,
.BR adjtimex ()