|  | @@ -508,3 +508,106 @@ u64 blkg_prfill_rwstat(struct seq_file *sf, struct blkg_policy_data *pd,
 | 
	
		
			
				|  |  |  		       int off)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  |  	struct blkg_rwstat rwstat = blkg_rwstat_read((void *)pd + off);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	return __blkg_prfill_rwstat(sf, pd, &rwstat);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +EXPORT_SYMBOL_GPL(blkg_prfill_rwstat);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * blkg_conf_prep - parse and prepare for per-blkg config update
 | 
	
		
			
				|  |  | + * @blkcg: target block cgroup
 | 
	
		
			
				|  |  | + * @pol: target policy
 | 
	
		
			
				|  |  | + * @input: input string
 | 
	
		
			
				|  |  | + * @ctx: blkg_conf_ctx to be filled
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * Parse per-blkg config update from @input and initialize @ctx with the
 | 
	
		
			
				|  |  | + * result.  @ctx->blkg points to the blkg to be updated and @ctx->v the new
 | 
	
		
			
				|  |  | + * value.  This function returns with RCU read lock and queue lock held and
 | 
	
		
			
				|  |  | + * must be paired with blkg_conf_finish().
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +int blkg_conf_prep(struct blkcg *blkcg, const struct blkcg_policy *pol,
 | 
	
		
			
				|  |  | +		   const char *input, struct blkg_conf_ctx *ctx)
 | 
	
		
			
				|  |  | +	__acquires(rcu) __acquires(disk->queue->queue_lock)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	struct gendisk *disk;
 | 
	
		
			
				|  |  | +	struct blkcg_gq *blkg;
 | 
	
		
			
				|  |  | +	unsigned int major, minor;
 | 
	
		
			
				|  |  | +	unsigned long long v;
 | 
	
		
			
				|  |  | +	int part, ret;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if (sscanf(input, "%u:%u %llu", &major, &minor, &v) != 3)
 | 
	
		
			
				|  |  | +		return -EINVAL;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	disk = get_gendisk(MKDEV(major, minor), &part);
 | 
	
		
			
				|  |  | +	if (!disk || part)
 | 
	
		
			
				|  |  | +		return -EINVAL;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	rcu_read_lock();
 | 
	
		
			
				|  |  | +	spin_lock_irq(disk->queue->queue_lock);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if (blkcg_policy_enabled(disk->queue, pol))
 | 
	
		
			
				|  |  | +		blkg = blkg_lookup_create(blkcg, disk->queue);
 | 
	
		
			
				|  |  | +	else
 | 
	
		
			
				|  |  | +		blkg = ERR_PTR(-EINVAL);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if (IS_ERR(blkg)) {
 | 
	
		
			
				|  |  | +		ret = PTR_ERR(blkg);
 | 
	
		
			
				|  |  | +		rcu_read_unlock();
 | 
	
		
			
				|  |  | +		spin_unlock_irq(disk->queue->queue_lock);
 | 
	
		
			
				|  |  | +		put_disk(disk);
 | 
	
		
			
				|  |  | +		/*
 | 
	
		
			
				|  |  | +		 * If queue was bypassing, we should retry.  Do so after a
 | 
	
		
			
				|  |  | +		 * short msleep().  It isn't strictly necessary but queue
 | 
	
		
			
				|  |  | +		 * can be bypassing for some time and it's always nice to
 | 
	
		
			
				|  |  | +		 * avoid busy looping.
 | 
	
		
			
				|  |  | +		 */
 | 
	
		
			
				|  |  | +		if (ret == -EBUSY) {
 | 
	
		
			
				|  |  | +			msleep(10);
 | 
	
		
			
				|  |  | +			ret = restart_syscall();
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +		return ret;
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	ctx->disk = disk;
 | 
	
		
			
				|  |  | +	ctx->blkg = blkg;
 | 
	
		
			
				|  |  | +	ctx->v = v;
 | 
	
		
			
				|  |  | +	return 0;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +EXPORT_SYMBOL_GPL(blkg_conf_prep);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * blkg_conf_finish - finish up per-blkg config update
 | 
	
		
			
				|  |  | + * @ctx: blkg_conf_ctx intiailized by blkg_conf_prep()
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * Finish up after per-blkg config update.  This function must be paired
 | 
	
		
			
				|  |  | + * with blkg_conf_prep().
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +void blkg_conf_finish(struct blkg_conf_ctx *ctx)
 | 
	
		
			
				|  |  | +	__releases(ctx->disk->queue->queue_lock) __releases(rcu)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	spin_unlock_irq(ctx->disk->queue->queue_lock);
 | 
	
		
			
				|  |  | +	rcu_read_unlock();
 | 
	
		
			
				|  |  | +	put_disk(ctx->disk);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +EXPORT_SYMBOL_GPL(blkg_conf_finish);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +struct cftype blkcg_files[] = {
 | 
	
		
			
				|  |  | +	{
 | 
	
		
			
				|  |  | +		.name = "reset_stats",
 | 
	
		
			
				|  |  | +		.write_u64 = blkcg_reset_stats,
 | 
	
		
			
				|  |  | +	},
 | 
	
		
			
				|  |  | +	{ }	/* terminate */
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * blkcg_css_offline - cgroup css_offline callback
 | 
	
		
			
				|  |  | + * @cgroup: cgroup of interest
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * This function is called when @cgroup is about to go away and responsible
 | 
	
		
			
				|  |  | + * for shooting down all blkgs associated with @cgroup.  blkgs should be
 | 
	
		
			
				|  |  | + * removed while holding both q and blkcg locks.  As blkcg lock is nested
 | 
	
		
			
				|  |  | + * inside q lock, this function performs reverse double lock dancing.
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * This is the blkcg counterpart of ioc_release_fn().
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +static void blkcg_css_offline(struct cgroup *cgroup)
 |