2222#include <shell.h>
2323#include <asm/guest/vmcs.h>
2424#include <asm/host_pm.h>
25+ #include <asm/tsc.h>
2526
2627#define TEMP_STR_SIZE 60U
2728#define MAX_STR_SIZE 256U
2829#define SHELL_PROMPT_STR "ACRN:\\>"
2930
30- #define SHELL_LOG_BUF_SIZE (PAGE_SIZE * MAX_PCPU_NUM / 2U )
31+ #define SHELL_LOG_BUF_SIZE (PAGE_SIZE * MAX_PCPU_NUM)
3132static char shell_log_buf [SHELL_LOG_BUF_SIZE ];
3233
3334/* Input Line Other - Switch to the "other" input line (there are only two
@@ -52,6 +53,7 @@ static int32_t shell_cpuid(int32_t argc, char **argv);
5253static int32_t shell_reboot (int32_t argc , char * * argv );
5354static int32_t shell_rdmsr (int32_t argc , char * * argv );
5455static int32_t shell_wrmsr (int32_t argc , char * * argv );
56+ static int32_t shell_show_vmexit_profile (__unused int argc , __unused char * * argv );
5557
5658static struct shell_cmd shell_cmds [] = {
5759 {
@@ -156,6 +158,12 @@ static struct shell_cmd shell_cmds[] = {
156158 .help_str = SHELL_CMD_WRMSR_HELP ,
157159 .fcn = shell_wrmsr ,
158160 },
161+ {
162+ .str = SHELL_CMD_VMEXIT ,
163+ .cmd_param = SHELL_CMD_VMEXIT_PARAM ,
164+ .help_str = SHELL_CMD_VMEXIT_HELP ,
165+ .fcn = shell_show_vmexit_profile ,
166+ },
159167};
160168
161169/* The initial log level*/
@@ -1447,3 +1455,317 @@ static int32_t shell_wrmsr(int32_t argc, char **argv)
14471455
14481456 return ret ;
14491457}
1458+ static const char * level_info [MAX_VMEXIT_LEVEL ] = {
1459+ " 0us - 2us" ,
1460+ " 2us - 4us" ,
1461+ " 4us - 8us" ,
1462+ " 8us - 16us" ,
1463+ " 16us - 32us" ,
1464+ " 32us - 64us" ,
1465+ " 64us - 128us" ,
1466+ " 128us - 256us" ,
1467+ " 256us - 512us" ,
1468+ " 512us -1024us" ,
1469+ "1024us -2048us" ,
1470+ "2048us -4096us" ,
1471+ "4096us -8192us" ,
1472+ "8192us - more" ,
1473+ };
1474+
1475+ /* if target_vm_id valid, just check this vm or check all VMs; if count is 0, just ignore it */
1476+ static bool check_vmexit_count_per_reason (uint16_t target_vm_id , uint16_t reason , uint16_t level )
1477+ {
1478+ bool has_vmexit = false;
1479+ uint16_t vm_id , vcpu_id ;
1480+ struct acrn_vm * vm ;
1481+ struct acrn_vcpu * vcpu ;
1482+
1483+ if (target_vm_id != CONFIG_MAX_VM_NUM ) {
1484+ vm = get_vm_from_vmid (target_vm_id );
1485+ foreach_vcpu (vcpu_id , vm , vcpu ) {
1486+ if (vcpu -> vmexit_cnt [reason ][level ] != 0 ) {
1487+ has_vmexit = true;
1488+ break ;
1489+ }
1490+ }
1491+ } else {
1492+ for (vm_id = 0U ; vm_id < CONFIG_MAX_VM_NUM ; vm_id ++ ) {
1493+ vm = get_vm_from_vmid (vm_id );
1494+ if (is_poweroff_vm (vm ))
1495+ continue ;
1496+
1497+ foreach_vcpu (vcpu_id , vm , vcpu ) {
1498+ if (vcpu -> vmexit_cnt [reason ][level ] != 0 ) {
1499+ has_vmexit = true;
1500+ break ;
1501+ }
1502+ }
1503+
1504+ if (has_vmexit )
1505+ break ;
1506+ }
1507+ }
1508+
1509+ return has_vmexit ;
1510+ }
1511+
1512+ /* if target_vm_id valid, just output this vm or output all VMs */
1513+ static bool output_id_per_vm_vcpu (char * * str_arg , size_t * size_max , uint16_t target_vm_id )
1514+ {
1515+ char * str = * str_arg ;
1516+ size_t len , size = * size_max ;
1517+ uint16_t vm_id , vcpu_id ;
1518+ struct acrn_vm * vm ;
1519+ struct acrn_vcpu * vcpu ;
1520+
1521+ if (target_vm_id != CONFIG_MAX_VM_NUM ) {
1522+ vm = get_vm_from_vmid (target_vm_id );
1523+ foreach_vcpu (vcpu_id , vm , vcpu ) {
1524+ len = snprintf (str , size , " VM%u/vCPU%u" , target_vm_id , vcpu_id );
1525+ if (len >= size ) {
1526+ return false;
1527+ }
1528+ size -= len ;
1529+ str += len ;
1530+ }
1531+ } else {
1532+ for (vm_id = 0U ; vm_id < CONFIG_MAX_VM_NUM ; vm_id ++ ) {
1533+ vm = get_vm_from_vmid (vm_id );
1534+ if (is_poweroff_vm (vm ))
1535+ continue ;
1536+
1537+ foreach_vcpu (vcpu_id , vm , vcpu ) {
1538+ len = snprintf (str , size , " VM%u/vCPU%u" , vm_id , vcpu_id );
1539+ if (len >= size ) {
1540+ return false;
1541+ }
1542+ size -= len ;
1543+ str += len ;
1544+ }
1545+ }
1546+ }
1547+
1548+ * str_arg = str ;
1549+ * size_max = size ;
1550+ return true;
1551+ }
1552+
1553+ static bool output_vmexit_info_per_vcpu (char * * str_arg , size_t * size_max , uint16_t target_vm_id ,
1554+ uint16_t reason , uint16_t level )
1555+ {
1556+ char * str = * str_arg ;
1557+ size_t len , size = * size_max ;
1558+ uint16_t vm_id , vcpu_id ;
1559+ struct acrn_vm * vm ;
1560+ struct acrn_vcpu * vcpu ;
1561+
1562+ if (target_vm_id != CONFIG_MAX_VM_NUM ) {
1563+ vm = get_vm_from_vmid (target_vm_id );
1564+
1565+ foreach_vcpu (vcpu_id , vm , vcpu ) {
1566+ /* if level is MAX, output the max time */
1567+ if (level == MAX_VMEXIT_LEVEL ) {
1568+ len = snprintf (str , size , "%12lld" , vcpu -> vmexit_time [reason ]);
1569+ } else {
1570+ len = snprintf (str , size , "%12lld" , vcpu -> vmexit_cnt [reason ][level ]);
1571+ }
1572+
1573+ if (len >= size ) {
1574+ return false;
1575+ }
1576+
1577+ size -= len ;
1578+ str += len ;
1579+ }
1580+ } else {
1581+ for (vm_id = 0U ; vm_id < CONFIG_MAX_VM_NUM ; vm_id ++ ) {
1582+ vm = get_vm_from_vmid (vm_id );
1583+ if (is_poweroff_vm (vm ))
1584+ continue ;
1585+
1586+ foreach_vcpu (vcpu_id , vm , vcpu ) {
1587+ /* if level is MAX, output the max time */
1588+ if (level == MAX_VMEXIT_LEVEL ) {
1589+ len = snprintf (str , size , "%12lld" , vcpu -> vmexit_time [reason ]);
1590+ } else {
1591+ len = snprintf (str , size , "%12lld" , vcpu -> vmexit_cnt [reason ][level ]);
1592+ }
1593+
1594+ if (len >= size ) {
1595+ return false;
1596+ }
1597+
1598+ size -= len ;
1599+ str += len ;
1600+ }
1601+ }
1602+ }
1603+
1604+ * str_arg = str ;
1605+ * size_max = size ;
1606+ return true;
1607+ }
1608+
1609+ static void get_vmexit_details_per_vcpu (char * str_arg , size_t str_max , uint16_t target_vm_id )
1610+ {
1611+ char * str = str_arg ;
1612+ uint16_t reason , level ;
1613+ size_t len , size = str_max ;
1614+
1615+ len = snprintf (str , size , "\r\nNow=%lldus, for detailed latency of each vmexit on %s VM/vCPU:" ,
1616+ ticks_to_us (rdtsc ()), (target_vm_id == CONFIG_MAX_VM_NUM ) ? "each" : "one" );
1617+ if (len >= size ) {
1618+ goto overflow ;
1619+ }
1620+ size -= len ;
1621+ str += len ;
1622+
1623+ for (reason = 0U ; reason < NR_VMX_EXIT_REASONS ; reason ++ ) {
1624+
1625+ if (!check_vmexit_count_per_reason (target_vm_id , reason , TOTAL_ARRAY_LEVEL - 1 ))
1626+ continue ;
1627+
1628+ len = snprintf (str , size , "\r\n\r\n VMEXIT/0x%02x" , reason );
1629+ if (len >= size ) {
1630+ goto overflow ;
1631+ }
1632+ size -= len ;
1633+ str += len ;
1634+
1635+ if (!output_id_per_vm_vcpu (& str , & size , target_vm_id )) {
1636+ goto overflow ;
1637+ }
1638+
1639+ for (level = 0 ; level < MAX_VMEXIT_LEVEL ; level ++ ) {
1640+
1641+ if (!check_vmexit_count_per_reason (target_vm_id , reason , level )) {
1642+ continue ;
1643+ }
1644+
1645+ len = snprintf (str , size , "\r\n%s" , level_info [level ]);
1646+ if (len >= size ) {
1647+ goto overflow ;
1648+ }
1649+ size -= len ;
1650+ str += len ;
1651+
1652+ if (!output_vmexit_info_per_vcpu (& str , & size , target_vm_id , reason , level )) {
1653+ goto overflow ;
1654+ }
1655+ }
1656+
1657+ len = snprintf (str , size , "\r\n Max Lat(us):" );
1658+ if (len >= size ) {
1659+ goto overflow ;
1660+ }
1661+ size -= len ;
1662+ str += len ;
1663+
1664+ if (!output_vmexit_info_per_vcpu (& str , & size , target_vm_id , reason , MAX_VMEXIT_LEVEL )) {
1665+ goto overflow ;
1666+ }
1667+ }
1668+
1669+ snprintf (str , size , "\r\n" );
1670+ return ;
1671+
1672+ overflow :
1673+ printf ("buffer size could not be enough! please check!\n" );
1674+ }
1675+
1676+ static void clear_vmexit_info_buffer (void )
1677+ {
1678+ uint16_t vm_id , vcpu_id ;
1679+ struct acrn_vm * vm ;
1680+ struct acrn_vcpu * vcpu ;
1681+
1682+ for (vm_id = 0U ; vm_id < CONFIG_MAX_VM_NUM ; vm_id ++ ) {
1683+ vm = get_vm_from_vmid (vm_id );
1684+ if (!is_poweroff_vm (vm )) {
1685+ foreach_vcpu (vcpu_id , vm , vcpu ) {
1686+ memset (vcpu -> vmexit_cnt , 0 , sizeof (vcpu -> vmexit_cnt ));
1687+ memset (vcpu -> vmexit_time , 0 , sizeof (vcpu -> vmexit_time ));
1688+ }
1689+ }
1690+ }
1691+ }
1692+
1693+ /* used to control vmexit sample function */
1694+ static bool sample_vmexit_enabled = true;
1695+
1696+ static int shell_show_vmexit_profile (__unused int argc , __unused char * * argv )
1697+ {
1698+ uint16_t vm_id = CONFIG_MAX_VM_NUM ;
1699+ bool to_continue = true;
1700+
1701+ if (argc == 2 ) {
1702+ if (strcmp (argv [1 ], "clear" ) == 0 ) {
1703+ clear_vmexit_info_buffer ();
1704+ } else if (strcmp (argv [1 ], "enable" ) == 0 ) {
1705+ shell_puts ("\tenable vmexit sample!\n" );
1706+ sample_vmexit_enabled = true;
1707+ to_continue = false;
1708+ } else if (strcmp (argv [1 ], "disable" ) == 0 ) {
1709+ shell_puts ("\tdisable vmexit sample!\n" );
1710+ sample_vmexit_enabled = false;
1711+ to_continue = false;
1712+ } else {
1713+ vm_id = (uint16_t )strtol_deci (argv [1 ]);
1714+ if ((vm_id >= CONFIG_MAX_VM_NUM ) || is_poweroff_vm (get_vm_from_vmid (vm_id ))) {
1715+ shell_puts ("\tThis vm_id not existed or active! please check!\n" );
1716+ to_continue = false;
1717+ }
1718+ }
1719+ }
1720+
1721+ if (to_continue && !sample_vmexit_enabled ) {
1722+ shell_puts ("\tvmexit sample is disabled!\n" );
1723+ to_continue = false;
1724+ }
1725+
1726+ if (to_continue ) {
1727+ get_vmexit_details_per_vcpu (shell_log_buf , SHELL_LOG_BUF_SIZE , vm_id );
1728+ shell_puts (shell_log_buf );
1729+ }
1730+
1731+ return 0 ;
1732+ }
1733+
1734+ /* to sample vmexit tsc data */
1735+ void sample_vmexit_end (struct acrn_vcpu * vcpu )
1736+ {
1737+ uint32_t basic_exit_reason = vcpu -> arch .exit_reason & 0xFFFFUL ;
1738+
1739+ if (!sample_vmexit_enabled )
1740+ return ;
1741+
1742+ if (vcpu -> vmexit_begin != 0UL ) {
1743+ uint64_t delta = rdtsc () - vcpu -> vmexit_begin ;
1744+ uint32_t us = (uint32_t )ticks_to_us (delta );
1745+ uint16_t fls = (uint16_t )(fls32 (us ) + 1 ); /* to avoid us = 0 case, then fls=0xFFFF */
1746+ uint16_t index = 0 ;
1747+
1748+ if (fls >= MAX_VMEXIT_LEVEL ) {
1749+ index = MAX_VMEXIT_LEVEL - 1 ;
1750+ } else if (fls > 0 ) { /* if fls == 0, it means the us = 0 */
1751+ index = fls - 1 ;
1752+ }
1753+
1754+ vcpu -> vmexit_cnt [basic_exit_reason ][index ]++ ;
1755+
1756+ if (us > vcpu -> vmexit_time [basic_exit_reason ]) {
1757+ vcpu -> vmexit_time [basic_exit_reason ] = us ;
1758+ }
1759+ }
1760+ }
1761+
1762+ void sample_vmexit_begin (struct acrn_vcpu * vcpu )
1763+ {
1764+ uint32_t basic_exit_reason = vcpu -> arch .exit_reason & 0xFFFFUL ;
1765+
1766+ if (!sample_vmexit_enabled )
1767+ return ;
1768+
1769+ vcpu -> vmexit_begin = rdtsc ();
1770+ vcpu -> vmexit_cnt [basic_exit_reason ][TOTAL_ARRAY_LEVEL - 1 ]++ ;
1771+ }
0 commit comments