All of lore.kernel.org
 help / color / mirror / Atom feed
From: merez@codeaurora.org
Cc: Maya Erez <merez@codeaurora.org>,
	linux-mmc@vger.kernel.org, linux-arm-msm@vger.kernel.org,
	Jaehoon Chung <jh80.chung@samsung.com>,
	open list <linux-kernel@vger.kernel.org>
Subject: Re: [PATCH v2] mmc: core: Add support for idle time BKOPS
Date: Tue, 6 Nov 2012 20:28:54 -0800 (PST)	[thread overview]
Message-ID: <24cf15558496fc4dff9035181df0a9bb.squirrel@www.codeaurora.org> (raw)
In-Reply-To: <507D1295.5000908@samsung.com>

Hi Jaehoon,

Any update on this patch review and testing?

Thanks,
Maya
On Mon, October 15, 2012 11:53 pm, Jaehoon Chung wrote:
> Hi Maya,
>
> I'm testing with your patch..but i need to have the more time for testing.
> In now, it looks good to me. Thank you for working the idle bkops.
>
> Best Regards,
> Jaehoon Chung
>
>> ---
>> diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
>> index 172a768..ed040d5 100644
>> --- a/drivers/mmc/card/block.c
>> +++ b/drivers/mmc/card/block.c
>> @@ -827,6 +827,9 @@ static int mmc_blk_issue_discard_rq(struct mmc_queue
>> *mq, struct request *req)
>>  	from = blk_rq_pos(req);
>>  	nr = blk_rq_sectors(req);
>>
>> +	if (card->ext_csd.bkops_en)
>> +		card->bkops_info.sectors_changed += blk_rq_sectors(req);
> using nr?
>> +
>>  	if (mmc_can_discard(card))
>>  		arg = MMC_DISCARD_ARG;
>>  	else if (mmc_can_trim(card))
>> @@ -1268,6 +1271,9 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue
>> *mq, struct request *rqc)
>>  	if (!rqc && !mq->mqrq_prev->req)
>>  		return 0;
>>
>> +	if (rqc && (card->ext_csd.bkops_en) && (rq_data_dir(rqc) == WRITE))
>> +			card->bkops_info.sectors_changed += blk_rq_sectors(rqc);
> Fix the indent.
>> +
>>  	do {
>>  		if (rqc) {
>>  			/*
>> diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
>> index e360a97..e96f5cf 100644
>> --- a/drivers/mmc/card/queue.c
>> +++ b/drivers/mmc/card/queue.c
>> @@ -51,6 +51,7 @@ static int mmc_queue_thread(void *d)
>>  {
>>  	struct mmc_queue *mq = d;
>>  	struct request_queue *q = mq->queue;
>> +	struct mmc_card *card = mq->card;
>>
>>  	current->flags |= PF_MEMALLOC;
>>
>> @@ -66,6 +67,17 @@ static int mmc_queue_thread(void *d)
>>  		spin_unlock_irq(q->queue_lock);
>>
>>  		if (req || mq->mqrq_prev->req) {
>> +			/*
>> +			 * If this is the first request, BKOPs might be in
>> +			 * progress and needs to be stopped before issuing the
>> +			 * request
>> +			 */
>> +			if (card->ext_csd.bkops_en &&
>> +			    card->bkops_info.started_delayed_bkops) {
>> +				card->bkops_info.started_delayed_bkops = false;
>> +				mmc_stop_bkops(card);
> if mmc_stop_bkops is failed..?
>> +			}
>> +
>>  			set_current_state(TASK_RUNNING);
>>  			mq->issue_fn(mq, req);
>>  		} else {
>> @@ -73,6 +85,7 @@ static int mmc_queue_thread(void *d)
>>  				set_current_state(TASK_RUNNING);
>>  				break;
>>  			}
>> +			mmc_start_delayed_bkops(card);
>>  			up(&mq->thread_sem);
>>  			schedule();
>>  			down(&mq->thread_sem);
>> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
>> index 6612163..fd8783d 100644
>> --- a/drivers/mmc/core/core.c
>> +++ b/drivers/mmc/core/core.c
>> @@ -253,9 +253,42 @@ mmc_start_request(struct mmc_host *host, struct
>> mmc_request *mrq)
>>  }
>>
>>  /**
>> + * mmc_start_delayed_bkops() - Start a delayed work to check for
>> + *      the need of non urgent BKOPS
>> + *
>> + * @card: MMC card to start BKOPS on
>> + */
>> +void mmc_start_delayed_bkops(struct mmc_card *card)
>> +{
>> +	if (!card || !card->ext_csd.bkops_en || mmc_card_doing_bkops(card))
>> +		return;
>> +
>> +	if (card->bkops_info.sectors_changed <
>> +	    BKOPS_MIN_SECTORS_TO_QUEUE_DELAYED_WORK)
>> +		return;
>> +
>> +	pr_debug("%s: %s: queueing delayed_bkops_work\n",
>> +		 mmc_hostname(card->host), __func__);
>> +
>> +	card->bkops_info.sectors_changed = 0;
>> +
>> +	/*
>> +	 * cancel_delayed_bkops_work will prevent a race condition between
>> +	 * fetching a request by the mmcqd and the delayed work, in case
>> +	 * it was removed from the queue work but not started yet
>> +	 */
>> +	card->bkops_info.cancel_delayed_work = false;
>> +	card->bkops_info.started_delayed_bkops = true;
>> +	queue_delayed_work(system_nrt_wq, &card->bkops_info.dw,
>> +			   msecs_to_jiffies(
>> +				   card->bkops_info.delay_ms));
>> +}
>> +EXPORT_SYMBOL(mmc_start_delayed_bkops);
>> +
>> +/**
>>   *	mmc_start_bkops - start BKOPS for supported cards
>>   *	@card: MMC card to start BKOPS
>> - *	@form_exception: A flag to indicate if this function was
>> + *	@from_exception: A flag to indicate if this function was
>>   *			 called due to an exception raised by the card
>>   *
>>   *	Start background operations whenever requested.
>> @@ -269,25 +302,47 @@ void mmc_start_bkops(struct mmc_card *card, bool
>> from_exception)
>>  	bool use_busy_signal;
>>
>>  	BUG_ON(!card);
>> -
>> -	if (!card->ext_csd.bkops_en || mmc_card_doing_bkops(card))
>> +	if (!card->ext_csd.bkops_en)
>>  		return;
>>
>> +	mmc_claim_host(card->host);
>> +
>> +	if ((card->bkops_info.cancel_delayed_work) && !from_exception) {
>> +		pr_debug("%s: %s: cancel_delayed_work was set, exit\n",
>> +			 mmc_hostname(card->host), __func__);
>> +		card->bkops_info.cancel_delayed_work = false;
>> +		goto out;
>> +	}
>> +
>> +	if (mmc_card_doing_bkops(card)) {
>> +		pr_debug("%s: %s: already doing bkops, exit\n",
>> +			 mmc_hostname(card->host), __func__);
>> +		goto out;
>> +	}
>> +
>>  	err = mmc_read_bkops_status(card);
>>  	if (err) {
>>  		pr_err("%s: Failed to read bkops status: %d\n",
>>  		       mmc_hostname(card->host), err);
>> -		return;
>> +		goto out;
>>  	}
>>
>>  	if (!card->ext_csd.raw_bkops_status)
>> -		return;
>> +		goto out;
>> +
>> +	pr_info("%s: %s: card->ext_csd.raw_bkops_status = 0x%x\n",
>> +		mmc_hostname(card->host), __func__,
>> +		card->ext_csd.raw_bkops_status);
>>
>> +	/*
>> +	 * If the function was called due to exception but there is no need
>> +	 * for urgent BKOPS, BKOPs will be performed by the delayed BKOPs
>> +	 * work, before going to suspend
>> +	 */
>>  	if (card->ext_csd.raw_bkops_status < EXT_CSD_BKOPS_LEVEL_2 &&
>>  	    from_exception)
>> -		return;
>> +		goto out;
>>
>> -	mmc_claim_host(card->host);
>>  	if (card->ext_csd.raw_bkops_status >= EXT_CSD_BKOPS_LEVEL_2) {
>>  		timeout = MMC_BKOPS_MAX_TIMEOUT;
>>  		use_busy_signal = true;
>> @@ -309,13 +364,108 @@ void mmc_start_bkops(struct mmc_card *card, bool
>> from_exception)
>>  	 * bkops executed synchronously, otherwise
>>  	 * the operation is in progress
>>  	 */
>> -	if (!use_busy_signal)
>> +	if (!use_busy_signal) {
>>  		mmc_card_set_doing_bkops(card);
>> +		pr_debug("%s: %s: starting the polling thread\n",
>> +			 mmc_hostname(card->host), __func__);
>> +		queue_work(system_nrt_wq,
>> +			   &card->bkops_info.poll_for_completion);
>> +	}
>> +
>>  out:
>>  	mmc_release_host(card->host);
>>  }
>>  EXPORT_SYMBOL(mmc_start_bkops);
>>
>> +/**
>> + * mmc_bkops_completion_polling() - Poll on the card status to
>> + * wait for the non-blocking BKOPS completion
>> + * @work:	The completion polling work
>> + *
>> + * The on-going reading of the card status will prevent the card
>> + * from getting into suspend while it is in the middle of
>> + * performing BKOPS.
>> + * Since the non blocking BKOPS can be interrupted by a fetched
>> + * request we also check IF mmc_card_doing_bkops in each
>> + * iteration.
>> + */
>> +void mmc_bkops_completion_polling(struct work_struct *work)
>> +{
>> +	struct mmc_card *card = container_of(work, struct mmc_card,
>> +			bkops_info.poll_for_completion);
>> +	unsigned long timeout_jiffies = jiffies +
>> +		msecs_to_jiffies(BKOPS_COMPLETION_POLLING_TIMEOUT_MS);
>> +	u32 status;
>> +	int err;
>> +
>> +	/*
>> +	 * Wait for the BKOPs to complete. Keep reading the status to prevent
>> +	 * the host from getting into suspend
>> +	 */
>> +	do {
>> +		mmc_claim_host(card->host);
>> +
>> +		if (!mmc_card_doing_bkops(card))
>> +			goto out;
>> +
>> +		err = mmc_send_status(card, &status);
>> +		if (err) {
>> +			pr_err("%s: error %d requesting status\n",
>> +			       mmc_hostname(card->host), err);
>> +			goto out;
>> +		}
>> +
>> +		/*
>> +		 * Some cards mishandle the status bits, so make sure to check
>> +		 * both the busy indication and the card state.
>> +		 */
>> +		if ((status & R1_READY_FOR_DATA) &&
>> +		    (R1_CURRENT_STATE(status) != R1_STATE_PRG)) {
>> +			pr_debug("%s: %s: completed BKOPs, exit polling\n",
>> +				 mmc_hostname(card->host), __func__);
>> +			mmc_card_clr_doing_bkops(card);
>> +			card->bkops_info.started_delayed_bkops = false;
>> +			goto out;
>> +		}
>> +
>> +		mmc_release_host(card->host);
>> +
>> +		/*
>> +		 * Sleep before checking the card status again to allow the
>> +		 * card to complete the BKOPs operation
>> +		 */
>> +		msleep(BKOPS_COMPLETION_POLLING_INTERVAL_MS);
>> +	} while (time_before(jiffies, timeout_jiffies));
>> +
>> +	pr_err("%s: %s: exit polling due to timeout\n",
>> +	       mmc_hostname(card->host), __func__);
>> +
>> +	return;
>> +out:
>> +	mmc_release_host(card->host);
>> +}
>> +
>> +/**
>> + * mmc_start_idle_time_bkops() - check if a non urgent BKOPS is
>> + * needed
>> + * @work:	The idle time BKOPS work
>> + */
>> +void mmc_start_idle_time_bkops(struct work_struct *work)
>> +{
>> +	struct mmc_card *card = container_of(work, struct mmc_card,
>> +			bkops_info.dw.work);
>> +
>> +	/*
>> +	 * Prevent a race condition between mmc_stop_bkops and the delayed
>> +	 * BKOPS work in case the delayed work is executed on another CPU
>> +	 */
>> +	if (card->bkops_info.cancel_delayed_work)
>> +		return;
>> +
>> +	mmc_start_bkops(card, false);
>> +}
>> +EXPORT_SYMBOL(mmc_start_idle_time_bkops);
>> +
>>  static void mmc_wait_done(struct mmc_request *mrq)
>>  {
>>  	complete(&mrq->completion);
>> @@ -582,6 +732,19 @@ int mmc_stop_bkops(struct mmc_card *card)
>>  	int err = 0;
>>
>>  	BUG_ON(!card);
>> +
>> +	mmc_claim_host(card->host);
>> +
>> +	/*
>> +	 * Notify the delayed work to be cancelled, in case it was already
>> +	 * removed from the queue, but was not started yet
>> +	 */
>> +	card->bkops_info.cancel_delayed_work = true;
>> +	if (delayed_work_pending(&card->bkops_info.dw))
>> +		cancel_delayed_work_sync(&card->bkops_info.dw);
>> +	if (!mmc_card_doing_bkops(card))
>> +		goto out;
>> +
>>  	err = mmc_interrupt_hpi(card);
>>
>>  	/*
>> @@ -593,6 +756,8 @@ int mmc_stop_bkops(struct mmc_card *card)
>>  		err = 0;
>>  	}
>>
>> +out:
>> +	mmc_release_host(card->host);
>>  	return err;
>>  }
>>  EXPORT_SYMBOL(mmc_stop_bkops);
>> @@ -2566,15 +2731,13 @@ int mmc_pm_notify(struct notifier_block
>> *notify_block,
>>  	switch (mode) {
>>  	case PM_HIBERNATION_PREPARE:
>>  	case PM_SUSPEND_PREPARE:
>> -		if (host->card && mmc_card_mmc(host->card) &&
>> -		    mmc_card_doing_bkops(host->card)) {
>> +		if (host->card && mmc_card_mmc(host->card)) {
>>  			err = mmc_stop_bkops(host->card);
>>  			if (err) {
>>  				pr_err("%s: didn't stop bkops\n",
>>  					mmc_hostname(host));
>>  				return err;
>>  			}
>> -			mmc_card_clr_doing_bkops(host->card);
>>  		}
>>
>>  		spin_lock_irqsave(&host->lock, flags);
>> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
>> index 7509de1..f8ff640 100644
>> --- a/drivers/mmc/core/mmc.c
>> +++ b/drivers/mmc/core/mmc.c
>> @@ -1258,6 +1258,30 @@ static int mmc_init_card(struct mmc_host *host,
>> u32 ocr,
>>  		}
>>  	}
>>
>> +	if (!oldcard) {
>> +		if (card->ext_csd.bkops_en) {
>> +			INIT_DELAYED_WORK(&card->bkops_info.dw,
>> +					  mmc_start_idle_time_bkops);
>> +			INIT_WORK(&card->bkops_info.poll_for_completion,
>> +				  mmc_bkops_completion_polling);
>> +
>> +			/*
>> +			 * Calculate the time to start the BKOPs checking.
>> +			 * The idle time of the host controller should be taken
>> +			 * into account in order to prevent a race condition
>> +			 * before starting BKOPs and going into suspend.
>> +			 * If the host controller didn't set its idle time,
>> +			 * a default value is used.
>> +			 */
>> +			card->bkops_info.delay_ms = MMC_IDLE_BKOPS_TIME_MS;
>> +			if (card->bkops_info.host_suspend_tout_ms)
>> +				card->bkops_info.delay_ms = min(
>> +					card->bkops_info.delay_ms,
>> +				      card->bkops_info.host_suspend_tout_ms/2);
>> +
>> +		}
>> +	}
>> +
>>  	if (!oldcard)
>>  		host->card = card;
>>
>> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
>> index 78cc3be..0e4d55a 100644
>> --- a/include/linux/mmc/card.h
>> +++ b/include/linux/mmc/card.h
>> @@ -207,6 +207,48 @@ struct mmc_part {
>>  #define MMC_BLK_DATA_AREA_GP	(1<<2)
>>  };
>>
>> +/**
>> + * struct mmc_bkops_info - BKOPS data
>> + * @dw:	Idle time bkops delayed work
>> + * @host_suspend_tout_ms:	The host controller idle time,
>> + * before getting into suspend
>> + * @delay_ms:	The time to start the BKOPS
>> + *        delayed work once MMC thread is idle
>> + * @poll_for_completion:	Poll on BKOPS completion
>> + * @cancel_delayed_work: A flag to indicate if the delayed work
>> + *        should be cancelled
>> + * @started_delayed_bkops:  A flag to indicate if the delayed
>> + *        work was scheduled
>> + * @sectors_changed:  number of  sectors written or
>> + *       discard since the last idle BKOPS were scheduled
>> + */
>> +struct mmc_bkops_info {
>> +	struct delayed_work	dw;
>> +	unsigned int		host_suspend_tout_ms;
>> +	unsigned int		delay_ms;
>> +/*
>> + * A default time for checking the need for non urgent BKOPS once mmcqd
>> + * is idle.
>> + */
>> +#define MMC_IDLE_BKOPS_TIME_MS 2000
>> +	struct work_struct	poll_for_completion;
>> +/* Polling timeout and interval for waiting on non-blocking BKOPs
>> completion */
>> +#define BKOPS_COMPLETION_POLLING_TIMEOUT_MS 10000 /* in ms */
>> +#define BKOPS_COMPLETION_POLLING_INTERVAL_MS 1000 /* in ms */
>> +	bool			cancel_delayed_work;
>> +	bool			started_delayed_bkops;
>> +	unsigned int		sectors_changed;
>> +/*
>> + * Since canceling the delayed work might have significant effect on
>> the
>> + * performance of small requests we won't queue the delayed work every
>> time
>> + * mmcqd thread is idle.
>> + * The delayed work for idle BKOPS will be scheduled only after a
>> significant
>> + * amount of write or discard data.
>> + * 100MB is chosen based on benchmark tests.
>> + */
>> +#define BKOPS_MIN_SECTORS_TO_QUEUE_DELAYED_WORK 204800 /* 100MB */
>> +};
>> +
>>  /*
>>   * MMC device
>>   */
>> @@ -281,6 +323,8 @@ struct mmc_card {
>>  	struct dentry		*debugfs_root;
>>  	struct mmc_part	part[MMC_NUM_PHY_PARTITION]; /* physical partitions */
>>  	unsigned int    nr_parts;
>> +
>> +	struct mmc_bkops_info	bkops_info;
>>  };
>>
>>  /*
>> diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
>> index 9b9cdaf..665d345 100644
>> --- a/include/linux/mmc/core.h
>> +++ b/include/linux/mmc/core.h
>> @@ -145,6 +145,9 @@ extern int mmc_app_cmd(struct mmc_host *, struct
>> mmc_card *);
>>  extern int mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *,
>>  	struct mmc_command *, int);
>>  extern void mmc_start_bkops(struct mmc_card *card, bool
>> from_exception);
>> +extern void mmc_start_delayed_bkops(struct mmc_card *card);
>> +extern void mmc_start_idle_time_bkops(struct work_struct *work);
>> +extern void mmc_bkops_completion_polling(struct work_struct *work);
>>  extern int __mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int,
>> bool);
>>  extern int mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int);
>>
>>
>
>


-- 
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member
of Code Aurora Forum, hosted by The Linux Foundation

WARNING: multiple messages have this Message-ID (diff)
From: merez@codeaurora.org
To: "Jaehoon Chung" <jh80.chung@samsung.com>
Cc: "Maya Erez" <merez@codeaurora.org>,
	linux-mmc@vger.kernel.org, linux-arm-msm@vger.kernel.org,
	"Jaehoon Chung" <jh80.chung@samsung.com>,
	"open list" <linux-kernel@vger.kernel.org>
Subject: Re: [PATCH v2] mmc: core: Add support for idle time BKOPS
Date: Tue, 6 Nov 2012 20:28:54 -0800 (PST)	[thread overview]
Message-ID: <24cf15558496fc4dff9035181df0a9bb.squirrel@www.codeaurora.org> (raw)
In-Reply-To: <507D1295.5000908@samsung.com>

Hi Jaehoon,

Any update on this patch review and testing?

Thanks,
Maya
On Mon, October 15, 2012 11:53 pm, Jaehoon Chung wrote:
> Hi Maya,
>
> I'm testing with your patch..but i need to have the more time for testing.
> In now, it looks good to me. Thank you for working the idle bkops.
>
> Best Regards,
> Jaehoon Chung
>
>> ---
>> diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
>> index 172a768..ed040d5 100644
>> --- a/drivers/mmc/card/block.c
>> +++ b/drivers/mmc/card/block.c
>> @@ -827,6 +827,9 @@ static int mmc_blk_issue_discard_rq(struct mmc_queue
>> *mq, struct request *req)
>>  	from = blk_rq_pos(req);
>>  	nr = blk_rq_sectors(req);
>>
>> +	if (card->ext_csd.bkops_en)
>> +		card->bkops_info.sectors_changed += blk_rq_sectors(req);
> using nr?
>> +
>>  	if (mmc_can_discard(card))
>>  		arg = MMC_DISCARD_ARG;
>>  	else if (mmc_can_trim(card))
>> @@ -1268,6 +1271,9 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue
>> *mq, struct request *rqc)
>>  	if (!rqc && !mq->mqrq_prev->req)
>>  		return 0;
>>
>> +	if (rqc && (card->ext_csd.bkops_en) && (rq_data_dir(rqc) == WRITE))
>> +			card->bkops_info.sectors_changed += blk_rq_sectors(rqc);
> Fix the indent.
>> +
>>  	do {
>>  		if (rqc) {
>>  			/*
>> diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
>> index e360a97..e96f5cf 100644
>> --- a/drivers/mmc/card/queue.c
>> +++ b/drivers/mmc/card/queue.c
>> @@ -51,6 +51,7 @@ static int mmc_queue_thread(void *d)
>>  {
>>  	struct mmc_queue *mq = d;
>>  	struct request_queue *q = mq->queue;
>> +	struct mmc_card *card = mq->card;
>>
>>  	current->flags |= PF_MEMALLOC;
>>
>> @@ -66,6 +67,17 @@ static int mmc_queue_thread(void *d)
>>  		spin_unlock_irq(q->queue_lock);
>>
>>  		if (req || mq->mqrq_prev->req) {
>> +			/*
>> +			 * If this is the first request, BKOPs might be in
>> +			 * progress and needs to be stopped before issuing the
>> +			 * request
>> +			 */
>> +			if (card->ext_csd.bkops_en &&
>> +			    card->bkops_info.started_delayed_bkops) {
>> +				card->bkops_info.started_delayed_bkops = false;
>> +				mmc_stop_bkops(card);
> if mmc_stop_bkops is failed..?
>> +			}
>> +
>>  			set_current_state(TASK_RUNNING);
>>  			mq->issue_fn(mq, req);
>>  		} else {
>> @@ -73,6 +85,7 @@ static int mmc_queue_thread(void *d)
>>  				set_current_state(TASK_RUNNING);
>>  				break;
>>  			}
>> +			mmc_start_delayed_bkops(card);
>>  			up(&mq->thread_sem);
>>  			schedule();
>>  			down(&mq->thread_sem);
>> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
>> index 6612163..fd8783d 100644
>> --- a/drivers/mmc/core/core.c
>> +++ b/drivers/mmc/core/core.c
>> @@ -253,9 +253,42 @@ mmc_start_request(struct mmc_host *host, struct
>> mmc_request *mrq)
>>  }
>>
>>  /**
>> + * mmc_start_delayed_bkops() - Start a delayed work to check for
>> + *      the need of non urgent BKOPS
>> + *
>> + * @card: MMC card to start BKOPS on
>> + */
>> +void mmc_start_delayed_bkops(struct mmc_card *card)
>> +{
>> +	if (!card || !card->ext_csd.bkops_en || mmc_card_doing_bkops(card))
>> +		return;
>> +
>> +	if (card->bkops_info.sectors_changed <
>> +	    BKOPS_MIN_SECTORS_TO_QUEUE_DELAYED_WORK)
>> +		return;
>> +
>> +	pr_debug("%s: %s: queueing delayed_bkops_work\n",
>> +		 mmc_hostname(card->host), __func__);
>> +
>> +	card->bkops_info.sectors_changed = 0;
>> +
>> +	/*
>> +	 * cancel_delayed_bkops_work will prevent a race condition between
>> +	 * fetching a request by the mmcqd and the delayed work, in case
>> +	 * it was removed from the queue work but not started yet
>> +	 */
>> +	card->bkops_info.cancel_delayed_work = false;
>> +	card->bkops_info.started_delayed_bkops = true;
>> +	queue_delayed_work(system_nrt_wq, &card->bkops_info.dw,
>> +			   msecs_to_jiffies(
>> +				   card->bkops_info.delay_ms));
>> +}
>> +EXPORT_SYMBOL(mmc_start_delayed_bkops);
>> +
>> +/**
>>   *	mmc_start_bkops - start BKOPS for supported cards
>>   *	@card: MMC card to start BKOPS
>> - *	@form_exception: A flag to indicate if this function was
>> + *	@from_exception: A flag to indicate if this function was
>>   *			 called due to an exception raised by the card
>>   *
>>   *	Start background operations whenever requested.
>> @@ -269,25 +302,47 @@ void mmc_start_bkops(struct mmc_card *card, bool
>> from_exception)
>>  	bool use_busy_signal;
>>
>>  	BUG_ON(!card);
>> -
>> -	if (!card->ext_csd.bkops_en || mmc_card_doing_bkops(card))
>> +	if (!card->ext_csd.bkops_en)
>>  		return;
>>
>> +	mmc_claim_host(card->host);
>> +
>> +	if ((card->bkops_info.cancel_delayed_work) && !from_exception) {
>> +		pr_debug("%s: %s: cancel_delayed_work was set, exit\n",
>> +			 mmc_hostname(card->host), __func__);
>> +		card->bkops_info.cancel_delayed_work = false;
>> +		goto out;
>> +	}
>> +
>> +	if (mmc_card_doing_bkops(card)) {
>> +		pr_debug("%s: %s: already doing bkops, exit\n",
>> +			 mmc_hostname(card->host), __func__);
>> +		goto out;
>> +	}
>> +
>>  	err = mmc_read_bkops_status(card);
>>  	if (err) {
>>  		pr_err("%s: Failed to read bkops status: %d\n",
>>  		       mmc_hostname(card->host), err);
>> -		return;
>> +		goto out;
>>  	}
>>
>>  	if (!card->ext_csd.raw_bkops_status)
>> -		return;
>> +		goto out;
>> +
>> +	pr_info("%s: %s: card->ext_csd.raw_bkops_status = 0x%x\n",
>> +		mmc_hostname(card->host), __func__,
>> +		card->ext_csd.raw_bkops_status);
>>
>> +	/*
>> +	 * If the function was called due to exception but there is no need
>> +	 * for urgent BKOPS, BKOPs will be performed by the delayed BKOPs
>> +	 * work, before going to suspend
>> +	 */
>>  	if (card->ext_csd.raw_bkops_status < EXT_CSD_BKOPS_LEVEL_2 &&
>>  	    from_exception)
>> -		return;
>> +		goto out;
>>
>> -	mmc_claim_host(card->host);
>>  	if (card->ext_csd.raw_bkops_status >= EXT_CSD_BKOPS_LEVEL_2) {
>>  		timeout = MMC_BKOPS_MAX_TIMEOUT;
>>  		use_busy_signal = true;
>> @@ -309,13 +364,108 @@ void mmc_start_bkops(struct mmc_card *card, bool
>> from_exception)
>>  	 * bkops executed synchronously, otherwise
>>  	 * the operation is in progress
>>  	 */
>> -	if (!use_busy_signal)
>> +	if (!use_busy_signal) {
>>  		mmc_card_set_doing_bkops(card);
>> +		pr_debug("%s: %s: starting the polling thread\n",
>> +			 mmc_hostname(card->host), __func__);
>> +		queue_work(system_nrt_wq,
>> +			   &card->bkops_info.poll_for_completion);
>> +	}
>> +
>>  out:
>>  	mmc_release_host(card->host);
>>  }
>>  EXPORT_SYMBOL(mmc_start_bkops);
>>
>> +/**
>> + * mmc_bkops_completion_polling() - Poll on the card status to
>> + * wait for the non-blocking BKOPS completion
>> + * @work:	The completion polling work
>> + *
>> + * The on-going reading of the card status will prevent the card
>> + * from getting into suspend while it is in the middle of
>> + * performing BKOPS.
>> + * Since the non blocking BKOPS can be interrupted by a fetched
>> + * request we also check IF mmc_card_doing_bkops in each
>> + * iteration.
>> + */
>> +void mmc_bkops_completion_polling(struct work_struct *work)
>> +{
>> +	struct mmc_card *card = container_of(work, struct mmc_card,
>> +			bkops_info.poll_for_completion);
>> +	unsigned long timeout_jiffies = jiffies +
>> +		msecs_to_jiffies(BKOPS_COMPLETION_POLLING_TIMEOUT_MS);
>> +	u32 status;
>> +	int err;
>> +
>> +	/*
>> +	 * Wait for the BKOPs to complete. Keep reading the status to prevent
>> +	 * the host from getting into suspend
>> +	 */
>> +	do {
>> +		mmc_claim_host(card->host);
>> +
>> +		if (!mmc_card_doing_bkops(card))
>> +			goto out;
>> +
>> +		err = mmc_send_status(card, &status);
>> +		if (err) {
>> +			pr_err("%s: error %d requesting status\n",
>> +			       mmc_hostname(card->host), err);
>> +			goto out;
>> +		}
>> +
>> +		/*
>> +		 * Some cards mishandle the status bits, so make sure to check
>> +		 * both the busy indication and the card state.
>> +		 */
>> +		if ((status & R1_READY_FOR_DATA) &&
>> +		    (R1_CURRENT_STATE(status) != R1_STATE_PRG)) {
>> +			pr_debug("%s: %s: completed BKOPs, exit polling\n",
>> +				 mmc_hostname(card->host), __func__);
>> +			mmc_card_clr_doing_bkops(card);
>> +			card->bkops_info.started_delayed_bkops = false;
>> +			goto out;
>> +		}
>> +
>> +		mmc_release_host(card->host);
>> +
>> +		/*
>> +		 * Sleep before checking the card status again to allow the
>> +		 * card to complete the BKOPs operation
>> +		 */
>> +		msleep(BKOPS_COMPLETION_POLLING_INTERVAL_MS);
>> +	} while (time_before(jiffies, timeout_jiffies));
>> +
>> +	pr_err("%s: %s: exit polling due to timeout\n",
>> +	       mmc_hostname(card->host), __func__);
>> +
>> +	return;
>> +out:
>> +	mmc_release_host(card->host);
>> +}
>> +
>> +/**
>> + * mmc_start_idle_time_bkops() - check if a non urgent BKOPS is
>> + * needed
>> + * @work:	The idle time BKOPS work
>> + */
>> +void mmc_start_idle_time_bkops(struct work_struct *work)
>> +{
>> +	struct mmc_card *card = container_of(work, struct mmc_card,
>> +			bkops_info.dw.work);
>> +
>> +	/*
>> +	 * Prevent a race condition between mmc_stop_bkops and the delayed
>> +	 * BKOPS work in case the delayed work is executed on another CPU
>> +	 */
>> +	if (card->bkops_info.cancel_delayed_work)
>> +		return;
>> +
>> +	mmc_start_bkops(card, false);
>> +}
>> +EXPORT_SYMBOL(mmc_start_idle_time_bkops);
>> +
>>  static void mmc_wait_done(struct mmc_request *mrq)
>>  {
>>  	complete(&mrq->completion);
>> @@ -582,6 +732,19 @@ int mmc_stop_bkops(struct mmc_card *card)
>>  	int err = 0;
>>
>>  	BUG_ON(!card);
>> +
>> +	mmc_claim_host(card->host);
>> +
>> +	/*
>> +	 * Notify the delayed work to be cancelled, in case it was already
>> +	 * removed from the queue, but was not started yet
>> +	 */
>> +	card->bkops_info.cancel_delayed_work = true;
>> +	if (delayed_work_pending(&card->bkops_info.dw))
>> +		cancel_delayed_work_sync(&card->bkops_info.dw);
>> +	if (!mmc_card_doing_bkops(card))
>> +		goto out;
>> +
>>  	err = mmc_interrupt_hpi(card);
>>
>>  	/*
>> @@ -593,6 +756,8 @@ int mmc_stop_bkops(struct mmc_card *card)
>>  		err = 0;
>>  	}
>>
>> +out:
>> +	mmc_release_host(card->host);
>>  	return err;
>>  }
>>  EXPORT_SYMBOL(mmc_stop_bkops);
>> @@ -2566,15 +2731,13 @@ int mmc_pm_notify(struct notifier_block
>> *notify_block,
>>  	switch (mode) {
>>  	case PM_HIBERNATION_PREPARE:
>>  	case PM_SUSPEND_PREPARE:
>> -		if (host->card && mmc_card_mmc(host->card) &&
>> -		    mmc_card_doing_bkops(host->card)) {
>> +		if (host->card && mmc_card_mmc(host->card)) {
>>  			err = mmc_stop_bkops(host->card);
>>  			if (err) {
>>  				pr_err("%s: didn't stop bkops\n",
>>  					mmc_hostname(host));
>>  				return err;
>>  			}
>> -			mmc_card_clr_doing_bkops(host->card);
>>  		}
>>
>>  		spin_lock_irqsave(&host->lock, flags);
>> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
>> index 7509de1..f8ff640 100644
>> --- a/drivers/mmc/core/mmc.c
>> +++ b/drivers/mmc/core/mmc.c
>> @@ -1258,6 +1258,30 @@ static int mmc_init_card(struct mmc_host *host,
>> u32 ocr,
>>  		}
>>  	}
>>
>> +	if (!oldcard) {
>> +		if (card->ext_csd.bkops_en) {
>> +			INIT_DELAYED_WORK(&card->bkops_info.dw,
>> +					  mmc_start_idle_time_bkops);
>> +			INIT_WORK(&card->bkops_info.poll_for_completion,
>> +				  mmc_bkops_completion_polling);
>> +
>> +			/*
>> +			 * Calculate the time to start the BKOPs checking.
>> +			 * The idle time of the host controller should be taken
>> +			 * into account in order to prevent a race condition
>> +			 * before starting BKOPs and going into suspend.
>> +			 * If the host controller didn't set its idle time,
>> +			 * a default value is used.
>> +			 */
>> +			card->bkops_info.delay_ms = MMC_IDLE_BKOPS_TIME_MS;
>> +			if (card->bkops_info.host_suspend_tout_ms)
>> +				card->bkops_info.delay_ms = min(
>> +					card->bkops_info.delay_ms,
>> +				      card->bkops_info.host_suspend_tout_ms/2);
>> +
>> +		}
>> +	}
>> +
>>  	if (!oldcard)
>>  		host->card = card;
>>
>> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
>> index 78cc3be..0e4d55a 100644
>> --- a/include/linux/mmc/card.h
>> +++ b/include/linux/mmc/card.h
>> @@ -207,6 +207,48 @@ struct mmc_part {
>>  #define MMC_BLK_DATA_AREA_GP	(1<<2)
>>  };
>>
>> +/**
>> + * struct mmc_bkops_info - BKOPS data
>> + * @dw:	Idle time bkops delayed work
>> + * @host_suspend_tout_ms:	The host controller idle time,
>> + * before getting into suspend
>> + * @delay_ms:	The time to start the BKOPS
>> + *        delayed work once MMC thread is idle
>> + * @poll_for_completion:	Poll on BKOPS completion
>> + * @cancel_delayed_work: A flag to indicate if the delayed work
>> + *        should be cancelled
>> + * @started_delayed_bkops:  A flag to indicate if the delayed
>> + *        work was scheduled
>> + * @sectors_changed:  number of  sectors written or
>> + *       discard since the last idle BKOPS were scheduled
>> + */
>> +struct mmc_bkops_info {
>> +	struct delayed_work	dw;
>> +	unsigned int		host_suspend_tout_ms;
>> +	unsigned int		delay_ms;
>> +/*
>> + * A default time for checking the need for non urgent BKOPS once mmcqd
>> + * is idle.
>> + */
>> +#define MMC_IDLE_BKOPS_TIME_MS 2000
>> +	struct work_struct	poll_for_completion;
>> +/* Polling timeout and interval for waiting on non-blocking BKOPs
>> completion */
>> +#define BKOPS_COMPLETION_POLLING_TIMEOUT_MS 10000 /* in ms */
>> +#define BKOPS_COMPLETION_POLLING_INTERVAL_MS 1000 /* in ms */
>> +	bool			cancel_delayed_work;
>> +	bool			started_delayed_bkops;
>> +	unsigned int		sectors_changed;
>> +/*
>> + * Since canceling the delayed work might have significant effect on
>> the
>> + * performance of small requests we won't queue the delayed work every
>> time
>> + * mmcqd thread is idle.
>> + * The delayed work for idle BKOPS will be scheduled only after a
>> significant
>> + * amount of write or discard data.
>> + * 100MB is chosen based on benchmark tests.
>> + */
>> +#define BKOPS_MIN_SECTORS_TO_QUEUE_DELAYED_WORK 204800 /* 100MB */
>> +};
>> +
>>  /*
>>   * MMC device
>>   */
>> @@ -281,6 +323,8 @@ struct mmc_card {
>>  	struct dentry		*debugfs_root;
>>  	struct mmc_part	part[MMC_NUM_PHY_PARTITION]; /* physical partitions */
>>  	unsigned int    nr_parts;
>> +
>> +	struct mmc_bkops_info	bkops_info;
>>  };
>>
>>  /*
>> diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
>> index 9b9cdaf..665d345 100644
>> --- a/include/linux/mmc/core.h
>> +++ b/include/linux/mmc/core.h
>> @@ -145,6 +145,9 @@ extern int mmc_app_cmd(struct mmc_host *, struct
>> mmc_card *);
>>  extern int mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *,
>>  	struct mmc_command *, int);
>>  extern void mmc_start_bkops(struct mmc_card *card, bool
>> from_exception);
>> +extern void mmc_start_delayed_bkops(struct mmc_card *card);
>> +extern void mmc_start_idle_time_bkops(struct work_struct *work);
>> +extern void mmc_bkops_completion_polling(struct work_struct *work);
>>  extern int __mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int,
>> bool);
>>  extern int mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int);
>>
>>
>
>


-- 
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member
of Code Aurora Forum, hosted by The Linux Foundation


  reply	other threads:[~2012-11-07  4:28 UTC|newest]

Thread overview: 88+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <n@samsung.com>
2012-08-05 13:08 ` [RFC/PATCH] mmc: core: Add support for idle time BKOPs Maya Erez
2012-08-05 13:08   ` Maya Erez
2012-08-30  7:36   ` Jaehoon Chung
2012-09-04  5:42     ` merez
2012-09-04  5:42       ` merez
2012-10-04 22:28 ` [PATCH v2] mmc: core: Add support for idle time BKOPS Maya Erez
2012-10-04 22:28   ` Maya Erez
2012-10-09  3:52   ` merez
2012-10-09  3:52     ` merez
2012-10-16  7:53   ` Jaehoon Chung
2012-11-07  4:28     ` merez [this message]
2012-11-07  4:28       ` merez
2012-11-08  5:26       ` Jaehoon Chung
2012-11-11  9:25   ` Subhash Jadavani
2012-10-18 16:31 ` [PATCH 1/6] mmc: core: sdio_io.c: Fixed lines with > 80 chars Sangho Yi
2012-10-18 16:31   ` [PATCH 2/6] mmc: core: sdio_bus.c: Removed trailing whitespace error Sangho Yi
2012-10-18 16:31   ` [PATCH 3/6] mmc: core: sdio_bus.c: Fixed lines > 80 chars Sangho Yi
2012-10-18 23:54     ` Jaehoon Chung
2012-10-18 16:31   ` [PATCH 4/6] mmc: core: sdio_bus.c: Removed space between func name and () Sangho Yi
2012-10-18 16:31   ` [PATCH 5/6] mmc: core: sdio_bus.c: Fixed a warning for the pr_warning( Sangho Yi
2012-10-18 16:31   ` [PATCH 6/6] mmc: core: sdio_bus.c: Removed unnecessary NULL check routine Sangho Yi
2012-10-18 16:47   ` [PATCH 1/6] mmc: core: sdio_io.c: Fixed lines with > 80 chars Joe Perches
2012-11-01 14:40 ` [PATCH v2] mmc: fix async request mechanism for sequential read scenarios Konstantin Dorfman
2012-11-05  6:20   ` Per Förlin
2012-11-05  7:15     ` Jaehoon Chung
2012-11-12 12:10       ` Konstantin Dorfman
2012-11-08 10:41     ` Jaehoon Chung
2012-11-08 12:51       ` merez
2012-11-09  5:46         ` Jaehoon Chung
2012-11-12 12:49     ` Konstantin Dorfman
2012-11-06  8:40   ` Jaehoon Chung
2012-11-12 12:42     ` Konstantin Dorfman
2012-11-12 16:51 ` [PATCH v3] " Konstantin Dorfman
2012-11-13 13:42   ` Seungwon Jeon
2012-11-15 10:23     ` Seungwon Jeon
2012-11-20  2:05       ` Seungwon Jeon
2012-11-25 11:56 ` [PATCH v3] mmc: core: Add support for idle time BKOPS Maya Erez
2012-11-25 11:56   ` Maya Erez
2012-11-25 12:46   ` merez
2012-11-25 12:46     ` merez
2012-11-28 14:22     ` Chris Ball
2012-11-29 12:40   ` Jaehoon Chung
2012-12-03  9:49     ` merez
2012-12-04  9:52       ` Ulf Hansson
2012-12-04 21:17         ` merez
2012-12-06 10:18           ` Ulf Hansson
2012-12-12 12:32             ` merez
2012-12-13 10:17               ` Ulf Hansson
2012-12-21  8:35                 ` Maya Erez
2012-12-21  8:35                   ` Maya Erez
2012-12-21  9:56                   ` Ulf Hansson
2012-12-21 10:24                     ` Jaehoon Chung
2012-12-05 13:38 ` [PATCH v4] mmc: fix async request mechanism for sequential read scenarios Konstantin Dorfman
2012-12-06  5:24   ` Seungwon Jeon
2012-12-06 14:23     ` Konstantin Dorfman
2012-12-10 14:23 ` [RESEND PATCH " Konstantin Dorfman
2012-12-12  9:26   ` Seungwon Jeon
2012-12-17 12:26   ` Seungwon Jeon
2012-12-18 16:00     ` Konstantin Dorfman
2012-12-18 16:19       ` Chris Ball
2012-12-20  7:39       ` Seungwon Jeon
2012-12-26  9:26 ` [PATCH v5] " Konstantin Dorfman
2012-12-28 10:16   ` Seungwon Jeon
2013-01-14 19:31     ` Chris Ball
2013-01-15 12:00       ` Konstantin Dorfman
2013-01-22 10:48   ` [PATCH 1/2] mmc: core: fix permanent sleep of mmcqd during card removal Seungwon Jeon
2013-01-31 11:05     ` Jaehoon Chung
2013-01-31 11:25       ` Seungwon Jeon
2013-02-08 12:07     ` Konstantin Dorfman
2013-02-11 17:00     ` Chris Ball
2013-01-30  6:30   ` Seungwon Jeon
2013-01-31  6:53     ` Subhash Jadavani
2013-02-04 11:43     ` Subhash Jadavani
2013-02-05  5:57       ` Seungwon Jeon
2013-02-05  7:05         ` Jaehoon Chung
2013-02-05  7:32           ` Subhash Jadavani
2013-02-04 19:27     ` Konstantin Dorfman
2013-01-10 20:15 ` [PATCH v5 0/3] Add support for periodic BKOPS Maya Erez
2013-01-11 11:33   ` Jaehoon Chung
2014-11-10 17:13 ` [PATCH 0/3] mmc: Support FFU for eMMC v5.0 Avi Shchislowski
2014-11-11  3:33   ` Jaehoon Chung
2014-11-10 17:13 ` [PATCH 1/3] " Avi Shchislowski
2014-11-10 17:13   ` Avi Shchislowski
2014-11-11  3:07   ` Jaehoon Chung
2014-11-11  8:44     ` Avi Shchislowski
2014-11-10 17:18 ` [PATCH 2/3] " Avi Shchislowski
2014-11-10 17:18   ` Avi Shchislowski
2014-11-11  3:30   ` Jaehoon Chung

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=24cf15558496fc4dff9035181df0a9bb.squirrel@www.codeaurora.org \
    --to=merez@codeaurora.org \
    --cc=jh80.chung@samsung.com \
    --cc=linux-arm-msm@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mmc@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.