Commit 1b435b10324fe9937f254bb00718f78d5e50837a

Authored by aliguori
1 parent 474ad834

Make bottom halves more robust

Bottom halves are supposed to not complete until the next iteration of the main
loop.  This is very important to ensure that guests can not cause stack
overflows in the block driver code.  Right now, if you attempt to schedule a
bottom half within a bottom half callback, you will enter an infinite loop.

This patch uses the same logic that we use for the IOHandler loop to make the
bottom half processing robust in list manipulation while in a callback.

This patch also introduces idle scheduling for bottom halves.  qemu_bh_poll()
returns an indication of whether any bottom halves were successfully executed.
qemu_aio_wait() uses this to immediately return if a bottom half was executed
instead of waiting for a completion notification.

qemu_bh_schedule_idle() works around this by not reporting the callback has
run in the qemu_bh_poll loop.  qemu_aio_wait() probably needs some refactoring
but that would require a larger code audit.  idle scheduling seems like a good
compromise.

Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>




git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@5572 c046a42c-6fe2-441c-8c8c-71466251a162
Showing 2 changed files with 38 additions and 23 deletions
qemu-common.h
@@ -70,6 +70,7 @@ typedef void QEMUBHFunc(void *opaque); @@ -70,6 +70,7 @@ typedef void QEMUBHFunc(void *opaque);
70 70
71 QEMUBH *qemu_bh_new(QEMUBHFunc *cb, void *opaque); 71 QEMUBH *qemu_bh_new(QEMUBHFunc *cb, void *opaque);
72 void qemu_bh_schedule(QEMUBH *bh); 72 void qemu_bh_schedule(QEMUBH *bh);
  73 +void qemu_bh_schedule_idle(QEMUBH *bh);
73 void qemu_bh_cancel(QEMUBH *bh); 74 void qemu_bh_cancel(QEMUBH *bh);
74 void qemu_bh_delete(QEMUBH *bh); 75 void qemu_bh_delete(QEMUBH *bh);
75 int qemu_bh_poll(void); 76 int qemu_bh_poll(void);
@@ -7578,6 +7578,8 @@ struct QEMUBH { @@ -7578,6 +7578,8 @@ struct QEMUBH {
7578 QEMUBHFunc *cb; 7578 QEMUBHFunc *cb;
7579 void *opaque; 7579 void *opaque;
7580 int scheduled; 7580 int scheduled;
  7581 + int idle;
  7582 + int deleted;
7581 QEMUBH *next; 7583 QEMUBH *next;
7582 }; 7584 };
7583 7585
@@ -7591,37 +7593,56 @@ QEMUBH *qemu_bh_new(QEMUBHFunc *cb, void *opaque) @@ -7591,37 +7593,56 @@ QEMUBH *qemu_bh_new(QEMUBHFunc *cb, void *opaque)
7591 return NULL; 7593 return NULL;
7592 bh->cb = cb; 7594 bh->cb = cb;
7593 bh->opaque = opaque; 7595 bh->opaque = opaque;
  7596 + bh->next = first_bh;
  7597 + first_bh = bh;
7594 return bh; 7598 return bh;
7595 } 7599 }
7596 7600
7597 int qemu_bh_poll(void) 7601 int qemu_bh_poll(void)
7598 { 7602 {
7599 - QEMUBH *bh, **pbh; 7603 + QEMUBH *bh, **bhp;
7600 int ret; 7604 int ret;
7601 7605
7602 ret = 0; 7606 ret = 0;
7603 - for(;;) {  
7604 - pbh = &first_bh;  
7605 - bh = *pbh;  
7606 - if (!bh)  
7607 - break;  
7608 - ret = 1;  
7609 - *pbh = bh->next;  
7610 - bh->scheduled = 0;  
7611 - bh->cb(bh->opaque); 7607 + for (bh = first_bh; bh; bh = bh->next) {
  7608 + if (!bh->deleted && bh->scheduled) {
  7609 + bh->scheduled = 0;
  7610 + if (!bh->idle)
  7611 + ret = 1;
  7612 + bh->idle = 0;
  7613 + bh->cb(bh->opaque);
  7614 + }
7612 } 7615 }
  7616 +
  7617 + /* remove deleted bhs */
  7618 + bhp = &first_bh;
  7619 + while (*bhp) {
  7620 + bh = *bhp;
  7621 + if (bh->deleted) {
  7622 + *bhp = bh->next;
  7623 + qemu_free(bh);
  7624 + } else
  7625 + bhp = &bh->next;
  7626 + }
  7627 +
7613 return ret; 7628 return ret;
7614 } 7629 }
7615 7630
  7631 +void qemu_bh_schedule_idle(QEMUBH *bh)
  7632 +{
  7633 + if (bh->scheduled)
  7634 + return;
  7635 + bh->scheduled = 1;
  7636 + bh->idle = 1;
  7637 +}
  7638 +
7616 void qemu_bh_schedule(QEMUBH *bh) 7639 void qemu_bh_schedule(QEMUBH *bh)
7617 { 7640 {
7618 CPUState *env = cpu_single_env; 7641 CPUState *env = cpu_single_env;
7619 if (bh->scheduled) 7642 if (bh->scheduled)
7620 return; 7643 return;
7621 bh->scheduled = 1; 7644 bh->scheduled = 1;
7622 - bh->next = first_bh;  
7623 - first_bh = bh;  
7624 - 7645 + bh->idle = 0;
7625 /* stop the currently executing CPU to execute the BH ASAP */ 7646 /* stop the currently executing CPU to execute the BH ASAP */
7626 if (env) { 7647 if (env) {
7627 cpu_interrupt(env, CPU_INTERRUPT_EXIT); 7648 cpu_interrupt(env, CPU_INTERRUPT_EXIT);
@@ -7630,20 +7651,13 @@ void qemu_bh_schedule(QEMUBH *bh) @@ -7630,20 +7651,13 @@ void qemu_bh_schedule(QEMUBH *bh)
7630 7651
7631 void qemu_bh_cancel(QEMUBH *bh) 7652 void qemu_bh_cancel(QEMUBH *bh)
7632 { 7653 {
7633 - QEMUBH **pbh;  
7634 - if (bh->scheduled) {  
7635 - pbh = &first_bh;  
7636 - while (*pbh != bh)  
7637 - pbh = &(*pbh)->next;  
7638 - *pbh = bh->next;  
7639 - bh->scheduled = 0;  
7640 - } 7654 + bh->scheduled = 0;
7641 } 7655 }
7642 7656
7643 void qemu_bh_delete(QEMUBH *bh) 7657 void qemu_bh_delete(QEMUBH *bh)
7644 { 7658 {
7645 - qemu_bh_cancel(bh);  
7646 - qemu_free(bh); 7659 + bh->scheduled = 0;
  7660 + bh->deleted = 1;
7647 } 7661 }
7648 7662
7649 /***********************************************************/ 7663 /***********************************************************/