cfq-iosched: Don't update group weights when on service tree

Version 3 is updated to apply to for-2.6.39/core.

For version 2, I took Vivek's advice and made sure we update the group
weight from cfq_group_service_tree_add().

If a weight was updated while a group is on the service tree, the
calculation for the total weight of the service tree can be adjusted
improperly, which either leads to bad service tree weights, or
potentially crashes (if total_weight becomes 0).

This patch defers updates to the weight until a group is off the service
tree.

Signed-off-by: Justin TerAvest <teravest@google.com>
Acked-by: Vivek Goyal <vgoyal@redhat.com>
Signed-off-by: Jens Axboe <jaxboe@fusionio.com>

authored by Justin TerAvest and committed by Jens Axboe 8184f93e 95f28604

+42 -13
+42 -13
block/cfq-iosched.c
··· 178 178 /* group service_tree key */ 179 179 u64 vdisktime; 180 180 unsigned int weight; 181 + unsigned int new_weight; 182 + bool needs_update; 181 183 182 184 /* number of cfqq currently on this group */ 183 185 int nr_cfqq; ··· 855 853 } 856 854 857 855 static void 858 - cfq_group_service_tree_add(struct cfq_data *cfqd, struct cfq_group *cfqg) 856 + cfq_update_group_weight(struct cfq_group *cfqg) 857 + { 858 + BUG_ON(!RB_EMPTY_NODE(&cfqg->rb_node)); 859 + if (cfqg->needs_update) { 860 + cfqg->weight = cfqg->new_weight; 861 + cfqg->needs_update = false; 862 + } 863 + } 864 + 865 + static void 866 + cfq_group_service_tree_add(struct cfq_rb_root *st, struct cfq_group *cfqg) 867 + { 868 + BUG_ON(!RB_EMPTY_NODE(&cfqg->rb_node)); 869 + 870 + cfq_update_group_weight(cfqg); 871 + __cfq_group_service_tree_add(st, cfqg); 872 + st->total_weight += cfqg->weight; 873 + } 874 + 875 + static void 876 + cfq_group_notify_queue_add(struct cfq_data *cfqd, struct cfq_group *cfqg) 859 877 { 860 878 struct cfq_rb_root *st = &cfqd->grp_service_tree; 861 879 struct cfq_group *__cfqg; ··· 896 874 cfqg->vdisktime = __cfqg->vdisktime + CFQ_IDLE_DELAY; 897 875 } else 898 876 cfqg->vdisktime = st->min_vdisktime; 899 - 900 - __cfq_group_service_tree_add(st, cfqg); 901 - st->total_weight += cfqg->weight; 877 + cfq_group_service_tree_add(st, cfqg); 902 878 } 903 879 904 880 static void 905 - cfq_group_service_tree_del(struct cfq_data *cfqd, struct cfq_group *cfqg) 881 + cfq_group_service_tree_del(struct cfq_rb_root *st, struct cfq_group *cfqg) 882 + { 883 + st->total_weight -= cfqg->weight; 884 + if (!RB_EMPTY_NODE(&cfqg->rb_node)) 885 + cfq_rb_erase(&cfqg->rb_node, st); 886 + } 887 + 888 + static void 889 + cfq_group_notify_queue_del(struct cfq_data *cfqd, struct cfq_group *cfqg) 906 890 { 907 891 struct cfq_rb_root *st = &cfqd->grp_service_tree; 908 892 ··· 920 892 return; 921 893 922 894 cfq_log_cfqg(cfqd, cfqg, "del_from_rr group"); 923 - st->total_weight -= cfqg->weight; 924 - if (!RB_EMPTY_NODE(&cfqg->rb_node)) 925 - cfq_rb_erase(&cfqg->rb_node, st); 895 + cfq_group_service_tree_del(st, cfqg); 926 896 cfqg->saved_workload_slice = 0; 927 897 cfq_blkiocg_update_dequeue_stats(&cfqg->blkg, 1); 928 898 } ··· 974 948 charge = cfqq->allocated_slice; 975 949 976 950 /* Can't update vdisktime while group is on service tree */ 977 - cfq_rb_erase(&cfqg->rb_node, st); 951 + cfq_group_service_tree_del(st, cfqg); 978 952 cfqg->vdisktime += cfq_scale_slice(charge, cfqg); 979 - __cfq_group_service_tree_add(st, cfqg); 953 + /* If a new weight was requested, update now, off tree */ 954 + cfq_group_service_tree_add(st, cfqg); 980 955 981 956 /* This group is being expired. Save the context */ 982 957 if (time_after(cfqd->workload_expires, jiffies)) { ··· 1009 982 void cfq_update_blkio_group_weight(void *key, struct blkio_group *blkg, 1010 983 unsigned int weight) 1011 984 { 1012 - cfqg_of_blkg(blkg)->weight = weight; 985 + struct cfq_group *cfqg = cfqg_of_blkg(blkg); 986 + cfqg->new_weight = weight; 987 + cfqg->needs_update = true; 1013 988 } 1014 989 1015 990 static struct cfq_group * ··· 1284 1255 service_tree->count++; 1285 1256 if ((add_front || !new_cfqq) && !group_changed) 1286 1257 return; 1287 - cfq_group_service_tree_add(cfqd, cfqq->cfqg); 1258 + cfq_group_notify_queue_add(cfqd, cfqq->cfqg); 1288 1259 } 1289 1260 1290 1261 static struct cfq_queue * ··· 1397 1368 cfqq->p_root = NULL; 1398 1369 } 1399 1370 1400 - cfq_group_service_tree_del(cfqd, cfqq->cfqg); 1371 + cfq_group_notify_queue_del(cfqd, cfqq->cfqg); 1401 1372 BUG_ON(!cfqd->busy_queues); 1402 1373 cfqd->busy_queues--; 1403 1374 if (cfq_cfqq_sync(cfqq))