Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Basic resctrl file system operations
0004  *
0005  * Copyright (C) 2018 Intel Corporation
0006  *
0007  * Authors:
0008  *    Sai Praneeth Prakhya <sai.praneeth.prakhya@intel.com>,
0009  *    Fenghua Yu <fenghua.yu@intel.com>
0010  */
0011 #include "resctrl.h"
0012 
0013 static int find_resctrl_mount(char *buffer)
0014 {
0015     FILE *mounts;
0016     char line[256], *fs, *mntpoint;
0017 
0018     mounts = fopen("/proc/mounts", "r");
0019     if (!mounts) {
0020         perror("/proc/mounts");
0021         return -ENXIO;
0022     }
0023     while (!feof(mounts)) {
0024         if (!fgets(line, 256, mounts))
0025             break;
0026         fs = strtok(line, " \t");
0027         if (!fs)
0028             continue;
0029         mntpoint = strtok(NULL, " \t");
0030         if (!mntpoint)
0031             continue;
0032         fs = strtok(NULL, " \t");
0033         if (!fs)
0034             continue;
0035         if (strcmp(fs, "resctrl"))
0036             continue;
0037 
0038         fclose(mounts);
0039         if (buffer)
0040             strncpy(buffer, mntpoint, 256);
0041 
0042         return 0;
0043     }
0044 
0045     fclose(mounts);
0046 
0047     return -ENOENT;
0048 }
0049 
0050 /*
0051  * remount_resctrlfs - Remount resctrl FS at /sys/fs/resctrl
0052  * @mum_resctrlfs:  Should the resctrl FS be remounted?
0053  *
0054  * If not mounted, mount it.
0055  * If mounted and mum_resctrlfs then remount resctrl FS.
0056  * If mounted and !mum_resctrlfs then noop
0057  *
0058  * Return: 0 on success, non-zero on failure
0059  */
0060 int remount_resctrlfs(bool mum_resctrlfs)
0061 {
0062     char mountpoint[256];
0063     int ret;
0064 
0065     ret = find_resctrl_mount(mountpoint);
0066     if (ret)
0067         strcpy(mountpoint, RESCTRL_PATH);
0068 
0069     if (!ret && mum_resctrlfs && umount(mountpoint))
0070         ksft_print_msg("Fail: unmounting \"%s\"\n", mountpoint);
0071 
0072     if (!ret && !mum_resctrlfs)
0073         return 0;
0074 
0075     ksft_print_msg("Mounting resctrl to \"%s\"\n", RESCTRL_PATH);
0076     ret = mount("resctrl", RESCTRL_PATH, "resctrl", 0, NULL);
0077     if (ret)
0078         perror("# mount");
0079 
0080     return ret;
0081 }
0082 
0083 int umount_resctrlfs(void)
0084 {
0085     if (find_resctrl_mount(NULL))
0086         return 0;
0087 
0088     if (umount(RESCTRL_PATH)) {
0089         perror("# Unable to umount resctrl");
0090 
0091         return errno;
0092     }
0093 
0094     return 0;
0095 }
0096 
0097 /*
0098  * get_resource_id - Get socket number/l3 id for a specified CPU
0099  * @cpu_no: CPU number
0100  * @resource_id: Socket number or l3_id
0101  *
0102  * Return: >= 0 on success, < 0 on failure.
0103  */
0104 int get_resource_id(int cpu_no, int *resource_id)
0105 {
0106     char phys_pkg_path[1024];
0107     FILE *fp;
0108 
0109     if (get_vendor() == ARCH_AMD)
0110         sprintf(phys_pkg_path, "%s%d/cache/index3/id",
0111             PHYS_ID_PATH, cpu_no);
0112     else
0113         sprintf(phys_pkg_path, "%s%d/topology/physical_package_id",
0114             PHYS_ID_PATH, cpu_no);
0115 
0116     fp = fopen(phys_pkg_path, "r");
0117     if (!fp) {
0118         perror("Failed to open physical_package_id");
0119 
0120         return -1;
0121     }
0122     if (fscanf(fp, "%d", resource_id) <= 0) {
0123         perror("Could not get socket number or l3 id");
0124         fclose(fp);
0125 
0126         return -1;
0127     }
0128     fclose(fp);
0129 
0130     return 0;
0131 }
0132 
0133 /*
0134  * get_cache_size - Get cache size for a specified CPU
0135  * @cpu_no: CPU number
0136  * @cache_type: Cache level L2/L3
0137  * @cache_size: pointer to cache_size
0138  *
0139  * Return: = 0 on success, < 0 on failure.
0140  */
0141 int get_cache_size(int cpu_no, char *cache_type, unsigned long *cache_size)
0142 {
0143     char cache_path[1024], cache_str[64];
0144     int length, i, cache_num;
0145     FILE *fp;
0146 
0147     if (!strcmp(cache_type, "L3")) {
0148         cache_num = 3;
0149     } else if (!strcmp(cache_type, "L2")) {
0150         cache_num = 2;
0151     } else {
0152         perror("Invalid cache level");
0153         return -1;
0154     }
0155 
0156     sprintf(cache_path, "/sys/bus/cpu/devices/cpu%d/cache/index%d/size",
0157         cpu_no, cache_num);
0158     fp = fopen(cache_path, "r");
0159     if (!fp) {
0160         perror("Failed to open cache size");
0161 
0162         return -1;
0163     }
0164     if (fscanf(fp, "%s", cache_str) <= 0) {
0165         perror("Could not get cache_size");
0166         fclose(fp);
0167 
0168         return -1;
0169     }
0170     fclose(fp);
0171 
0172     length = (int)strlen(cache_str);
0173 
0174     *cache_size = 0;
0175 
0176     for (i = 0; i < length; i++) {
0177         if ((cache_str[i] >= '0') && (cache_str[i] <= '9'))
0178 
0179             *cache_size = *cache_size * 10 + (cache_str[i] - '0');
0180 
0181         else if (cache_str[i] == 'K')
0182 
0183             *cache_size = *cache_size * 1024;
0184 
0185         else if (cache_str[i] == 'M')
0186 
0187             *cache_size = *cache_size * 1024 * 1024;
0188 
0189         else
0190             break;
0191     }
0192 
0193     return 0;
0194 }
0195 
0196 #define CORE_SIBLINGS_PATH  "/sys/bus/cpu/devices/cpu"
0197 
0198 /*
0199  * get_cbm_mask - Get cbm mask for given cache
0200  * @cache_type: Cache level L2/L3
0201  * @cbm_mask:   cbm_mask returned as a string
0202  *
0203  * Return: = 0 on success, < 0 on failure.
0204  */
0205 int get_cbm_mask(char *cache_type, char *cbm_mask)
0206 {
0207     char cbm_mask_path[1024];
0208     FILE *fp;
0209 
0210     if (!cbm_mask)
0211         return -1;
0212 
0213     sprintf(cbm_mask_path, "%s/%s/cbm_mask", CBM_MASK_PATH, cache_type);
0214 
0215     fp = fopen(cbm_mask_path, "r");
0216     if (!fp) {
0217         perror("Failed to open cache level");
0218 
0219         return -1;
0220     }
0221     if (fscanf(fp, "%s", cbm_mask) <= 0) {
0222         perror("Could not get max cbm_mask");
0223         fclose(fp);
0224 
0225         return -1;
0226     }
0227     fclose(fp);
0228 
0229     return 0;
0230 }
0231 
0232 /*
0233  * get_core_sibling - Get sibling core id from the same socket for given CPU
0234  * @cpu_no: CPU number
0235  *
0236  * Return:  > 0 on success, < 0 on failure.
0237  */
0238 int get_core_sibling(int cpu_no)
0239 {
0240     char core_siblings_path[1024], cpu_list_str[64];
0241     int sibling_cpu_no = -1;
0242     FILE *fp;
0243 
0244     sprintf(core_siblings_path, "%s%d/topology/core_siblings_list",
0245         CORE_SIBLINGS_PATH, cpu_no);
0246 
0247     fp = fopen(core_siblings_path, "r");
0248     if (!fp) {
0249         perror("Failed to open core siblings path");
0250 
0251         return -1;
0252     }
0253     if (fscanf(fp, "%s", cpu_list_str) <= 0) {
0254         perror("Could not get core_siblings list");
0255         fclose(fp);
0256 
0257         return -1;
0258     }
0259     fclose(fp);
0260 
0261     char *token = strtok(cpu_list_str, "-,");
0262 
0263     while (token) {
0264         sibling_cpu_no = atoi(token);
0265         /* Skipping core 0 as we don't want to run test on core 0 */
0266         if (sibling_cpu_no != 0 && sibling_cpu_no != cpu_no)
0267             break;
0268         token = strtok(NULL, "-,");
0269     }
0270 
0271     return sibling_cpu_no;
0272 }
0273 
0274 /*
0275  * taskset_benchmark - Taskset PID (i.e. benchmark) to a specified cpu
0276  * @bm_pid: PID that should be binded
0277  * @cpu_no: CPU number at which the PID would be binded
0278  *
0279  * Return: 0 on success, non-zero on failure
0280  */
0281 int taskset_benchmark(pid_t bm_pid, int cpu_no)
0282 {
0283     cpu_set_t my_set;
0284 
0285     CPU_ZERO(&my_set);
0286     CPU_SET(cpu_no, &my_set);
0287 
0288     if (sched_setaffinity(bm_pid, sizeof(cpu_set_t), &my_set)) {
0289         perror("Unable to taskset benchmark");
0290 
0291         return -1;
0292     }
0293 
0294     return 0;
0295 }
0296 
0297 /*
0298  * run_benchmark - Run a specified benchmark or fill_buf (default benchmark)
0299  *         in specified signal. Direct benchmark stdio to /dev/null.
0300  * @signum: signal number
0301  * @info:   signal info
0302  * @ucontext:   user context in signal handling
0303  *
0304  * Return: void
0305  */
0306 void run_benchmark(int signum, siginfo_t *info, void *ucontext)
0307 {
0308     int operation, ret, malloc_and_init_memory, memflush;
0309     unsigned long span, buffer_span;
0310     char **benchmark_cmd;
0311     char resctrl_val[64];
0312     FILE *fp;
0313 
0314     benchmark_cmd = info->si_ptr;
0315 
0316     /*
0317      * Direct stdio of child to /dev/null, so that only parent writes to
0318      * stdio (console)
0319      */
0320     fp = freopen("/dev/null", "w", stdout);
0321     if (!fp)
0322         PARENT_EXIT("Unable to direct benchmark status to /dev/null");
0323 
0324     if (strcmp(benchmark_cmd[0], "fill_buf") == 0) {
0325         /* Execute default fill_buf benchmark */
0326         span = strtoul(benchmark_cmd[1], NULL, 10);
0327         malloc_and_init_memory = atoi(benchmark_cmd[2]);
0328         memflush =  atoi(benchmark_cmd[3]);
0329         operation = atoi(benchmark_cmd[4]);
0330         sprintf(resctrl_val, "%s", benchmark_cmd[5]);
0331 
0332         if (strncmp(resctrl_val, CMT_STR, sizeof(CMT_STR)))
0333             buffer_span = span * MB;
0334         else
0335             buffer_span = span;
0336 
0337         if (run_fill_buf(buffer_span, malloc_and_init_memory, memflush,
0338                  operation, resctrl_val))
0339             fprintf(stderr, "Error in running fill buffer\n");
0340     } else {
0341         /* Execute specified benchmark */
0342         ret = execvp(benchmark_cmd[0], benchmark_cmd);
0343         if (ret)
0344             perror("wrong\n");
0345     }
0346 
0347     fclose(stdout);
0348     PARENT_EXIT("Unable to run specified benchmark");
0349 }
0350 
0351 /*
0352  * create_grp - Create a group only if one doesn't exist
0353  * @grp_name:   Name of the group
0354  * @grp:    Full path and name of the group
0355  * @parent_grp: Full path and name of the parent group
0356  *
0357  * Return: 0 on success, non-zero on failure
0358  */
0359 static int create_grp(const char *grp_name, char *grp, const char *parent_grp)
0360 {
0361     int found_grp = 0;
0362     struct dirent *ep;
0363     DIR *dp;
0364 
0365     /*
0366      * At this point, we are guaranteed to have resctrl FS mounted and if
0367      * length of grp_name == 0, it means, user wants to use root con_mon
0368      * grp, so do nothing
0369      */
0370     if (strlen(grp_name) == 0)
0371         return 0;
0372 
0373     /* Check if requested grp exists or not */
0374     dp = opendir(parent_grp);
0375     if (dp) {
0376         while ((ep = readdir(dp)) != NULL) {
0377             if (strcmp(ep->d_name, grp_name) == 0)
0378                 found_grp = 1;
0379         }
0380         closedir(dp);
0381     } else {
0382         perror("Unable to open resctrl for group");
0383 
0384         return -1;
0385     }
0386 
0387     /* Requested grp doesn't exist, hence create it */
0388     if (found_grp == 0) {
0389         if (mkdir(grp, 0) == -1) {
0390             perror("Unable to create group");
0391 
0392             return -1;
0393         }
0394     }
0395 
0396     return 0;
0397 }
0398 
0399 static int write_pid_to_tasks(char *tasks, pid_t pid)
0400 {
0401     FILE *fp;
0402 
0403     fp = fopen(tasks, "w");
0404     if (!fp) {
0405         perror("Failed to open tasks file");
0406 
0407         return -1;
0408     }
0409     if (fprintf(fp, "%d\n", pid) < 0) {
0410         perror("Failed to wr pid to tasks file");
0411         fclose(fp);
0412 
0413         return -1;
0414     }
0415     fclose(fp);
0416 
0417     return 0;
0418 }
0419 
0420 /*
0421  * write_bm_pid_to_resctrl - Write a PID (i.e. benchmark) to resctrl FS
0422  * @bm_pid:     PID that should be written
0423  * @ctrlgrp:        Name of the control monitor group (con_mon grp)
0424  * @mongrp:     Name of the monitor group (mon grp)
0425  * @resctrl_val:    Resctrl feature (Eg: mbm, mba.. etc)
0426  *
0427  * If a con_mon grp is requested, create it and write pid to it, otherwise
0428  * write pid to root con_mon grp.
0429  * If a mon grp is requested, create it and write pid to it, otherwise
0430  * pid is not written, this means that pid is in con_mon grp and hence
0431  * should consult con_mon grp's mon_data directory for results.
0432  *
0433  * Return: 0 on success, non-zero on failure
0434  */
0435 int write_bm_pid_to_resctrl(pid_t bm_pid, char *ctrlgrp, char *mongrp,
0436                 char *resctrl_val)
0437 {
0438     char controlgroup[128], monitorgroup[512], monitorgroup_p[256];
0439     char tasks[1024];
0440     int ret = 0;
0441 
0442     if (strlen(ctrlgrp))
0443         sprintf(controlgroup, "%s/%s", RESCTRL_PATH, ctrlgrp);
0444     else
0445         sprintf(controlgroup, "%s", RESCTRL_PATH);
0446 
0447     /* Create control and monitoring group and write pid into it */
0448     ret = create_grp(ctrlgrp, controlgroup, RESCTRL_PATH);
0449     if (ret)
0450         goto out;
0451     sprintf(tasks, "%s/tasks", controlgroup);
0452     ret = write_pid_to_tasks(tasks, bm_pid);
0453     if (ret)
0454         goto out;
0455 
0456     /* Create mon grp and write pid into it for "mbm" and "cmt" test */
0457     if (!strncmp(resctrl_val, CMT_STR, sizeof(CMT_STR)) ||
0458         !strncmp(resctrl_val, MBM_STR, sizeof(MBM_STR))) {
0459         if (strlen(mongrp)) {
0460             sprintf(monitorgroup_p, "%s/mon_groups", controlgroup);
0461             sprintf(monitorgroup, "%s/%s", monitorgroup_p, mongrp);
0462             ret = create_grp(mongrp, monitorgroup, monitorgroup_p);
0463             if (ret)
0464                 goto out;
0465 
0466             sprintf(tasks, "%s/mon_groups/%s/tasks",
0467                 controlgroup, mongrp);
0468             ret = write_pid_to_tasks(tasks, bm_pid);
0469             if (ret)
0470                 goto out;
0471         }
0472     }
0473 
0474 out:
0475     ksft_print_msg("Writing benchmark parameters to resctrl FS\n");
0476     if (ret)
0477         perror("# writing to resctrlfs");
0478 
0479     return ret;
0480 }
0481 
0482 /*
0483  * write_schemata - Update schemata of a con_mon grp
0484  * @ctrlgrp:        Name of the con_mon grp
0485  * @schemata:       Schemata that should be updated to
0486  * @cpu_no:     CPU number that the benchmark PID is binded to
0487  * @resctrl_val:    Resctrl feature (Eg: mbm, mba.. etc)
0488  *
0489  * Update schemata of a con_mon grp *only* if requested resctrl feature is
0490  * allocation type
0491  *
0492  * Return: 0 on success, non-zero on failure
0493  */
0494 int write_schemata(char *ctrlgrp, char *schemata, int cpu_no, char *resctrl_val)
0495 {
0496     char controlgroup[1024], schema[1024], reason[64];
0497     int resource_id, ret = 0;
0498     FILE *fp;
0499 
0500     if (strncmp(resctrl_val, MBA_STR, sizeof(MBA_STR)) &&
0501         strncmp(resctrl_val, CAT_STR, sizeof(CAT_STR)) &&
0502         strncmp(resctrl_val, CMT_STR, sizeof(CMT_STR)))
0503         return -ENOENT;
0504 
0505     if (!schemata) {
0506         ksft_print_msg("Skipping empty schemata update\n");
0507 
0508         return -1;
0509     }
0510 
0511     if (get_resource_id(cpu_no, &resource_id) < 0) {
0512         sprintf(reason, "Failed to get resource id");
0513         ret = -1;
0514 
0515         goto out;
0516     }
0517 
0518     if (strlen(ctrlgrp) != 0)
0519         sprintf(controlgroup, "%s/%s/schemata", RESCTRL_PATH, ctrlgrp);
0520     else
0521         sprintf(controlgroup, "%s/schemata", RESCTRL_PATH);
0522 
0523     if (!strncmp(resctrl_val, CAT_STR, sizeof(CAT_STR)) ||
0524         !strncmp(resctrl_val, CMT_STR, sizeof(CMT_STR)))
0525         sprintf(schema, "%s%d%c%s", "L3:", resource_id, '=', schemata);
0526     if (!strncmp(resctrl_val, MBA_STR, sizeof(MBA_STR)))
0527         sprintf(schema, "%s%d%c%s", "MB:", resource_id, '=', schemata);
0528 
0529     fp = fopen(controlgroup, "w");
0530     if (!fp) {
0531         sprintf(reason, "Failed to open control group");
0532         ret = -1;
0533 
0534         goto out;
0535     }
0536 
0537     if (fprintf(fp, "%s\n", schema) < 0) {
0538         sprintf(reason, "Failed to write schemata in control group");
0539         fclose(fp);
0540         ret = -1;
0541 
0542         goto out;
0543     }
0544     fclose(fp);
0545 
0546 out:
0547     ksft_print_msg("Write schema \"%s\" to resctrl FS%s%s\n",
0548                schema, ret ? " # " : "",
0549                ret ? reason : "");
0550 
0551     return ret;
0552 }
0553 
0554 bool check_resctrlfs_support(void)
0555 {
0556     FILE *inf = fopen("/proc/filesystems", "r");
0557     DIR *dp;
0558     char *res;
0559     bool ret = false;
0560 
0561     if (!inf)
0562         return false;
0563 
0564     res = fgrep(inf, "nodev\tresctrl\n");
0565 
0566     if (res) {
0567         ret = true;
0568         free(res);
0569     }
0570 
0571     fclose(inf);
0572 
0573     ksft_print_msg("%s Check kernel supports resctrl filesystem\n",
0574                ret ? "Pass:" : "Fail:");
0575 
0576     if (!ret)
0577         return ret;
0578 
0579     dp = opendir(RESCTRL_PATH);
0580     ksft_print_msg("%s Check resctrl mountpoint \"%s\" exists\n",
0581                dp ? "Pass:" : "Fail:", RESCTRL_PATH);
0582     if (dp)
0583         closedir(dp);
0584 
0585     ksft_print_msg("resctrl filesystem %s mounted\n",
0586                find_resctrl_mount(NULL) ? "not" : "is");
0587 
0588     return ret;
0589 }
0590 
0591 char *fgrep(FILE *inf, const char *str)
0592 {
0593     char line[256];
0594     int slen = strlen(str);
0595 
0596     while (!feof(inf)) {
0597         if (!fgets(line, 256, inf))
0598             break;
0599         if (strncmp(line, str, slen))
0600             continue;
0601 
0602         return strdup(line);
0603     }
0604 
0605     return NULL;
0606 }
0607 
0608 /*
0609  * validate_resctrl_feature_request - Check if requested feature is valid.
0610  * @resctrl_val:    Requested feature
0611  *
0612  * Return: True if the feature is supported, else false
0613  */
0614 bool validate_resctrl_feature_request(const char *resctrl_val)
0615 {
0616     struct stat statbuf;
0617     bool found = false;
0618     char *res;
0619     FILE *inf;
0620 
0621     if (!resctrl_val)
0622         return false;
0623 
0624     if (remount_resctrlfs(false))
0625         return false;
0626 
0627     if (!strncmp(resctrl_val, CAT_STR, sizeof(CAT_STR))) {
0628         if (!stat(L3_PATH, &statbuf))
0629             return true;
0630     } else if (!strncmp(resctrl_val, MBA_STR, sizeof(MBA_STR))) {
0631         if (!stat(MB_PATH, &statbuf))
0632             return true;
0633     } else if (!strncmp(resctrl_val, MBM_STR, sizeof(MBM_STR)) ||
0634            !strncmp(resctrl_val, CMT_STR, sizeof(CMT_STR))) {
0635         if (!stat(L3_MON_PATH, &statbuf)) {
0636             inf = fopen(L3_MON_FEATURES_PATH, "r");
0637             if (!inf)
0638                 return false;
0639 
0640             if (!strncmp(resctrl_val, CMT_STR, sizeof(CMT_STR))) {
0641                 res = fgrep(inf, "llc_occupancy");
0642                 if (res) {
0643                     found = true;
0644                     free(res);
0645                 }
0646             }
0647 
0648             if (!strncmp(resctrl_val, MBM_STR, sizeof(MBM_STR))) {
0649                 res = fgrep(inf, "mbm_total_bytes");
0650                 if (res) {
0651                     free(res);
0652                     res = fgrep(inf, "mbm_local_bytes");
0653                     if (res) {
0654                         found = true;
0655                         free(res);
0656                     }
0657                 }
0658             }
0659             fclose(inf);
0660         }
0661     }
0662 
0663     return found;
0664 }
0665 
0666 int filter_dmesg(void)
0667 {
0668     char line[1024];
0669     FILE *fp;
0670     int pipefds[2];
0671     pid_t pid;
0672     int ret;
0673 
0674     ret = pipe(pipefds);
0675     if (ret) {
0676         perror("pipe");
0677         return ret;
0678     }
0679     pid = fork();
0680     if (pid == 0) {
0681         close(pipefds[0]);
0682         dup2(pipefds[1], STDOUT_FILENO);
0683         execlp("dmesg", "dmesg", NULL);
0684         perror("executing dmesg");
0685         exit(1);
0686     }
0687     close(pipefds[1]);
0688     fp = fdopen(pipefds[0], "r");
0689     if (!fp) {
0690         perror("fdopen(pipe)");
0691         kill(pid, SIGTERM);
0692 
0693         return -1;
0694     }
0695 
0696     while (fgets(line, 1024, fp)) {
0697         if (strstr(line, "intel_rdt:"))
0698             ksft_print_msg("dmesg: %s", line);
0699         if (strstr(line, "resctrl:"))
0700             ksft_print_msg("dmesg: %s", line);
0701     }
0702     fclose(fp);
0703     waitpid(pid, NULL, 0);
0704 
0705     return 0;
0706 }
0707 
0708 int validate_bw_report_request(char *bw_report)
0709 {
0710     if (strcmp(bw_report, "reads") == 0)
0711         return 0;
0712     if (strcmp(bw_report, "writes") == 0)
0713         return 0;
0714     if (strcmp(bw_report, "nt-writes") == 0) {
0715         strcpy(bw_report, "writes");
0716         return 0;
0717     }
0718     if (strcmp(bw_report, "total") == 0)
0719         return 0;
0720 
0721     fprintf(stderr, "Requested iMC B/W report type unavailable\n");
0722 
0723     return -1;
0724 }
0725 
0726 int perf_event_open(struct perf_event_attr *hw_event, pid_t pid, int cpu,
0727             int group_fd, unsigned long flags)
0728 {
0729     int ret;
0730 
0731     ret = syscall(__NR_perf_event_open, hw_event, pid, cpu,
0732               group_fd, flags);
0733     return ret;
0734 }
0735 
0736 unsigned int count_bits(unsigned long n)
0737 {
0738     unsigned int count = 0;
0739 
0740     while (n) {
0741         count += n & 1;
0742         n >>= 1;
0743     }
0744 
0745     return count;
0746 }