text: /* cyanblkdev_queue.h - Antioch Linux Block Driver queue source file
  ## ===========================
  ## Copyright (C) 2010  Cypress Semiconductor
  ##
  ## This program is free software; you can redistribute it and/or
  ## modify it under the terms of the GNU General Public License
  ## as published by the Free Software Foundation; either version 2
  ## of the License, or (at your option) any later version.
  ##
  ## This program is distributed in the hope that it will be useful,
  ## but WITHOUT ANY WARRANTY; without even the implied warranty of
  ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  ## GNU General Public License for more details.
  ##
  ## You should have received a copy of the GNU General Public License
  ## along with this program; if not, write to the Free Software
  ## Foundation, Inc., 51 Franklin Street, Fifth Floor,
  ## Boston, MA  02110-1301, USA.
  ## ===========================
  */
  
  /*
   * Request queue handling for Antioch block device driver.
   * Based on the mmc queue handling code by Russell King in the
   * linux 2.6.10 kernel.
   */
  
  /*
   *  linux/drivers/mmc/mmc_queue.c
   *
   *  Copyright (C) 2003 Russell King, All Rights Reserved.
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License version 2 as
   * published by the Free Software Foundation.
   *
   */
  
  #include <linux/module.h>
  #include <linux/blkdev.h>
  
  #include "cyasblkdev_queue.h"
  
  #define CYASBLKDEV_QUEUE_EXIT		(1 << 0)
  #define CYASBLKDEV_QUEUE_SUSPENDED	(1 << 1)
  #define CY_AS_USE_ASYNC_API
  
  
  
  /* print flags by name */
  const char *rq_flag_bit_names[] = {
  	"REQ_RW",			/* not set, read. set, write */
  	"REQ_FAILFAST",		/* no low level driver retries */
  	"REQ_SORTED",		/* elevator knows about this request */
  	"REQ_SOFTBARRIER",	/* may not be passed by ioscheduler */
  	"REQ_HARDBARRIER",	/* may not be passed by drive either */
  	"REQ_FUA",			/* forced unit access */
  	"REQ_NOMERGE",		/* don't touch this for merging */
  	"REQ_STARTED",		/* drive already may have started this one */
  	"REQ_DONTPREP",		/* don't call prep for this one */
  	"REQ_QUEUED",		/* uses queueing */
  	"REQ_ELVPRIV",		/* elevator private data attached */
  	"REQ_FAILED",		/* set if the request failed */
  	"REQ_QUIET",		/* don't worry about errors */
  	"REQ_PREEMPT",		/* set for "ide_preempt" requests */
  	"REQ_ORDERED_COLOR",/* is before or after barrier */
  	"REQ_RW_SYNC",		/* request is sync (O_DIRECT) */
  	"REQ_ALLOCED",		/* request came from our alloc pool */
  	"REQ_RW_META",		/* metadata io request */
  	"REQ_COPY_USER",	/* contains copies of user pages */
  	"REQ_NR_BITS",		/* stops here */
  };
  
  void verbose_rq_flags(int flags)
  {
  	int i;
  	uint32_t j;
  	j = 1;
  	for (i = 0; i < 32; i++) {
  		if (flags & j)
  			DBGPRN("<1>%s", rq_flag_bit_names[i]);
  		j = j << 1;
  	}
  }
  
  
  /*
   * Prepare a -BLK_DEV  request.  Essentially, this means passing the
   * preparation off to the media driver.  The media driver will
   * create request to CyAsDev.
   */
  static int cyasblkdev_prep_request(
  	struct request_queue *q, struct request *req)
  {
  	DBGPRN_FUNC_NAME;
  
  	/* we only like normal block requests.*/
  	if (req->cmd_type != REQ_TYPE_FS && !(req->cmd_flags & REQ_DISCARD)) {
  		#ifndef WESTBRIDGE_NDEBUG
  		cy_as_hal_print_message("%s:%x bad request received\n",
  			__func__, current->pid);
  		#endif
  
  		blk_dump_rq_flags(req, "cyasblkdev bad request");
  		return BLKPREP_KILL;
  	}
  
  	req->cmd_flags |= REQ_DONTPREP;
  
  	return BLKPREP_OK;
  }
  
  /* queue worker thread */
  static int cyasblkdev_queue_thread(void *d)
  {
  	DECLARE_WAITQUEUE(wait, current);
  	struct cyasblkdev_queue *bq = d;
  	struct request_queue *q = bq->queue;
  	u32 qth_pid;
  
  	DBGPRN_FUNC_NAME;
  
  	/*
  	 * set iothread to ensure that we aren't put to sleep by
  	 * the process freezing.  we handle suspension ourselves.
  	 */
  	daemonize("cyasblkdev_queue_thread");
  
  	/* signal to queue_init() so it could contnue */
  	complete(&bq->thread_complete);
  
  	down(&bq->thread_sem);
  	add_wait_queue(&bq->thread_wq, &wait);
  
  	qth_pid = current->pid;
  
  	#ifndef WESTBRIDGE_NDEBUG
  	cy_as_hal_print_message(
  		"%s:%x started, bq:%p, q:%p\n", __func__, qth_pid, bq, q);
  	#endif
  
  	do {
  		struct request *req = NULL;
  
  		/* the thread wants to be woken up by signals as well */
  		set_current_state(TASK_INTERRUPTIBLE);
  
  		spin_lock_irq(q->queue_lock);
  
  		#ifndef WESTBRIDGE_NDEBUG
  		cy_as_hal_print_message(
  			"%s: for bq->queue is null\n", __func__);
  		#endif
  
  		if (!bq->req) {
  			/* chk if queue is plugged */
  			if (!blk_queue_plugged(q)) {
  				bq->req = req = blk_fetch_request(q);
  				#ifndef WESTBRIDGE_NDEBUG
  				cy_as_hal_print_message(
  					"%s: blk_fetch_request:%x\n",
  					__func__, (uint32_t)req);
  				#endif
  			} else {
  				#ifndef WESTBRIDGE_NDEBUG
  				cy_as_hal_print_message(
  					"%s: queue plugged, "
  					"skip blk_fetch()\n", __func__);
  				#endif
  			}
  		}
  		spin_unlock_irq(q->queue_lock);
  
  		#ifndef WESTBRIDGE_NDEBUG
  		cy_as_hal_print_message(
  			"%s: checking if request queue is null\n", __func__);
  		#endif
  
  		if (!req) {
  			if (bq->flags & CYASBLKDEV_QUEUE_EXIT) {
  				#ifndef WESTBRIDGE_NDEBUG
  				cy_as_hal_print_message(
  					"%s:got QUEUE_EXIT flag\n", __func__);
  				#endif
  
  				break;
  			}
  
  			#ifndef WESTBRIDGE_NDEBUG
  			cy_as_hal_print_message(
  				"%s: request queue is null, goto sleep, "
  				"thread_sem->count=%d\n",
  				__func__, bq->thread_sem.count);
  			if (spin_is_locked(q->queue_lock)) {
  				cy_as_hal_print_message("%s: queue_lock "
  				"is locked, need to release\n", __func__);
  				spin_unlock(q->queue_lock);
  
  				if (spin_is_locked(q->queue_lock))
  					cy_as_hal_print_message(
  						"%s: unlock did not work\n",
  						__func__);
  			} else {
  				cy_as_hal_print_message(
  					"%s: checked lock, is not locked\n",
  					__func__);
  			}
  			#endif
  
  			up(&bq->thread_sem);
  
  			/* yields to the next rdytorun proc,
  			 * then goes back to sleep*/
  			schedule();
  			down(&bq->thread_sem);
  
  			#ifndef WESTBRIDGE_NDEBUG
  			cy_as_hal_print_message(
  				"%s: wake_up,continue\n",
  				__func__);
  			#endif
  			continue;
  		}
  
  		/* new req received, issue it to the driver  */
  		set_current_state(TASK_RUNNING);
  
  		#ifndef WESTBRIDGE_NDEBUG
  		cy_as_hal_print_message(
  			"%s: issued a RQ:%x\n",
  			__func__, (uint32_t)req);
  		#endif
  
  		bq->issue_fn(bq, req);
  
  		#ifndef WESTBRIDGE_NDEBUG
  		cy_as_hal_print_message(
  			"%s: bq->issue_fn() returned\n",
  			__func__);
  		#endif
  
  
  	} while (1);
  
  	set_current_state(TASK_RUNNING);
  	remove_wait_queue(&bq->thread_wq, &wait);
  	up(&bq->thread_sem);
  
  	complete_and_exit(&bq->thread_complete, 0);
  
  	#ifndef WESTBRIDGE_NDEBUG
  	cy_as_hal_print_message("%s: is finished\n", __func__);
  	#endif
  
  	return 0;
  }
  
  /*
   * Generic request handler. it is called for any queue on a
   * particular host.  When the host is not busy, we look for a request
   * on any queue on this host, and attempt to issue it.  This may
   * not be the queue we were asked to process.
   */
  static void cyasblkdev_request(struct request_queue *q)
  {
  	struct cyasblkdev_queue *bq = q->queuedata;
  	DBGPRN_FUNC_NAME;
  
  	#ifndef WESTBRIDGE_NDEBUG
  	cy_as_hal_print_message(
  		"%s new request on cyasblkdev_queue_t bq:=%x\n",
  		__func__, (uint32_t)bq);
  	#endif
  
  	if (!bq->req) {
  		#ifndef WESTBRIDGE_NDEBUG
  		cy_as_hal_print_message("%s wake_up(&bq->thread_wq)\n",
  			__func__);
  		#endif
  
  		/* wake up cyasblkdev_queue worker thread*/
  		wake_up(&bq->thread_wq);
  	} else {
  		#ifndef WESTBRIDGE_NDEBUG
  		cy_as_hal_print_message("%s: don't wake Q_thr, bq->req:%x\n",
  			__func__, (uint32_t)bq->req);
  		#endif
  	}
  }
  
  /*
   * cyasblkdev_init_queue - initialise a queue structure.
   * @bq: cyasblkdev queue
   * @dev:  CyAsDeviceHandle to attach this queue
   * @lock: queue lock
   *
   * Initialise a cyasblkdev_request queue.
   */
  
  /* MAX NUMBER OF SECTORS PER REQUEST **/
  #define Q_MAX_SECTORS 128
  
  /* MAX NUMBER OF PHYS SEGMENTS (entries in the SG list)*/
  #define Q_MAX_SGS   16
  
  int cyasblkdev_init_queue(struct cyasblkdev_queue *bq, spinlock_t *lock)
  {
  	int ret;
  
  	DBGPRN_FUNC_NAME;
  
  	/* 1st param is a function that wakes up the queue thread */
  	bq->queue = blk_init_queue(cyasblkdev_request, lock);
  	if (!bq->queue)
  		return -ENOMEM;
  
  	blk_queue_prep_rq(bq->queue, cyasblkdev_prep_request);
  
  	blk_queue_bounce_limit(bq->queue, BLK_BOUNCE_ANY);
  	blk_queue_max_hw_sectors(bq->queue, Q_MAX_SECTORS);
  
  	/* As of now, we have the HAL/driver support to
  	 * merge scattered segments and handle them simultaneously.
  	 * so, setting the max_phys_segments to 8. */
  	/*blk_queue_max_phys_segments(bq->queue, Q_MAX_SGS);
  	blk_queue_max_hw_segments(bq->queue, Q_MAX_SGS);*/
  	blk_queue_max_segments(bq->queue, Q_MAX_SGS);
  
  	/* should be < then HAL can handle */
  	blk_queue_max_segment_size(bq->queue, 512*Q_MAX_SECTORS);
  
  	bq->queue->queuedata = bq;
  	bq->req = NULL;
  
  	init_completion(&bq->thread_complete);
  	init_waitqueue_head(&bq->thread_wq);
  	sema_init(&bq->thread_sem, 1);
  
  	ret = kernel_thread(cyasblkdev_queue_thread, bq, CLONE_KERNEL);
  	if (ret >= 0) {
  		/* wait until the thread is spawned */
  		wait_for_completion(&bq->thread_complete);
  
  		/* reinitialize the completion */
  		init_completion(&bq->thread_complete);
  		ret = 0;
  		goto out;
  	}
  
  out:
  	return ret;
  }
  EXPORT_SYMBOL(cyasblkdev_init_queue);
  
  /*called from blk_put()  */
  void cyasblkdev_cleanup_queue(struct cyasblkdev_queue *bq)
  {
  	DBGPRN_FUNC_NAME;
  
  	bq->flags |= CYASBLKDEV_QUEUE_EXIT;
  	wake_up(&bq->thread_wq);
  	wait_for_completion(&bq->thread_complete);
  
  	blk_cleanup_queue(bq->queue);
  }
  EXPORT_SYMBOL(cyasblkdev_cleanup_queue);
  
  
  /**
   * cyasblkdev_queue_suspend - suspend a CyAsBlkDev request queue
   * @bq: CyAsBlkDev queue to suspend
   *
   * Stop the block request queue, and wait for our thread to
   * complete any outstanding requests.  This ensures that we
   * won't suspend while a request is being processed.
   */
  void cyasblkdev_queue_suspend(struct cyasblkdev_queue *bq)
  {
  	struct request_queue *q = bq->queue;
  	unsigned long flags;
  
  	DBGPRN_FUNC_NAME;
  
  	if (!(bq->flags & CYASBLKDEV_QUEUE_SUSPENDED)) {
  		bq->flags |= CYASBLKDEV_QUEUE_SUSPENDED;
  
  		spin_lock_irqsave(q->queue_lock, flags);
  		blk_stop_queue(q);
  		spin_unlock_irqrestore(q->queue_lock, flags);
  
  		down(&bq->thread_sem);
  	}
  }
  EXPORT_SYMBOL(cyasblkdev_queue_suspend);
  
  /*cyasblkdev_queue_resume - resume a previously suspended
   * CyAsBlkDev request queue @bq: CyAsBlkDev queue to resume */
  void cyasblkdev_queue_resume(struct cyasblkdev_queue *bq)
  {
  	struct request_queue *q = bq->queue;
  	unsigned long flags;
  
  	DBGPRN_FUNC_NAME;
  
  	if (bq->flags & CYASBLKDEV_QUEUE_SUSPENDED)  {
  		bq->flags &= ~CYASBLKDEV_QUEUE_SUSPENDED;
  
  		up(&bq->thread_sem);
  
  		spin_lock_irqsave(q->queue_lock, flags);
  		blk_start_queue(q);
  		spin_unlock_irqrestore(q->queue_lock, flags);
  	}
  }
  EXPORT_SYMBOL(cyasblkdev_queue_resume);
  
  /*[]*/
  