dm: block_if: add multiple queues support

block_if is the backend of ahci and virtio-blk. Only one queue is
supported by block_if now. Several worker threads are created as
the thread pool for the queue. One BIG mutex is used for the queue
and thread operation. With this patch block_if can support multiple
queues and each queue is backed by several worker threads. blockif_req
can be submited/enqueued into one specified queue. By spliting into
several queues contention from the BIG mutex can be relieved/eliminated.
This is used to support virtio-blk multiple queues feature.

Tracked-On: #8612

Signed-off-by: Jian Jun Chen <jian.jun.chen@intel.com>
Acked-by: Wang, Yu1 <yu1.wang@intel.com>
This commit is contained in:
Jian Jun Chen 2023-07-11 17:47:23 +08:00 committed by acrnsi-robot
parent 562c22fb4e
commit edb392e7ed
4 changed files with 127 additions and 78 deletions

View File

@ -97,6 +97,22 @@ struct blockif_elem {
off_t block; off_t block;
}; };
struct blockif_queue {
int closing;
pthread_t btid[BLOCKIF_NUMTHR];
pthread_mutex_t mtx;
pthread_cond_t cond;
/* Request elements and free/pending/busy queues */
TAILQ_HEAD(, blockif_elem) freeq;
TAILQ_HEAD(, blockif_elem) pendq;
TAILQ_HEAD(, blockif_elem) busyq;
struct blockif_elem reqs[BLOCKIF_MAXREQ];
struct blockif_ctxt *bc;
};
struct blockif_ctxt { struct blockif_ctxt {
int fd; int fd;
int isblk; int isblk;
@ -112,16 +128,8 @@ struct blockif_ctxt {
int max_discard_sectors; int max_discard_sectors;
int max_discard_seg; int max_discard_seg;
int discard_sector_alignment; int discard_sector_alignment;
int closing; struct blockif_queue *bqs;
pthread_t btid[BLOCKIF_NUMTHR]; int bq_num;
pthread_mutex_t mtx;
pthread_cond_t cond;
/* Request elements and free/pending/busy queues */
TAILQ_HEAD(, blockif_elem) freeq;
TAILQ_HEAD(, blockif_elem) pendq;
TAILQ_HEAD(, blockif_elem) busyq;
struct blockif_elem reqs[BLOCKIF_MAXREQ];
/* write cache enable */ /* write cache enable */
uint8_t wce; uint8_t wce;
@ -158,19 +166,19 @@ blockif_flush_cache(struct blockif_ctxt *bc)
} }
static int static int
blockif_enqueue(struct blockif_ctxt *bc, struct blockif_req *breq, blockif_enqueue(struct blockif_queue *bq, struct blockif_req *breq,
enum blockop op) enum blockop op)
{ {
struct blockif_elem *be, *tbe; struct blockif_elem *be, *tbe;
off_t off; off_t off;
int i; int i;
be = TAILQ_FIRST(&bc->freeq); be = TAILQ_FIRST(&bq->freeq);
if (be == NULL || be->status != BST_FREE) { if (be == NULL || be->status != BST_FREE) {
WPRINTF(("%s: failed to get element from freeq\n", __func__)); WPRINTF(("%s: failed to get element from freeq\n", __func__));
return 0; return 0;
} }
TAILQ_REMOVE(&bc->freeq, be, link); TAILQ_REMOVE(&bq->freeq, be, link);
be->req = breq; be->req = breq;
be->op = op; be->op = op;
switch (op) { switch (op) {
@ -186,12 +194,12 @@ blockif_enqueue(struct blockif_ctxt *bc, struct blockif_req *breq,
off = 1 << (sizeof(off_t) - 1); off = 1 << (sizeof(off_t) - 1);
} }
be->block = off; be->block = off;
TAILQ_FOREACH(tbe, &bc->pendq, link) { TAILQ_FOREACH(tbe, &bq->pendq, link) {
if (tbe->block == breq->offset) if (tbe->block == breq->offset)
break; break;
} }
if (tbe == NULL) { if (tbe == NULL) {
TAILQ_FOREACH(tbe, &bc->busyq, link) { TAILQ_FOREACH(tbe, &bq->busyq, link) {
if (tbe->block == breq->offset) if (tbe->block == breq->offset)
break; break;
} }
@ -200,46 +208,46 @@ blockif_enqueue(struct blockif_ctxt *bc, struct blockif_req *breq,
be->status = BST_PEND; be->status = BST_PEND;
else else
be->status = BST_BLOCK; be->status = BST_BLOCK;
TAILQ_INSERT_TAIL(&bc->pendq, be, link); TAILQ_INSERT_TAIL(&bq->pendq, be, link);
return (be->status == BST_PEND); return (be->status == BST_PEND);
} }
static int static int
blockif_dequeue(struct blockif_ctxt *bc, pthread_t t, struct blockif_elem **bep) blockif_dequeue(struct blockif_queue *bq, pthread_t t, struct blockif_elem **bep)
{ {
struct blockif_elem *be; struct blockif_elem *be;
TAILQ_FOREACH(be, &bc->pendq, link) { TAILQ_FOREACH(be, &bq->pendq, link) {
if (be->status == BST_PEND) if (be->status == BST_PEND)
break; break;
} }
if (be == NULL) if (be == NULL)
return 0; return 0;
TAILQ_REMOVE(&bc->pendq, be, link); TAILQ_REMOVE(&bq->pendq, be, link);
be->status = BST_BUSY; be->status = BST_BUSY;
be->tid = t; be->tid = t;
TAILQ_INSERT_TAIL(&bc->busyq, be, link); TAILQ_INSERT_TAIL(&bq->busyq, be, link);
*bep = be; *bep = be;
return 1; return 1;
} }
static void static void
blockif_complete(struct blockif_ctxt *bc, struct blockif_elem *be) blockif_complete(struct blockif_queue *bq, struct blockif_elem *be)
{ {
struct blockif_elem *tbe; struct blockif_elem *tbe;
if (be->status == BST_DONE || be->status == BST_BUSY) if (be->status == BST_DONE || be->status == BST_BUSY)
TAILQ_REMOVE(&bc->busyq, be, link); TAILQ_REMOVE(&bq->busyq, be, link);
else else
TAILQ_REMOVE(&bc->pendq, be, link); TAILQ_REMOVE(&bq->pendq, be, link);
TAILQ_FOREACH(tbe, &bc->pendq, link) { TAILQ_FOREACH(tbe, &bq->pendq, link) {
if (tbe->req->offset == be->block) if (tbe->req->offset == be->block)
tbe->status = BST_PEND; tbe->status = BST_PEND;
} }
be->tid = 0; be->tid = 0;
be->status = BST_FREE; be->status = BST_FREE;
be->req = NULL; be->req = NULL;
TAILQ_INSERT_TAIL(&bc->freeq, be, link); TAILQ_INSERT_TAIL(&bq->freeq, be, link);
} }
static int static int
@ -327,13 +335,15 @@ blockif_process_discard(struct blockif_ctxt *bc, struct blockif_req *br)
} }
static void static void
blockif_proc(struct blockif_ctxt *bc, struct blockif_elem *be) blockif_proc(struct blockif_queue *bq, struct blockif_elem *be)
{ {
struct blockif_req *br; struct blockif_req *br;
struct blockif_ctxt *bc;
ssize_t len; ssize_t len;
int err; int err;
br = be->req; br = be->req;
bc = bq->bc;
err = 0; err = 0;
switch (be->op) { switch (be->op) {
case BOP_READ: case BOP_READ:
@ -379,29 +389,29 @@ blockif_proc(struct blockif_ctxt *bc, struct blockif_elem *be)
static void * static void *
blockif_thr(void *arg) blockif_thr(void *arg)
{ {
struct blockif_ctxt *bc; struct blockif_queue *bq;
struct blockif_elem *be; struct blockif_elem *be;
pthread_t t; pthread_t t;
bc = arg; bq = arg;
t = pthread_self(); t = pthread_self();
pthread_mutex_lock(&bc->mtx); pthread_mutex_lock(&bq->mtx);
for (;;) { for (;;) {
while (blockif_dequeue(bc, t, &be)) { while (blockif_dequeue(bq, t, &be)) {
pthread_mutex_unlock(&bc->mtx); pthread_mutex_unlock(&bq->mtx);
blockif_proc(bc, be); blockif_proc(bq, be);
pthread_mutex_lock(&bc->mtx); pthread_mutex_lock(&bq->mtx);
blockif_complete(bc, be); blockif_complete(bq, be);
} }
/* Check ctxt status here to see if exit requested */ /* Check ctxt status here to see if exit requested */
if (bc->closing) if (bq->closing)
break; break;
pthread_cond_wait(&bc->cond, &bc->mtx); pthread_cond_wait(&bq->cond, &bq->mtx);
} }
pthread_mutex_unlock(&bc->mtx); pthread_mutex_unlock(&bq->mtx);
pthread_exit(NULL); pthread_exit(NULL);
return NULL; return NULL;
} }
@ -488,16 +498,16 @@ sub_file_unlock(struct blockif_ctxt *bc)
struct blockif_ctxt * struct blockif_ctxt *
blockif_open(const char *optstr, const char *ident) blockif_open(const char *optstr, const char *ident, int queue_num)
{ {
char tname[MAXCOMLEN + 1]; char tname[MAXCOMLEN + 1];
/* char name[MAXPATHLEN]; */ /* char name[MAXPATHLEN]; */
char *nopt, *xopts, *cp; char *nopt, *xopts, *cp;
struct blockif_ctxt *bc; struct blockif_ctxt *bc = NULL;
struct stat sbuf; struct stat sbuf;
/* struct diocgattr_arg arg; */ /* struct diocgattr_arg arg; */
off_t size, psectsz, psectoff; off_t size, psectsz, psectoff;
int fd, i, sectsz; int fd, i, j, sectsz;
int writeback, ro, candiscard, ssopt, pssopt; int writeback, ro, candiscard, ssopt, pssopt;
long sz; long sz;
long long b; long long b;
@ -526,6 +536,9 @@ blockif_open(const char *optstr, const char *ident)
candiscard = 0; candiscard = 0;
if (queue_num <= 0)
queue_num = 1;
/* /*
* The first element in the optstring is always a pathname. * The first element in the optstring is always a pathname.
* Optional elements follow * Optional elements follow
@ -737,23 +750,35 @@ blockif_open(const char *optstr, const char *ident)
bc->psectsz = psectsz; bc->psectsz = psectsz;
bc->psectoff = psectoff; bc->psectoff = psectoff;
bc->wce = writeback; bc->wce = writeback;
pthread_mutex_init(&bc->mtx, NULL); bc->bq_num = queue_num;
pthread_cond_init(&bc->cond, NULL); bc->bqs = calloc(bc->bq_num, sizeof(struct blockif_queue));
TAILQ_INIT(&bc->freeq); if (bc->bqs == NULL) {
TAILQ_INIT(&bc->pendq); pr_err("calloc bqs");
TAILQ_INIT(&bc->busyq); goto err;
}
for (j = 0; j < bc->bq_num; j++) {
struct blockif_queue *bq = bc->bqs + j;
bq->bc = bc;
pthread_mutex_init(&bq->mtx, NULL);
pthread_cond_init(&bq->cond, NULL);
TAILQ_INIT(&bq->freeq);
TAILQ_INIT(&bq->pendq);
TAILQ_INIT(&bq->busyq);
for (i = 0; i < BLOCKIF_MAXREQ; i++) { for (i = 0; i < BLOCKIF_MAXREQ; i++) {
bc->reqs[i].status = BST_FREE; bq->reqs[i].status = BST_FREE;
TAILQ_INSERT_HEAD(&bc->freeq, &bc->reqs[i], link); TAILQ_INSERT_HEAD(&bq->freeq, &bq->reqs[i], link);
} }
for (i = 0; i < BLOCKIF_NUMTHR; i++) { for (i = 0; i < BLOCKIF_NUMTHR; i++) {
if (snprintf(tname, sizeof(tname), "blk-%s-%d", if (snprintf(tname, sizeof(tname), "blk-%s-%d-%d",
ident, i) >= sizeof(tname)) { ident, j, i) >= sizeof(tname)) {
pr_err("blk thread name too long"); pr_err("blk thread name too long");
} }
pthread_create(&bc->btid[i], NULL, blockif_thr, bc); pthread_create(&bq->btid[i], NULL, blockif_thr, bq);
pthread_setname_np(bc->btid[i], tname); pthread_setname_np(bq->btid[i], tname);
}
} }
/* free strdup memory */ /* free strdup memory */
@ -767,9 +792,13 @@ err:
/* handle failure case: free strdup memory*/ /* handle failure case: free strdup memory*/
if (nopt) if (nopt)
free(nopt); free(nopt);
if (fd >= 0) if (fd >= 0)
close(fd); close(fd);
if (bc) {
if (bc->bqs)
free(bc->bqs);
free(bc);
}
return NULL; return NULL;
} }
@ -777,18 +806,25 @@ static int
blockif_request(struct blockif_ctxt *bc, struct blockif_req *breq, blockif_request(struct blockif_ctxt *bc, struct blockif_req *breq,
enum blockop op) enum blockop op)
{ {
struct blockif_queue *bq;
int err; int err;
err = 0; err = 0;
pthread_mutex_lock(&bc->mtx); if (breq->qidx >= bc->bq_num) {
if (!TAILQ_EMPTY(&bc->freeq)) { pr_err("%s: invalid qidx %d\n", __func__, breq->qidx);
return ENOENT;
}
bq = bc->bqs + breq->qidx;
pthread_mutex_lock(&bq->mtx);
if (!TAILQ_EMPTY(&bq->freeq)) {
/* /*
* Enqueue and inform the block i/o thread * Enqueue and inform the block i/o thread
* that there is work available * that there is work available
*/ */
if (blockif_enqueue(bc, breq, op)) if (blockif_enqueue(bq, breq, op))
pthread_cond_signal(&bc->cond); pthread_cond_signal(&bq->cond);
} else { } else {
/* /*
* Callers are not allowed to enqueue more than * Callers are not allowed to enqueue more than
@ -798,7 +834,7 @@ blockif_request(struct blockif_ctxt *bc, struct blockif_req *breq,
*/ */
err = E2BIG; err = E2BIG;
} }
pthread_mutex_unlock(&bc->mtx); pthread_mutex_unlock(&bq->mtx);
return err; return err;
} }
@ -831,12 +867,19 @@ int
blockif_cancel(struct blockif_ctxt *bc, struct blockif_req *breq) blockif_cancel(struct blockif_ctxt *bc, struct blockif_req *breq)
{ {
struct blockif_elem *be; struct blockif_elem *be;
struct blockif_queue *bq;
pthread_mutex_lock(&bc->mtx); if (breq->qidx >= bc->bq_num) {
pr_err("%s: invalid qidx %d\n", __func__, breq->qidx);
return ENOENT;
}
bq = bc->bqs + breq->qidx;
pthread_mutex_lock(&bq->mtx);
/* /*
* Check pending requests. * Check pending requests.
*/ */
TAILQ_FOREACH(be, &bc->pendq, link) { TAILQ_FOREACH(be, &bq->pendq, link) {
if (be->req == breq) if (be->req == breq)
break; break;
} }
@ -844,8 +887,8 @@ blockif_cancel(struct blockif_ctxt *bc, struct blockif_req *breq)
/* /*
* Found it. * Found it.
*/ */
blockif_complete(bc, be); blockif_complete(bq, be);
pthread_mutex_unlock(&bc->mtx); pthread_mutex_unlock(&bq->mtx);
return 0; return 0;
} }
@ -853,7 +896,7 @@ blockif_cancel(struct blockif_ctxt *bc, struct blockif_req *breq)
/* /*
* Check in-flight requests. * Check in-flight requests.
*/ */
TAILQ_FOREACH(be, &bc->busyq, link) { TAILQ_FOREACH(be, &bq->busyq, link) {
if (be->req == breq) if (be->req == breq)
break; break;
} }
@ -861,7 +904,7 @@ blockif_cancel(struct blockif_ctxt *bc, struct blockif_req *breq)
/* /*
* Didn't find it. * Didn't find it.
*/ */
pthread_mutex_unlock(&bc->mtx); pthread_mutex_unlock(&bq->mtx);
return -1; return -1;
} }
@ -893,7 +936,7 @@ blockif_cancel(struct blockif_ctxt *bc, struct blockif_req *breq)
pthread_mutex_unlock(&bse.mtx); pthread_mutex_unlock(&bse.mtx);
} }
pthread_mutex_unlock(&bc->mtx); pthread_mutex_unlock(&bq->mtx);
/* /*
* The processing thread has been interrupted. Since it's not * The processing thread has been interrupted. Since it's not
@ -906,20 +949,24 @@ int
blockif_close(struct blockif_ctxt *bc) blockif_close(struct blockif_ctxt *bc)
{ {
void *jval; void *jval;
int i; int i, j;
sub_file_unlock(bc); sub_file_unlock(bc);
/* /*
* Stop the block i/o thread * Stop the block i/o thread
*/ */
pthread_mutex_lock(&bc->mtx); for (j = 0; j < bc->bq_num; j++) {
bc->closing = 1; struct blockif_queue *bq = bc->bqs + j;
pthread_cond_broadcast(&bc->cond);
pthread_mutex_unlock(&bc->mtx); pthread_mutex_lock(&bq->mtx);
bq->closing = 1;
pthread_cond_broadcast(&bq->cond);
pthread_mutex_unlock(&bq->mtx);
for (i = 0; i < BLOCKIF_NUMTHR; i++) for (i = 0; i < BLOCKIF_NUMTHR; i++)
pthread_join(bc->btid[i], &jval); pthread_join(bq->btid[i], &jval);
}
/* XXX Cancel queued i/o's ??? */ /* XXX Cancel queued i/o's ??? */
@ -927,6 +974,8 @@ blockif_close(struct blockif_ctxt *bc)
* Release resources * Release resources
*/ */
close(bc->fd); close(bc->fd);
if (bc->bqs)
free(bc->bqs);
free(bc); free(bc);
return 0; return 0;

View File

@ -2405,7 +2405,7 @@ pci_ahci_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts, int atapi)
*/ */
snprintf(bident, sizeof(bident), "%02x:%02x:%02x", dev->slot, snprintf(bident, sizeof(bident), "%02x:%02x:%02x", dev->slot,
dev->func, p); dev->func, p);
bctxt = blockif_open(opts, bident); bctxt = blockif_open(opts, bident, 1);
if (bctxt == NULL) { if (bctxt == NULL) {
ahci_dev->ports = p; ahci_dev->ports = p;
ret = 1; ret = 1;

View File

@ -545,7 +545,7 @@ virtio_blk_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts)
break; break;
} }
} }
bctxt = blockif_open(p, bident); bctxt = blockif_open(p, bident, num_vqs);
if (bctxt == NULL) { if (bctxt == NULL) {
pr_err("Could not open backing file"); pr_err("Could not open backing file");
free(opts_start); free(opts_start);
@ -804,7 +804,7 @@ virtio_blk_rescan(struct vmctx *ctx, struct pci_vdev *dev, char *newpath)
pr_err("name=%s, Path=%s, ident=%s\n", dev->name, newpath, bident); pr_err("name=%s, Path=%s, ident=%s\n", dev->name, newpath, bident);
/* update the bctxt for the virtio-blk device */ /* update the bctxt for the virtio-blk device */
bctxt = blockif_open(newpath, bident); bctxt = blockif_open(newpath, bident, blk->num_vqs);
if (bctxt == NULL) { if (bctxt == NULL) {
pr_err("Error opening backing file\n"); pr_err("Error opening backing file\n");
goto end; goto end;

View File

@ -52,7 +52,7 @@ struct blockif_req {
}; };
struct blockif_ctxt; struct blockif_ctxt;
struct blockif_ctxt *blockif_open(const char *optstr, const char *ident); struct blockif_ctxt *blockif_open(const char *optstr, const char *ident, int queue_num);
off_t blockif_size(struct blockif_ctxt *bc); off_t blockif_size(struct blockif_ctxt *bc);
void blockif_chs(struct blockif_ctxt *bc, uint16_t *c, uint8_t *h, void blockif_chs(struct blockif_ctxt *bc, uint16_t *c, uint8_t *h,
uint8_t *s); uint8_t *s);