11// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
22// SPDX-License-Identifier: Apache-2.0
33
4- #include <errno.h>
54#include <stdatomic.h>
65#include <stdint.h>
76#include <stdio.h>
87#include <stdlib.h>
98#include <string.h>
9+ #include <sys/epoll.h>
1010#include <sys/stat.h>
1111#include <fcntl.h>
1212#include <sys/mman.h>
1616
1717const char * VMCLOCK_DEV_PATH = "/dev/vmclock0" ;
1818
19- int get_vmclock_handle ( struct vmclock_abi * * vmclock )
19+ int open_vmclock ( void )
2020{
2121 int fd = open (VMCLOCK_DEV_PATH , 0 );
22- if (fd == -1 )
23- goto out_err ;
22+ if (fd == -1 ) {
23+ perror ("open" );
24+ exit (1 );
25+ }
2426
25- void * ptr = mmap (NULL , sizeof (struct vmclock_abi ), PROT_READ , MAP_SHARED , fd , 0 );
26- if (ptr == MAP_FAILED )
27- goto out_err_mmap ;
27+ return fd ;
28+ }
2829
29- * vmclock = ptr ;
30- return 0 ;
30+ struct vmclock_abi * get_vmclock_handle (int fd )
31+ {
32+ void * ptr = mmap (NULL , sizeof (struct vmclock_abi ), PROT_READ , MAP_SHARED , fd , 0 );
33+ if (ptr == MAP_FAILED ) {
34+ perror ("mmap" );
35+ exit (1 );
36+ }
3137
32- out_err_mmap :
33- close (fd );
34- out_err :
35- return errno ;
38+ return ptr ;
3639}
3740
3841#define READ_VMCLOCK_FIELD_FN (type , field ) \
@@ -56,23 +59,136 @@ type read##_##field (struct vmclock_abi *vmclock) { \
5659}
5760
5861READ_VMCLOCK_FIELD_FN (uint64_t , disruption_marker );
62+ READ_VMCLOCK_FIELD_FN (uint64_t , vm_generation_counter );
5963
60- int main ()
64+ /*
65+ * Read `vmclock_abi` structure using a file descriptor pointing to
66+ * `/dev/vmclock0`.
67+ */
68+ void read_vmclock (int fd , struct vmclock_abi * vmclock )
6169{
62- struct vmclock_abi * vmclock ;
70+ int ret ;
6371
64- int err = get_vmclock_handle (& vmclock );
65- if (err ) {
66- printf ("Could not mmap vmclock struct: %s\n" , strerror (err ));
72+ /*
73+ * Use `pread()`, since the device doesn't implement lseek(), so
74+ * we can't reset `fp`.
75+ */
76+ ret = pread (fd , vmclock , sizeof (* vmclock ), 0 );
77+ if (ret < 0 ) {
78+ perror ("read" );
79+ exit (1 );
80+ } else if (ret < (int ) sizeof (* vmclock )) {
81+ fprintf (stderr , "We don't handle partial writes (%d). Exiting!\n" , ret );
6782 exit (1 );
6883 }
84+ }
85+
86+ void print_vmclock (struct vmclock_abi * vmclock )
87+ {
88+ if (vmclock -> flags & VMCLOCK_FLAG_VM_GEN_COUNTER_PRESENT ) {
89+ printf ("VMCLOCK_FLAG_VM_GEN_COUNTER_PRESENT: true\n" );
90+ } else {
91+ printf ("VMCLOCK_FLAG_VM_GEN_COUNTER_PRESENT: false\n" );
92+ }
93+
94+ if (vmclock -> flags & VMCLOCK_FLAG_NOTIFICATION_PRESENT ) {
95+ printf ("VMCLOCK_FLAG_NOTIFICATION_PRESENT: true\n" );
96+ } else {
97+ printf ("VMCLOCK_FLAG_NOTIFICATION_PRESENT: false\n" );
98+ }
6999
70100 printf ("VMCLOCK_MAGIC: 0x%x\n" , vmclock -> magic );
71101 printf ("VMCLOCK_SIZE: 0x%x\n" , vmclock -> size );
72102 printf ("VMCLOCK_VERSION: %u\n" , vmclock -> version );
73103 printf ("VMCLOCK_CLOCK_STATUS: %u\n" , vmclock -> clock_status );
74104 printf ("VMCLOCK_COUNTER_ID: %u\n" , vmclock -> counter_id );
75105 printf ("VMCLOCK_DISRUPTION_MARKER: %lu\n" , read_disruption_marker (vmclock ));
106+ printf ("VMCLOCK_VM_GENERATION_COUNTER: %lu\n" , read_vm_generation_counter (vmclock ));
107+ fflush (stdout );
108+ }
109+
110+ void run_poll (int fd )
111+ {
112+ struct vmclock_abi vmclock ;
113+ int epfd , ret , nfds ;
114+ struct epoll_event ev ;
115+
116+ read_vmclock (fd , & vmclock );
117+ print_vmclock (& vmclock );
118+
119+ epfd = epoll_create (1 );
120+ if (epfd < 0 ) {
121+ perror ("epoll_create" );
122+ exit (1 );
123+ }
124+
125+ ev .events = EPOLLIN | EPOLLRDNORM ;
126+ ev .data .fd = fd ;
127+ ret = epoll_ctl (epfd , EPOLL_CTL_ADD , fd , & ev );
128+ if (ret < 0 ) {
129+ perror ("epoll_add" );
130+ exit (1 );
131+ }
132+
133+ while (1 ) {
134+ nfds = epoll_wait (epfd , & ev , 1 , -1 );
135+ if (nfds < 0 ) {
136+ perror ("epoll_wait" );
137+ exit (1 );
138+ }
139+
140+ if (ev .data .fd != fd ) {
141+ fprintf (stderr , "Unknown file descriptor %d\n" , ev .data .fd );
142+ exit (1 );
143+ }
144+
145+ if (ev .events & EPOLLHUP ) {
146+ fprintf (stderr , "Device does not support notifications. Stop polling\n" );
147+ exit (1 );
148+ } else if (ev .events & EPOLLIN ) {
149+ fprintf (stdout , "Got VMClock notification\n" );
150+ read_vmclock (fd , & vmclock );
151+ print_vmclock (& vmclock );
152+ }
153+ }
154+ }
155+
156+ void print_help_message ()
157+ {
158+ fprintf (stderr , "usage: vmclock MODE\n" );
159+ fprintf (stderr , "Available modes:\n" );
160+ fprintf (stderr , " -r\tRead vmclock_abi using read()\n" );
161+ fprintf (stderr , " -m\tRead vmclock_abi using mmap()\n" );
162+ fprintf (stderr , " -p\tPoll VMClock for changes\n" );
163+ }
164+
165+ int main (int argc , char * argv [])
166+ {
167+ int fd ;
168+ struct vmclock_abi vmclock , * vmclock_ptr ;
169+
170+ if (argc != 2 ) {
171+ print_help_message ();
172+ exit (1 );
173+ }
174+
175+ fd = open_vmclock ();
176+
177+ if (!strncmp (argv [1 ], "-r" , 3 )) {
178+ printf ("Reading VMClock with read()\n" );
179+ read_vmclock (fd , & vmclock );
180+ print_vmclock (& vmclock );
181+ } else if (!strncmp (argv [1 ], "-m" , 3 )) {
182+ printf ("Reading VMClock with mmap()\n" );
183+ vmclock_ptr = get_vmclock_handle (fd );
184+ print_vmclock (vmclock_ptr );
185+ } else if (!strncmp (argv [1 ], "-p" , 3 )) {
186+ printf ("Polling VMClock\n" );
187+ run_poll (fd );
188+ } else {
189+ print_help_message ();
190+ exit (1 );
191+ }
76192
77193 return 0 ;
78194}
0 commit comments