/* * Calculate thresh of wb in writeback cgroup which is min of * thresh in global domain and thresh in cgroup domain. Drop * rcu lock because cgwb_calc_thresh may sleep in * cgroup_rstat_flush. We can do so here because we have a ref.
*/ if (mem_cgroup_wb_domain(wb)) {
rcu_read_unlock();
stats.wb_thresh = min(stats.wb_thresh, cgwb_calc_thresh(wb));
rcu_read_lock();
}
/* * Remove bdi from the global list and shutdown any threads we have running
*/ staticvoid wb_shutdown(struct bdi_writeback *wb)
{ /* Make sure nobody queues further work */
spin_lock_irq(&wb->work_lock); if (!test_and_clear_bit(WB_registered, &wb->state)) {
spin_unlock_irq(&wb->work_lock); return;
}
spin_unlock_irq(&wb->work_lock);
cgwb_remove_from_bdi_list(wb); /* * Drain work list and shutdown the delayed_work. !WB_registered * tells wb_workfn() that @wb is dying and its work_list needs to * be drained no matter what.
*/
mod_delayed_work(bdi_wq, &wb->dwork, 0);
flush_delayed_work(&wb->dwork);
WARN_ON(!list_empty(&wb->work_list));
flush_delayed_work(&wb->bw_dwork);
}
/* * The root wb determines the registered state of the whole bdi and * memcg_cgwb_list and blkcg_cgwb_list's next pointers indicate * whether they're still online. Don't link @wb if any is dead. * See wb_memcg_offline() and wb_blkcg_offline().
*/
ret = -ENODEV;
spin_lock_irqsave(&cgwb_lock, flags); if (test_bit(WB_registered, &bdi->wb.state) &&
blkcg_cgwb_list->next && memcg_cgwb_list->next) { /* we might have raced another instance of this function */
ret = radix_tree_insert(&bdi->cgwb_tree, memcg_css->id, wb); if (!ret) {
list_add_tail_rcu(&wb->bdi_node, &bdi->wb_list);
list_add(&wb->memcg_node, memcg_cgwb_list);
list_add(&wb->blkcg_node, blkcg_cgwb_list);
blkcg_pin_online(blkcg_css);
css_get(memcg_css);
css_get(blkcg_css);
}
}
spin_unlock_irqrestore(&cgwb_lock, flags); if (ret) { if (ret == -EEXIST)
ret = 0; goto err_fprop_exit;
} goto out_put;
/** * wb_get_lookup - get wb for a given memcg * @bdi: target bdi * @memcg_css: cgroup_subsys_state of the target memcg (must have positive ref) * * Try to get the wb for @memcg_css on @bdi. The returned wb has its * refcount incremented. * * This function uses css_get() on @memcg_css and thus expects its refcnt * to be positive on invocation. IOW, rcu_read_lock() protection on * @memcg_css isn't enough. try_get it before calling this function. * * A wb is keyed by its associated memcg. As blkcg implicitly enables * memcg on the default hierarchy, memcg association is guaranteed to be * more specific (equal or descendant to the associated blkcg) and thus can * identify both the memcg and blkcg associations. * * Because the blkcg associated with a memcg may change as blkcg is enabled * and disabled closer to root in the hierarchy, each wb keeps track of * both the memcg and blkcg associated with it and verifies the blkcg on * each lookup. On mismatch, the existing wb is discarded and a new one is * created.
*/ struct bdi_writeback *wb_get_lookup(struct backing_dev_info *bdi, struct cgroup_subsys_state *memcg_css)
{ struct bdi_writeback *wb;
/* see whether the blkcg association has changed */
blkcg_css = cgroup_get_e_css(memcg_css->cgroup, &io_cgrp_subsys); if (unlikely(wb->blkcg_css != blkcg_css || !wb_tryget(wb)))
wb = NULL;
css_put(blkcg_css);
}
rcu_read_unlock();
return wb;
}
/** * wb_get_create - get wb for a given memcg, create if necessary * @bdi: target bdi * @memcg_css: cgroup_subsys_state of the target memcg (must have positive ref) * @gfp: allocation mask to use * * Try to get the wb for @memcg_css on @bdi. If it doesn't exist, try to * create one. See wb_get_lookup() for more details.
*/ struct bdi_writeback *wb_get_create(struct backing_dev_info *bdi, struct cgroup_subsys_state *memcg_css,
gfp_t gfp)
{ struct bdi_writeback *wb;
might_alloc(gfp);
do {
wb = wb_get_lookup(bdi, memcg_css);
} while (!wb && !cgwb_create(bdi, memcg_css, gfp));
return wb;
}
staticint cgwb_bdi_init(struct backing_dev_info *bdi)
{ int ret;
/* * cleanup_offline_cgwbs_workfn - try to release dying cgwbs * * Try to release dying cgwbs by switching attached inodes to the nearest * living ancestor's writeback. Processed wbs are placed at the end * of the list to guarantee the forward progress.
*/ staticvoid cleanup_offline_cgwbs_workfn(struct work_struct *work)
{ struct bdi_writeback *wb;
LIST_HEAD(processed);
/* * If wb is dirty, cleaning up the writeback by switching * attached inodes will result in an effective removal of any * bandwidth restrictions, which isn't the goal. Instead, * it can be postponed until the next time, when all io * will be likely completed. If in the meantime some inodes * will get re-dirtied, they should be eventually switched to * a new cgwb.
*/ if (wb_has_dirty_io(wb)) continue;
if (!wb_tryget(wb)) continue;
spin_unlock_irq(&cgwb_lock); while (cleanup_offline_cgwb(wb))
cond_resched();
spin_lock_irq(&cgwb_lock);
wb_put(wb);
}
if (!list_empty(&processed))
list_splice_tail(&processed, &offline_cgwbs);
spin_unlock_irq(&cgwb_lock);
}
/** * wb_memcg_offline - kill all wb's associated with a memcg being offlined * @memcg: memcg being offlined * * Also prevents creation of any new wb's associated with @memcg.
*/ void wb_memcg_offline(struct mem_cgroup *memcg)
{ struct list_head *memcg_cgwb_list = &memcg->cgwb_list; struct bdi_writeback *wb, *next;
/** * wb_blkcg_offline - kill all wb's associated with a blkcg being offlined * @css: blkcg being offlined * * Also prevents creation of any new wb's associated with @blkcg.
*/ void wb_blkcg_offline(struct cgroup_subsys_state *css)
{ struct bdi_writeback *wb, *next; struct list_head *list = blkcg_get_cgwb_list(css);
staticint __init cgwb_init(void)
{ /* * There can be many concurrent release work items overwhelming * system_wq. Put them in a separate wq and limit concurrency. * There's no point in executing many of these in parallel.
*/
cgwb_release_wq = alloc_workqueue("cgwb_release", 0, 1); if (!cgwb_release_wq) return -ENOMEM;
if (bdi->id > id)
p = &(*p)->rb_left; elseif (bdi->id < id)
p = &(*p)->rb_right; else break;
}
if (parentp)
*parentp = parent; return p;
}
/** * bdi_get_by_id - lookup and get bdi from its id * @id: bdi id to lookup * * Find bdi matching @id and get it. Returns NULL if the matching bdi * doesn't exist or is already unregistered.
*/ struct backing_dev_info *bdi_get_by_id(u64 id)
{ struct backing_dev_info *bdi = NULL; struct rb_node **p;
spin_lock_bh(&bdi_lock);
p = bdi_lookup_rb_node(id, NULL); if (*p) {
bdi = rb_entry(*p, struct backing_dev_info, rb_node);
bdi_get(bdi);
}
spin_unlock_bh(&bdi_lock);
/* * Remove bdi from bdi_list, and ensure that it is no longer visible
*/ staticvoid bdi_remove_from_list(struct backing_dev_info *bdi)
{
spin_lock_bh(&bdi_lock);
rb_erase(&bdi->rb_node, &bdi_tree);
list_del_rcu(&bdi->bdi_list);
spin_unlock_bh(&bdi_lock);
/* make sure nobody finds us on the bdi_list anymore */
bdi_remove_from_list(bdi);
wb_shutdown(&bdi->wb);
cgwb_bdi_unregister(bdi);
/* * If this BDI's min ratio has been set, use bdi_set_min_ratio() to * update the global bdi_min_ratio.
*/ if (bdi->min_ratio)
bdi_set_min_ratio(bdi, 0);
if (bdi->dev) {
bdi_debug_unregister(bdi);
device_unregister(bdi->dev);
bdi->dev = NULL;
}
if (bdi->owner) {
put_device(bdi->owner);
bdi->owner = NULL;
}
}
EXPORT_SYMBOL(bdi_unregister);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.