#include <unistd.h>
#include <stdio.h>
#include <stdbool.h>
#include "vma.h"
#include "xmalloc.h"
#include "aio.h"
#include "parasite.h"
#include "parasite-syscall.h"
#include "protobuf/mm.pb-c.h"

int dump_aio_ring(MmEntry *mme, struct vma_area *vma)
{
	int nr = mme->n_aios;
	AioRingEntry *re;

	pr_info("Dumping AIO ring @%"PRIx64", %u reqs\n",
			vma->e->start, vma->aio_nr_req);

	mme->aios = xrealloc(mme->aios, (nr + 1) * sizeof(re));
	if (!mme->aios)
		return -1;

	re = xmalloc(sizeof(*re));
	if (!re)
		return -1;

	aio_ring_entry__init(re);
	re->id = vma->e->start;
	re->nr_req = vma->aio_nr_req;
	re->ring_len = vma->e->end - vma->e->start;
	mme->aios[nr] = re;
	mme->n_aios = nr + 1;
	return 0;
}

void free_aios(MmEntry *mme)
{
	int i;

	if (mme->aios) {
		for (i = 0; i < mme->n_aios; i++)
			xfree(mme->aios[i]);
		xfree(mme->aios);
	}
}

static unsigned int aio_estimate_nr_reqs(unsigned int k_max_reqs)
{
	/*
	 * Kernel does
	 *
	 * nr_reqs = max(nr_reqs, nr_cpus * 4)
	 * nr_reqs *= 2
	 * nr_reqs += 2
	 * ring = roundup(sizeof(head) + nr_reqs * sizeof(req))
	 * nr_reqs = (ring - sizeof(head)) / sizeof(req)
	 *
	 * And the k_max_reqs here is the resulting value.
	 *
	 * We need to get the initial nr_reqs that would grow
	 * up back to the k_max_reqs.
	 */

	return (k_max_reqs - 2) / 2;
}

unsigned long aio_rings_args_size(struct vm_area_list *vmas)
{
	return sizeof(struct parasite_check_aios_args) +
		vmas->nr_aios * sizeof(struct parasite_aio);
}

int parasite_check_aios(struct parasite_ctl *ctl, struct vm_area_list *vmas)
{
	struct vma_area *vma;
	struct parasite_check_aios_args *aa;
	struct parasite_aio *pa;
	int i;

	if (!vmas->nr_aios)
		return 0;

	pr_info("Checking AIO rings\n");

	/*
	 * Go to parasite and
	 * a) check that no requests are currently pengind
	 * b) get the maximum number of requests kernel handles
	 *    to estimate what was the user request on ring
	 *    creation.
	 */

	aa = parasite_args_s(ctl, aio_rings_args_size(vmas));
	pa = &aa->ring[0];
	list_for_each_entry(vma, &vmas->h, list) {
		if (!vma_area_is(vma, VMA_AREA_AIORING))
			continue;

		pr_debug(" `- Ring #%ld @%"PRIx64"\n",
				(long)(pa - &aa->ring[0]), vma->e->start);
		pa->ctx = vma->e->start;
		pa->max_reqs = 0;
		pa->vma_nr_reqs = &vma->aio_nr_req;
		pa++;
	}
	aa->nr_rings = vmas->nr_aios;

	if (parasite_execute_daemon(PARASITE_CMD_CHECK_AIOS, ctl))
		return -1;

	pa = &aa->ring[0];
	for (i = 0; i < vmas->nr_aios; i++) {
		pa = &aa->ring[i];
		*pa->vma_nr_reqs = aio_estimate_nr_reqs(pa->max_reqs);
		pr_debug(" `- Ring #%d has %u reqs, estimated to %u\n", i,
				pa->max_reqs, *pa->vma_nr_reqs);
	}

	return 0;
}
