/*
 * reqlist API
 *
 * Copyright (C) 2013 Proxmox Server Solutions
 * Copyright (c) 2021 Virtuozzo International GmbH.
 *
 * Authors:
 *  Dietmar Maurer (dietmar@proxmox.com)
 *  Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
 *
 * This work is licensed under the terms of the GNU GPL, version 2 or later.
 * See the COPYING file in the top-level directory.
 */

#include "qemu/osdep.h"
#include "qemu/range.h"

#include "block/reqlist.h"

void reqlist_init_req(BlockReqList *reqs, BlockReq *req, int64_t offset,
                      int64_t bytes)
{
    *req = (BlockReq) {
        .offset = offset,
        .bytes = bytes,
    };
    qemu_co_queue_init(&req->wait_queue);
    QLIST_INSERT_HEAD(reqs, req, list);
}

BlockReq *reqlist_find_conflict(BlockReqList *reqs, int64_t offset,
                                int64_t bytes)
{
    BlockReq *r;

    QLIST_FOREACH(r, reqs, list) {
        if (ranges_overlap(offset, bytes, r->offset, r->bytes)) {
            return r;
        }
    }

    return NULL;
}

bool coroutine_fn reqlist_wait_one(BlockReqList *reqs, int64_t offset,
                                   int64_t bytes, CoMutex *lock)
{
    BlockReq *r = reqlist_find_conflict(reqs, offset, bytes);

    if (!r) {
        return false;
    }

    qemu_co_queue_wait(&r->wait_queue, lock);

    return true;
}

void coroutine_fn reqlist_wait_all(BlockReqList *reqs, int64_t offset,
                                   int64_t bytes, CoMutex *lock)
{
    while (reqlist_wait_one(reqs, offset, bytes, lock)) {
        /* continue */
    }
}

void coroutine_fn reqlist_shrink_req(BlockReq *req, int64_t new_bytes)
{
    if (new_bytes == req->bytes) {
        return;
    }

    assert(new_bytes > 0 && new_bytes < req->bytes);

    req->bytes = new_bytes;
    qemu_co_queue_restart_all(&req->wait_queue);
}

void coroutine_fn reqlist_remove_req(BlockReq *req)
{
    QLIST_REMOVE(req, list);
    qemu_co_queue_restart_all(&req->wait_queue);
}
