+/*
+ * Copyright (C) 2012 Pierre Habouzit <pierre.habouzit@intersec.com>
+ * Copyright (C) 2012 Intersec SAS
+ *
+ * This file is part of the Linux Pthread Workqueue Regulator tests.
+ *
+ * The Linux Pthread Workqueue Regulator is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU Lesser
+ * General Public License as published by the Free Software Foundation,
+ * either version 2.1 of the License, or (at your option) any later version.
+ *
+ * The Linux Pthread Workqueue Regulator is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with The Linux Pthread Workqueue Regultaor.
+ * If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <sched.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/ioctl.h>
+#include <sys/poll.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "../kernel/pwqr.h"
+
+/* pwqr wrapping {{{ */
+
+static int pwqr_create(int flags)
+{
+ if (flags & ~PWQR_FL__SET) {
+ errno = -EINVAL;
+ return -1;
+ }
+ return open("/dev/"PWQR_DEVICE_NAME, O_RDWR | flags);
+}
+
+static int pwqr_ctl(int fd, int op, int val, void *uaddr)
+{
+ struct pwqr_ioc_wait wait;
+
+ switch (op) {
+ case PWQR_CTL_GET_CONC:
+ case PWQR_CTL_REGISTER:
+ case PWQR_CTL_UNREGISTER:
+ case PWQR_CTL_PARK:
+ return ioctl(fd, op);
+ case PWQR_CTL_SET_CONC:
+ case PWQR_CTL_WAKE:
+ case PWQR_CTL_WAKE_OC:
+ return ioctl(fd, op, val);
+ case PWQR_CTL_WAIT:
+ wait.pwqr_ticket = val;
+ wait.pwqr_uaddr = uaddr;
+ return ioctl(fd, op, &wait);
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+}
+
+/* }}} */
+
+static int pwqr_fd_g = -1;
+static int pi_g;
+
+static void *thr_main(void *unused)
+{
+ if (pwqr_ctl(pwqr_fd_g, PWQR_CTL_REGISTER, 0, NULL) < 0)
+ err(-1, "pwqr_ctl(PWQR_CTL_REGISTER)");
+
+ /* busy-loop */
+ for (;;)
+ pthread_testcancel();
+ return NULL;
+}
+
+static uint64_t tv_diff(const struct timeval *t1, const struct timeval *t2)
+{
+ return (t1->tv_sec - t2->tv_sec) * 1000000ULL + t1->tv_usec - t2->tv_usec;
+}
+
+int main(void)
+{
+ pthread_t tids[32];
+ struct pollfd pfd;
+ struct timeval start, tv;
+ int rc;
+
+ pwqr_fd_g = pwqr_create(PWQR_FL_NONBLOCK);
+ if (pwqr_fd_g < 0)
+ err(-1, "pwqr_create");
+
+ pi_g = pwqr_ctl(pwqr_fd_g, PWQR_CTL_GET_CONC, 0, NULL);
+ if (pi_g < 1)
+ err(-1, "pwqr_ctl(PWQR_CTL_GET_CONC)");
+
+ for (int i = 0; i < pi_g + 1; i++) {
+ pthread_create(&tids[i], NULL, thr_main, NULL);
+ }
+
+ pfd.fd = pwqr_fd_g;
+ pfd.events = POLLIN;
+ gettimeofday(&start, NULL);
+ for (;;) {
+ pfd.revents = 0;
+ rc = poll(&pfd, 1, -1);
+ if (rc < 0) {
+ warn("poll");
+ continue;
+ }
+ if (rc == 1) {
+ if (pfd.revents & POLLIN) {
+ if (read(pwqr_fd_g, &rc, sizeof(rc)) != sizeof(rc)) {
+ warn("read");
+ } else {
+ gettimeofday(&tv, NULL);
+ warnx("[%06jd.%06jd] overcommit of %d",
+ tv_diff(&tv, &start) / 1000000,
+ tv_diff(&tv, &start) % 1000000, rc);
+ }
+ }
+ if (pfd.revents & POLLHUP) {
+ errx(-1, "poll returned POLLHUP");
+ }
+ }
+ }
+
+ for (int i = 0; i < pi_g + 1; i++) {
+ pthread_join(tids[i], NULL);
+ }
+ return 0;
+}