blkcg: clean up blkg_tryget_closest()
[sfrench/cifs-2.6.git] / block / bio.c
index 4f4d9884443b63a8f002ddd754ea467f9a0e4c16..9194d8ad3d5e23535e5752c18c8e7882a81c848e 100644 (file)
@@ -244,7 +244,7 @@ fallback:
 
 void bio_uninit(struct bio *bio)
 {
-       bio_disassociate_task(bio);
+       bio_disassociate_blkg(bio);
 }
 EXPORT_SYMBOL(bio_uninit);
 
@@ -571,14 +571,13 @@ void bio_put(struct bio *bio)
 }
 EXPORT_SYMBOL(bio_put);
 
-inline int bio_phys_segments(struct request_queue *q, struct bio *bio)
+int bio_phys_segments(struct request_queue *q, struct bio *bio)
 {
        if (unlikely(!bio_flagged(bio, BIO_SEG_VALID)))
                blk_recount_segments(q, bio);
 
        return bio->bi_phys_segments;
 }
-EXPORT_SYMBOL(bio_phys_segments);
 
 /**
  *     __bio_clone_fast - clone a bio that shares the original bio's biovec
@@ -610,7 +609,8 @@ void __bio_clone_fast(struct bio *bio, struct bio *bio_src)
        bio->bi_iter = bio_src->bi_iter;
        bio->bi_io_vec = bio_src->bi_io_vec;
 
-       bio_clone_blkcg_association(bio, bio_src);
+       bio_clone_blkg_association(bio, bio_src);
+       blkcg_bio_issue_init(bio);
 }
 EXPORT_SYMBOL(__bio_clone_fast);
 
@@ -901,7 +901,6 @@ int bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter)
 
        return 0;
 }
-EXPORT_SYMBOL_GPL(bio_iov_iter_get_pages);
 
 static void submit_bio_wait_endio(struct bio *bio)
 {
@@ -1591,7 +1590,6 @@ void bio_set_pages_dirty(struct bio *bio)
                        set_page_dirty_lock(bvec->bv_page);
        }
 }
-EXPORT_SYMBOL_GPL(bio_set_pages_dirty);
 
 static void bio_release_pages(struct bio *bio)
 {
@@ -1661,17 +1659,33 @@ defer:
        spin_unlock_irqrestore(&bio_dirty_lock, flags);
        schedule_work(&bio_dirty_work);
 }
-EXPORT_SYMBOL_GPL(bio_check_pages_dirty);
+
+void update_io_ticks(struct hd_struct *part, unsigned long now)
+{
+       unsigned long stamp;
+again:
+       stamp = READ_ONCE(part->stamp);
+       if (unlikely(stamp != now)) {
+               if (likely(cmpxchg(&part->stamp, stamp, now) == stamp)) {
+                       __part_stat_add(part, io_ticks, 1);
+               }
+       }
+       if (part->partno) {
+               part = &part_to_disk(part)->part0;
+               goto again;
+       }
+}
 
 void generic_start_io_acct(struct request_queue *q, int op,
                           unsigned long sectors, struct hd_struct *part)
 {
        const int sgrp = op_stat_group(op);
-       int cpu = part_stat_lock();
 
-       part_round_stats(q, cpu, part);
-       part_stat_inc(cpu, part, ios[sgrp]);
-       part_stat_add(cpu, part, sectors[sgrp], sectors);
+       part_stat_lock();
+
+       update_io_ticks(part, jiffies);
+       part_stat_inc(part, ios[sgrp]);
+       part_stat_add(part, sectors[sgrp], sectors);
        part_inc_in_flight(q, part, op_is_write(op));
 
        part_stat_unlock();
@@ -1681,12 +1695,15 @@ EXPORT_SYMBOL(generic_start_io_acct);
 void generic_end_io_acct(struct request_queue *q, int req_op,
                         struct hd_struct *part, unsigned long start_time)
 {
-       unsigned long duration = jiffies - start_time;
+       unsigned long now = jiffies;
+       unsigned long duration = now - start_time;
        const int sgrp = op_stat_group(req_op);
-       int cpu = part_stat_lock();
 
-       part_stat_add(cpu, part, nsecs[sgrp], jiffies_to_nsecs(duration));
-       part_round_stats(q, cpu, part);
+       part_stat_lock();
+
+       update_io_ticks(part, now);
+       part_stat_add(part, nsecs[sgrp], jiffies_to_nsecs(duration));
+       part_stat_add(part, time_in_queue, duration);
        part_dec_in_flight(q, part, op_is_write(req_op));
 
        part_stat_unlock();
@@ -1956,102 +1973,137 @@ EXPORT_SYMBOL(bioset_init_from_src);
 
 #ifdef CONFIG_BLK_CGROUP
 
-#ifdef CONFIG_MEMCG
 /**
- * bio_associate_blkcg_from_page - associate a bio with the page's blkcg
+ * bio_disassociate_blkg - puts back the blkg reference if associated
  * @bio: target bio
- * @page: the page to lookup the blkcg from
  *
- * Associate @bio with the blkcg from @page's owning memcg.  This works like
- * every other associate function wrt references.
+ * Helper to disassociate the blkg from @bio if a blkg is associated.
  */
-int bio_associate_blkcg_from_page(struct bio *bio, struct page *page)
+void bio_disassociate_blkg(struct bio *bio)
 {
-       struct cgroup_subsys_state *blkcg_css;
-
-       if (unlikely(bio->bi_css))
-               return -EBUSY;
-       if (!page->mem_cgroup)
-               return 0;
-       blkcg_css = cgroup_get_e_css(page->mem_cgroup->css.cgroup,
-                                    &io_cgrp_subsys);
-       bio->bi_css = blkcg_css;
-       return 0;
+       if (bio->bi_blkg) {
+               blkg_put(bio->bi_blkg);
+               bio->bi_blkg = NULL;
+       }
 }
-#endif /* CONFIG_MEMCG */
+EXPORT_SYMBOL_GPL(bio_disassociate_blkg);
 
 /**
- * bio_associate_blkcg - associate a bio with the specified blkcg
+ * __bio_associate_blkg - associate a bio with the a blkg
  * @bio: target bio
- * @blkcg_css: css of the blkcg to associate
+ * @blkg: the blkg to associate
  *
- * Associate @bio with the blkcg specified by @blkcg_css.  Block layer will
- * treat @bio as if it were issued by a task which belongs to the blkcg.
+ * This tries to associate @bio with the specified @blkg.  Association failure
+ * is handled by walking up the blkg tree.  Therefore, the blkg associated can
+ * be anything between @blkg and the root_blkg.  This situation only happens
+ * when a cgroup is dying and then the remaining bios will spill to the closest
+ * alive blkg.
  *
- * This function takes an extra reference of @blkcg_css which will be put
- * when @bio is released.  The caller must own @bio and is responsible for
- * synchronizing calls to this function.
+ * A reference will be taken on the @blkg and will be released when @bio is
+ * freed.
  */
-int bio_associate_blkcg(struct bio *bio, struct cgroup_subsys_state *blkcg_css)
+static void __bio_associate_blkg(struct bio *bio, struct blkcg_gq *blkg)
 {
-       if (unlikely(bio->bi_css))
-               return -EBUSY;
-       css_get(blkcg_css);
-       bio->bi_css = blkcg_css;
-       return 0;
+       bio_disassociate_blkg(bio);
+
+       bio->bi_blkg = blkg_tryget_closest(blkg);
 }
-EXPORT_SYMBOL_GPL(bio_associate_blkcg);
 
 /**
- * bio_associate_blkg - associate a bio with the specified blkg
+ * bio_associate_blkg_from_css - associate a bio with a specified css
  * @bio: target bio
- * @blkg: the blkg to associate
+ * @css: target css
  *
- * Associate @bio with the blkg specified by @blkg.  This is the queue specific
- * blkcg information associated with the @bio, a reference will be taken on the
- * @blkg and will be freed when the bio is freed.
+ * Associate @bio with the blkg found by combining the css's blkg and the
+ * request_queue of the @bio.  This falls back to the queue's root_blkg if
+ * the association fails with the css.
  */
-int bio_associate_blkg(struct bio *bio, struct blkcg_gq *blkg)
+void bio_associate_blkg_from_css(struct bio *bio,
+                                struct cgroup_subsys_state *css)
 {
-       if (unlikely(bio->bi_blkg))
-               return -EBUSY;
-       if (!blkg_try_get(blkg))
-               return -ENODEV;
-       bio->bi_blkg = blkg;
-       return 0;
+       struct request_queue *q = bio->bi_disk->queue;
+       struct blkcg_gq *blkg;
+
+       rcu_read_lock();
+
+       if (!css || !css->parent)
+               blkg = q->root_blkg;
+       else
+               blkg = blkg_lookup_create(css_to_blkcg(css), q);
+
+       __bio_associate_blkg(bio, blkg);
+
+       rcu_read_unlock();
 }
+EXPORT_SYMBOL_GPL(bio_associate_blkg_from_css);
 
+#ifdef CONFIG_MEMCG
 /**
- * bio_disassociate_task - undo bio_associate_current()
+ * bio_associate_blkg_from_page - associate a bio with the page's blkg
  * @bio: target bio
+ * @page: the page to lookup the blkcg from
+ *
+ * Associate @bio with the blkg from @page's owning memcg and the respective
+ * request_queue.  If cgroup_e_css returns %NULL, fall back to the queue's
+ * root_blkg.
  */
-void bio_disassociate_task(struct bio *bio)
+void bio_associate_blkg_from_page(struct bio *bio, struct page *page)
 {
-       if (bio->bi_ioc) {
-               put_io_context(bio->bi_ioc);
-               bio->bi_ioc = NULL;
-       }
-       if (bio->bi_css) {
-               css_put(bio->bi_css);
-               bio->bi_css = NULL;
-       }
-       if (bio->bi_blkg) {
-               blkg_put(bio->bi_blkg);
-               bio->bi_blkg = NULL;
-       }
+       struct cgroup_subsys_state *css;
+
+       if (!page->mem_cgroup)
+               return;
+
+       rcu_read_lock();
+
+       css = cgroup_e_css(page->mem_cgroup->css.cgroup, &io_cgrp_subsys);
+       bio_associate_blkg_from_css(bio, css);
+
+       rcu_read_unlock();
 }
+#endif /* CONFIG_MEMCG */
+
+/**
+ * bio_associate_blkg - associate a bio with a blkg
+ * @bio: target bio
+ *
+ * Associate @bio with the blkg found from the bio's css and request_queue.
+ * If one is not found, bio_lookup_blkg() creates the blkg.  If a blkg is
+ * already associated, the css is reused and association redone as the
+ * request_queue may have changed.
+ */
+void bio_associate_blkg(struct bio *bio)
+{
+       struct cgroup_subsys_state *css;
+
+       rcu_read_lock();
+
+       if (bio->bi_blkg)
+               css = &bio_blkcg(bio)->css;
+       else
+               css = blkcg_css();
+
+       bio_associate_blkg_from_css(bio, css);
+
+       rcu_read_unlock();
+}
+EXPORT_SYMBOL_GPL(bio_associate_blkg);
 
 /**
- * bio_clone_blkcg_association - clone blkcg association from src to dst bio
+ * bio_clone_blkg_association - clone blkg association from src to dst bio
  * @dst: destination bio
  * @src: source bio
  */
-void bio_clone_blkcg_association(struct bio *dst, struct bio *src)
+void bio_clone_blkg_association(struct bio *dst, struct bio *src)
 {
-       if (src->bi_css)
-               WARN_ON(bio_associate_blkcg(dst, src->bi_css));
+       rcu_read_lock();
+
+       if (src->bi_blkg)
+               __bio_associate_blkg(dst, src->bi_blkg);
+
+       rcu_read_unlock();
 }
-EXPORT_SYMBOL_GPL(bio_clone_blkcg_association);
+EXPORT_SYMBOL_GPL(bio_clone_blkg_association);
 #endif /* CONFIG_BLK_CGROUP */
 
 static void __init biovec_init_slabs(void)