diff options
Diffstat (limited to 'drivers/s390/scsi/zfcp_qdio.c')
| -rw-r--r-- | drivers/s390/scsi/zfcp_qdio.c | 100 | 
1 files changed, 71 insertions, 29 deletions
diff --git a/drivers/s390/scsi/zfcp_qdio.c b/drivers/s390/scsi/zfcp_qdio.c index a0554beb417..06025cdaa4a 100644 --- a/drivers/s390/scsi/zfcp_qdio.c +++ b/drivers/s390/scsi/zfcp_qdio.c @@ -3,18 +3,23 @@   *   * Setup and helper functions to access QDIO.   * - * Copyright IBM Corporation 2002, 2010 + * Copyright IBM Corp. 2002, 2010   */  #define KMSG_COMPONENT "zfcp"  #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt  #include <linux/slab.h> +#include <linux/module.h>  #include "zfcp_ext.h"  #include "zfcp_qdio.h"  #define QBUFF_PER_PAGE		(PAGE_SIZE / sizeof(struct qdio_buffer)) +static bool enable_multibuffer = 1; +module_param_named(datarouter, enable_multibuffer, bool, 0400); +MODULE_PARM_DESC(datarouter, "Enable hardware data router support (default on)"); +  static int zfcp_qdio_buffers_enqueue(struct qdio_buffer **sbal)  {  	int pos; @@ -37,11 +42,14 @@ static void zfcp_qdio_handler_error(struct zfcp_qdio *qdio, char *id,  	dev_warn(&adapter->ccw_device->dev, "A QDIO problem occurred\n"); -	if (qdio_err & QDIO_ERROR_SLSB_STATE) +	if (qdio_err & QDIO_ERROR_SLSB_STATE) {  		zfcp_qdio_siosl(adapter); +		zfcp_erp_adapter_shutdown(adapter, 0, id); +		return; +	}  	zfcp_erp_adapter_reopen(adapter,  				ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED | -				ZFCP_STATUS_COMMON_ERP_FAILED, id, NULL); +				ZFCP_STATUS_COMMON_ERP_FAILED, id);  }  static void zfcp_qdio_zero_sbals(struct qdio_buffer *sbal[], int first, int cnt) @@ -60,7 +68,7 @@ static inline void zfcp_qdio_account(struct zfcp_qdio *qdio)  	unsigned long long now, span;  	int used; -	now = get_clock_monotonic(); +	now = get_tod_clock_monotonic();  	span = (now - qdio->req_q_time) >> 12;  	used = QDIO_MAX_BUFFERS_PER_Q - atomic_read(&qdio->req_q_free);  	qdio->req_q_util += used * span; @@ -74,7 +82,6 @@ static void zfcp_qdio_int_req(struct ccw_device *cdev, unsigned int qdio_err,  	struct zfcp_qdio *qdio = (struct zfcp_qdio *) parm;  	if (unlikely(qdio_err)) { -		zfcp_dbf_hba_qdio(qdio->adapter->dbf, qdio_err, idx, count);  		zfcp_qdio_handler_error(qdio, "qdireq1", qdio_err);  		return;  	} @@ -94,10 +101,31 @@ static void zfcp_qdio_int_resp(struct ccw_device *cdev, unsigned int qdio_err,  			       unsigned long parm)  {  	struct zfcp_qdio *qdio = (struct zfcp_qdio *) parm; -	int sbal_idx, sbal_no; +	struct zfcp_adapter *adapter = qdio->adapter; +	int sbal_no, sbal_idx;  	if (unlikely(qdio_err)) { -		zfcp_dbf_hba_qdio(qdio->adapter->dbf, qdio_err, idx, count); +		if (zfcp_adapter_multi_buffer_active(adapter)) { +			void *pl[ZFCP_QDIO_MAX_SBALS_PER_REQ + 1]; +			struct qdio_buffer_element *sbale; +			u64 req_id; +			u8 scount; + +			memset(pl, 0, +			       ZFCP_QDIO_MAX_SBALS_PER_REQ * sizeof(void *)); +			sbale = qdio->res_q[idx]->element; +			req_id = (u64) sbale->addr; +			scount = min(sbale->scount + 1, +				     ZFCP_QDIO_MAX_SBALS_PER_REQ + 1); +				     /* incl. signaling SBAL */ + +			for (sbal_no = 0; sbal_no < scount; sbal_no++) { +				sbal_idx = (idx + sbal_no) % +					QDIO_MAX_BUFFERS_PER_Q; +				pl[sbal_no] = qdio->res_q[sbal_idx]; +			} +			zfcp_dbf_hba_def_err(adapter, req_id, scount, pl); +		}  		zfcp_qdio_handler_error(qdio, "qdires1", qdio_err);  		return;  	} @@ -116,7 +144,7 @@ static void zfcp_qdio_int_resp(struct ccw_device *cdev, unsigned int qdio_err,  	 * put SBALs back to response queue  	 */  	if (do_QDIO(cdev, QDIO_FLAG_SYNC_INPUT, 0, idx, count)) -		zfcp_erp_adapter_reopen(qdio->adapter, 0, "qdires2", NULL); +		zfcp_erp_adapter_reopen(qdio->adapter, 0, "qdires2");  }  static struct qdio_buffer_element * @@ -126,7 +154,7 @@ zfcp_qdio_sbal_chain(struct zfcp_qdio *qdio, struct zfcp_qdio_req *q_req)  	/* set last entry flag in current SBALE of current SBAL */  	sbale = zfcp_qdio_sbale_curr(qdio, q_req); -	sbale->flags |= SBAL_FLAGS_LAST_ENTRY; +	sbale->eflags |= SBAL_EFLAGS_LAST_ENTRY;  	/* don't exceed last allowed SBAL */  	if (q_req->sbal_last == q_req->sbal_limit) @@ -134,7 +162,7 @@ zfcp_qdio_sbal_chain(struct zfcp_qdio *qdio, struct zfcp_qdio_req *q_req)  	/* set chaining flag in first SBALE of current SBAL */  	sbale = zfcp_qdio_sbale_req(qdio, q_req); -	sbale->flags |= SBAL_FLAGS0_MORE_SBALS; +	sbale->sflags |= SBAL_SFLAGS0_MORE_SBALS;  	/* calculate index of next SBAL */  	q_req->sbal_last++; @@ -149,7 +177,7 @@ zfcp_qdio_sbal_chain(struct zfcp_qdio *qdio, struct zfcp_qdio_req *q_req)  	/* set storage-block type for new SBAL */  	sbale = zfcp_qdio_sbale_curr(qdio, q_req); -	sbale->flags |= q_req->sbtype; +	sbale->sflags |= q_req->sbtype;  	return sbale;  } @@ -157,7 +185,7 @@ zfcp_qdio_sbal_chain(struct zfcp_qdio *qdio, struct zfcp_qdio_req *q_req)  static struct qdio_buffer_element *  zfcp_qdio_sbale_next(struct zfcp_qdio *qdio, struct zfcp_qdio_req *q_req)  { -	if (q_req->sbale_curr == ZFCP_QDIO_LAST_SBALE_PER_SBAL) +	if (q_req->sbale_curr == qdio->max_sbale_per_sbal - 1)  		return zfcp_qdio_sbal_chain(qdio, q_req);  	q_req->sbale_curr++;  	return zfcp_qdio_sbale_curr(qdio, q_req); @@ -169,17 +197,16 @@ zfcp_qdio_sbale_next(struct zfcp_qdio *qdio, struct zfcp_qdio_req *q_req)   * @q_req: pointer to struct zfcp_qdio_req   * @sg: scatter-gather list   * @max_sbals: upper bound for number of SBALs to be used - * Returns: number of bytes, or error (negativ) + * Returns: zero or -EINVAL on error   */  int zfcp_qdio_sbals_from_sg(struct zfcp_qdio *qdio, struct zfcp_qdio_req *q_req,  			    struct scatterlist *sg)  {  	struct qdio_buffer_element *sbale; -	int bytes = 0;  	/* set storage-block type for this request */  	sbale = zfcp_qdio_sbale_req(qdio, q_req); -	sbale->flags |= q_req->sbtype; +	sbale->sflags |= q_req->sbtype;  	for (; sg; sg = sg_next(sg)) {  		sbale = zfcp_qdio_sbale_next(qdio, q_req); @@ -189,23 +216,17 @@ int zfcp_qdio_sbals_from_sg(struct zfcp_qdio *qdio, struct zfcp_qdio_req *q_req,  					     q_req->sbal_number);  			return -EINVAL;  		} -  		sbale->addr = sg_virt(sg);  		sbale->length = sg->length; - -		bytes += sg->length;  	} - -	return bytes; +	return 0;  }  static int zfcp_qdio_sbal_check(struct zfcp_qdio *qdio)  { -	spin_lock_irq(&qdio->req_q_lock);  	if (atomic_read(&qdio->req_q_free) ||  	    !(atomic_read(&qdio->adapter->status) & ZFCP_STATUS_ADAPTER_QDIOUP))  		return 1; -	spin_unlock_irq(&qdio->req_q_lock);  	return 0;  } @@ -223,9 +244,8 @@ int zfcp_qdio_sbal_get(struct zfcp_qdio *qdio)  {  	long ret; -	spin_unlock_irq(&qdio->req_q_lock); -	ret = wait_event_interruptible_timeout(qdio->req_q_wq, -			       zfcp_qdio_sbal_check(qdio), 5 * HZ); +	ret = wait_event_interruptible_lock_irq_timeout(qdio->req_q_wq, +		       zfcp_qdio_sbal_check(qdio), qdio->req_q_lock, 5 * HZ);  	if (!(atomic_read(&qdio->adapter->status) & ZFCP_STATUS_ADAPTER_QDIOUP))  		return -EIO; @@ -236,10 +256,9 @@ int zfcp_qdio_sbal_get(struct zfcp_qdio *qdio)  	if (!ret) {  		atomic_inc(&qdio->req_q_full);  		/* assume hanging outbound queue, try queue recovery */ -		zfcp_erp_adapter_reopen(qdio->adapter, 0, "qdsbg_1", NULL); +		zfcp_erp_adapter_reopen(qdio->adapter, 0, "qdsbg_1");  	} -	spin_lock_irq(&qdio->req_q_lock);  	return -EIO;  } @@ -285,6 +304,8 @@ static void zfcp_qdio_setup_init_data(struct qdio_initialize *id,  	memcpy(id->adapter_name, dev_name(&id->cdev->dev), 8);  	ASCEBC(id->adapter_name, 8);  	id->qib_rflags = QIB_RFLAGS_ENABLE_DATA_DIV; +	if (enable_multibuffer) +		id->qdr_ac |= QDR_AC_MULTI_BUFFER_ENABLE;  	id->no_input_qs = 1;  	id->no_output_qs = 1;  	id->input_handler = zfcp_qdio_int_resp; @@ -292,6 +313,8 @@ static void zfcp_qdio_setup_init_data(struct qdio_initialize *id,  	id->int_parm = (unsigned long) qdio;  	id->input_sbal_addr_array = (void **) (qdio->res_q);  	id->output_sbal_addr_array = (void **) (qdio->req_q); +	id->scan_threshold = +		QDIO_MAX_BUFFERS_PER_Q - ZFCP_QDIO_MAX_SBALS_PER_REQ * 2;  }  /** @@ -309,6 +332,7 @@ static int zfcp_qdio_allocate(struct zfcp_qdio *qdio)  		return -ENOMEM;  	zfcp_qdio_setup_init_data(&init_data, qdio); +	init_waitqueue_head(&qdio->req_q_wq);  	return qdio_allocate(&init_data);  } @@ -377,22 +401,40 @@ int zfcp_qdio_open(struct zfcp_qdio *qdio)  		atomic_set_mask(ZFCP_STATUS_ADAPTER_DATA_DIV_ENABLED,  				&qdio->adapter->status); +	if (ssqd.qdioac2 & CHSC_AC2_MULTI_BUFFER_ENABLED) { +		atomic_set_mask(ZFCP_STATUS_ADAPTER_MB_ACT, &adapter->status); +		qdio->max_sbale_per_sbal = QDIO_MAX_ELEMENTS_PER_BUFFER; +	} else { +		atomic_clear_mask(ZFCP_STATUS_ADAPTER_MB_ACT, &adapter->status); +		qdio->max_sbale_per_sbal = QDIO_MAX_ELEMENTS_PER_BUFFER - 1; +	} + +	qdio->max_sbale_per_req = +		ZFCP_QDIO_MAX_SBALS_PER_REQ * qdio->max_sbale_per_sbal +		- 2;  	if (qdio_activate(cdev))  		goto failed_qdio;  	for (cc = 0; cc < QDIO_MAX_BUFFERS_PER_Q; cc++) {  		sbale = &(qdio->res_q[cc]->element[0]);  		sbale->length = 0; -		sbale->flags = SBAL_FLAGS_LAST_ENTRY; +		sbale->eflags = SBAL_EFLAGS_LAST_ENTRY; +		sbale->sflags = 0;  		sbale->addr = NULL;  	}  	if (do_QDIO(cdev, QDIO_FLAG_SYNC_INPUT, 0, 0, QDIO_MAX_BUFFERS_PER_Q))  		goto failed_qdio; -	/* set index of first avalable SBALS / number of available SBALS */ +	/* set index of first available SBALS / number of available SBALS */  	qdio->req_q_idx = 0;  	atomic_set(&qdio->req_q_free, QDIO_MAX_BUFFERS_PER_Q); +	atomic_set_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &qdio->adapter->status); + +	if (adapter->scsi_host) { +		adapter->scsi_host->sg_tablesize = qdio->max_sbale_per_req; +		adapter->scsi_host->max_sectors = qdio->max_sbale_per_req * 8; +	}  	return 0;  | 
