|
@@ -1986,6 +1986,15 @@ static void cfq_setup_merge(struct cfq_queue *cfqq, struct cfq_queue *new_cfqq)
|
|
int process_refs, new_process_refs;
|
|
int process_refs, new_process_refs;
|
|
struct cfq_queue *__cfqq;
|
|
struct cfq_queue *__cfqq;
|
|
|
|
|
|
|
|
+ /*
|
|
|
|
+ * If there are no process references on the new_cfqq, then it is
|
|
|
|
+ * unsafe to follow the ->new_cfqq chain as other cfqq's in the
|
|
|
|
+ * chain may have dropped their last reference (not just their
|
|
|
|
+ * last process reference).
|
|
|
|
+ */
|
|
|
|
+ if (!cfqq_process_refs(new_cfqq))
|
|
|
|
+ return;
|
|
|
|
+
|
|
/* Avoid a circular list and skip interim queue merges */
|
|
/* Avoid a circular list and skip interim queue merges */
|
|
while ((__cfqq = new_cfqq->new_cfqq)) {
|
|
while ((__cfqq = new_cfqq->new_cfqq)) {
|
|
if (__cfqq == cfqq)
|
|
if (__cfqq == cfqq)
|
|
@@ -1994,17 +2003,17 @@ static void cfq_setup_merge(struct cfq_queue *cfqq, struct cfq_queue *new_cfqq)
|
|
}
|
|
}
|
|
|
|
|
|
process_refs = cfqq_process_refs(cfqq);
|
|
process_refs = cfqq_process_refs(cfqq);
|
|
|
|
+ new_process_refs = cfqq_process_refs(new_cfqq);
|
|
/*
|
|
/*
|
|
* If the process for the cfqq has gone away, there is no
|
|
* If the process for the cfqq has gone away, there is no
|
|
* sense in merging the queues.
|
|
* sense in merging the queues.
|
|
*/
|
|
*/
|
|
- if (process_refs == 0)
|
|
|
|
|
|
+ if (process_refs == 0 || new_process_refs == 0)
|
|
return;
|
|
return;
|
|
|
|
|
|
/*
|
|
/*
|
|
* Merge in the direction of the lesser amount of work.
|
|
* Merge in the direction of the lesser amount of work.
|
|
*/
|
|
*/
|
|
- new_process_refs = cfqq_process_refs(new_cfqq);
|
|
|
|
if (new_process_refs >= process_refs) {
|
|
if (new_process_refs >= process_refs) {
|
|
cfqq->new_cfqq = new_cfqq;
|
|
cfqq->new_cfqq = new_cfqq;
|
|
atomic_add(process_refs, &new_cfqq->ref);
|
|
atomic_add(process_refs, &new_cfqq->ref);
|