linux/drivers/s390/scsi/zfcp_qdio.c
Martin Peschke 94506fd148 [SCSI] zfcp: add measurement data for average qdio queue utilisation
Provide measurement data for the utilisation of the QDIO outbound queue.
The additional value allows to calculate an average queue utilisation
by looking at the deltas per time unit. Needed for capacity planning.
It is up to user space to handle wrap-arounds of the 64 bit value.

The new counter neatly complements the existing counter for queue full
conditions. That is why, both statistics counter have been integrated.

Signed-off-by: Martin Peschke <mpeschke@linux.vnet.ibm.com>
Signed-off-by: Christof Schmitt <christof.schmitt@de.ibm.com>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
2009-03-12 12:58:18 -05:00

513 lines
14 KiB
C

/*
* zfcp device driver
*
* Setup and helper functions to access QDIO.
*
* Copyright IBM Corporation 2002, 2008
*/
#define KMSG_COMPONENT "zfcp"
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
#include "zfcp_ext.h"
/* FIXME(tune): free space should be one max. SBAL chain plus what? */
#define ZFCP_QDIO_PCI_INTERVAL (QDIO_MAX_BUFFERS_PER_Q \
- (FSF_MAX_SBALS_PER_REQ + 4))
#define QBUFF_PER_PAGE (PAGE_SIZE / sizeof(struct qdio_buffer))
static int zfcp_qdio_buffers_enqueue(struct qdio_buffer **sbal)
{
int pos;
for (pos = 0; pos < QDIO_MAX_BUFFERS_PER_Q; pos += QBUFF_PER_PAGE) {
sbal[pos] = (struct qdio_buffer *) get_zeroed_page(GFP_KERNEL);
if (!sbal[pos])
return -ENOMEM;
}
for (pos = 0; pos < QDIO_MAX_BUFFERS_PER_Q; pos++)
if (pos % QBUFF_PER_PAGE)
sbal[pos] = sbal[pos - 1] + 1;
return 0;
}
static struct qdio_buffer_element *
zfcp_qdio_sbale(struct zfcp_qdio_queue *q, int sbal_idx, int sbale_idx)
{
return &q->sbal[sbal_idx]->element[sbale_idx];
}
/**
* zfcp_qdio_free - free memory used by request- and resposne queue
* @adapter: pointer to the zfcp_adapter structure
*/
void zfcp_qdio_free(struct zfcp_adapter *adapter)
{
struct qdio_buffer **sbal_req, **sbal_resp;
int p;
if (adapter->ccw_device)
qdio_free(adapter->ccw_device);
sbal_req = adapter->req_q.sbal;
sbal_resp = adapter->resp_q.sbal;
for (p = 0; p < QDIO_MAX_BUFFERS_PER_Q; p += QBUFF_PER_PAGE) {
free_page((unsigned long) sbal_req[p]);
free_page((unsigned long) sbal_resp[p]);
}
}
static void zfcp_qdio_handler_error(struct zfcp_adapter *adapter, u8 id)
{
dev_warn(&adapter->ccw_device->dev, "A QDIO problem occurred\n");
zfcp_erp_adapter_reopen(adapter,
ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED |
ZFCP_STATUS_COMMON_ERP_FAILED, id, NULL);
}
static void zfcp_qdio_zero_sbals(struct qdio_buffer *sbal[], int first, int cnt)
{
int i, sbal_idx;
for (i = first; i < first + cnt; i++) {
sbal_idx = i % QDIO_MAX_BUFFERS_PER_Q;
memset(sbal[sbal_idx], 0, sizeof(struct qdio_buffer));
}
}
/* this needs to be called prior to updating the queue fill level */
static void zfcp_qdio_account(struct zfcp_adapter *adapter)
{
ktime_t now;
s64 span;
int free, used;
spin_lock(&adapter->qdio_stat_lock);
now = ktime_get();
span = ktime_us_delta(now, adapter->req_q_time);
free = max(0, atomic_read(&adapter->req_q.count));
used = QDIO_MAX_BUFFERS_PER_Q - free;
adapter->req_q_util += used * span;
adapter->req_q_time = now;
spin_unlock(&adapter->qdio_stat_lock);
}
static void zfcp_qdio_int_req(struct ccw_device *cdev, unsigned int qdio_err,
int queue_no, int first, int count,
unsigned long parm)
{
struct zfcp_adapter *adapter = (struct zfcp_adapter *) parm;
struct zfcp_qdio_queue *queue = &adapter->req_q;
if (unlikely(qdio_err)) {
zfcp_hba_dbf_event_qdio(adapter, qdio_err, first, count);
zfcp_qdio_handler_error(adapter, 140);
return;
}
/* cleanup all SBALs being program-owned now */
zfcp_qdio_zero_sbals(queue->sbal, first, count);
zfcp_qdio_account(adapter);
atomic_add(count, &queue->count);
wake_up(&adapter->request_wq);
}
static void zfcp_qdio_reqid_check(struct zfcp_adapter *adapter,
unsigned long req_id, int sbal_idx)
{
struct zfcp_fsf_req *fsf_req;
unsigned long flags;
spin_lock_irqsave(&adapter->req_list_lock, flags);
fsf_req = zfcp_reqlist_find(adapter, req_id);
if (!fsf_req)
/*
* Unknown request means that we have potentially memory
* corruption and must stop the machine immediatly.
*/
panic("error: unknown request id (%lx) on adapter %s.\n",
req_id, dev_name(&adapter->ccw_device->dev));
zfcp_reqlist_remove(adapter, fsf_req);
spin_unlock_irqrestore(&adapter->req_list_lock, flags);
fsf_req->sbal_response = sbal_idx;
fsf_req->qdio_inb_usage = atomic_read(&adapter->resp_q.count);
zfcp_fsf_req_complete(fsf_req);
}
static void zfcp_qdio_resp_put_back(struct zfcp_adapter *adapter, int processed)
{
struct zfcp_qdio_queue *queue = &adapter->resp_q;
struct ccw_device *cdev = adapter->ccw_device;
u8 count, start = queue->first;
unsigned int retval;
count = atomic_read(&queue->count) + processed;
retval = do_QDIO(cdev, QDIO_FLAG_SYNC_INPUT, 0, start, count);
if (unlikely(retval)) {
atomic_set(&queue->count, count);
/* FIXME: Recover this with an adapter reopen? */
} else {
queue->first += count;
queue->first %= QDIO_MAX_BUFFERS_PER_Q;
atomic_set(&queue->count, 0);
}
}
static void zfcp_qdio_int_resp(struct ccw_device *cdev, unsigned int qdio_err,
int queue_no, int first, int count,
unsigned long parm)
{
struct zfcp_adapter *adapter = (struct zfcp_adapter *) parm;
struct zfcp_qdio_queue *queue = &adapter->resp_q;
struct qdio_buffer_element *sbale;
int sbal_idx, sbale_idx, sbal_no;
if (unlikely(qdio_err)) {
zfcp_hba_dbf_event_qdio(adapter, qdio_err, first, count);
zfcp_qdio_handler_error(adapter, 147);
return;
}
/*
* go through all SBALs from input queue currently
* returned by QDIO layer
*/
for (sbal_no = 0; sbal_no < count; sbal_no++) {
sbal_idx = (first + sbal_no) % QDIO_MAX_BUFFERS_PER_Q;
/* go through all SBALEs of SBAL */
for (sbale_idx = 0; sbale_idx < QDIO_MAX_ELEMENTS_PER_BUFFER;
sbale_idx++) {
sbale = zfcp_qdio_sbale(queue, sbal_idx, sbale_idx);
zfcp_qdio_reqid_check(adapter,
(unsigned long) sbale->addr,
sbal_idx);
if (likely(sbale->flags & SBAL_FLAGS_LAST_ENTRY))
break;
};
if (unlikely(!(sbale->flags & SBAL_FLAGS_LAST_ENTRY)))
dev_warn(&adapter->ccw_device->dev,
"A QDIO protocol error occurred, "
"operations continue\n");
}
/*
* put range of SBALs back to response queue
* (including SBALs which have already been free before)
*/
zfcp_qdio_resp_put_back(adapter, count);
}
/**
* zfcp_qdio_sbale_req - return ptr to SBALE of req_q for a struct zfcp_fsf_req
* @fsf_req: pointer to struct fsf_req
* Returns: pointer to qdio_buffer_element (SBALE) structure
*/
struct qdio_buffer_element *zfcp_qdio_sbale_req(struct zfcp_fsf_req *req)
{
return zfcp_qdio_sbale(&req->adapter->req_q, req->sbal_last, 0);
}
/**
* zfcp_qdio_sbale_curr - return curr SBALE on req_q for a struct zfcp_fsf_req
* @fsf_req: pointer to struct fsf_req
* Returns: pointer to qdio_buffer_element (SBALE) structure
*/
struct qdio_buffer_element *zfcp_qdio_sbale_curr(struct zfcp_fsf_req *req)
{
return zfcp_qdio_sbale(&req->adapter->req_q, req->sbal_last,
req->sbale_curr);
}
static void zfcp_qdio_sbal_limit(struct zfcp_fsf_req *fsf_req, int max_sbals)
{
int count = atomic_read(&fsf_req->adapter->req_q.count);
count = min(count, max_sbals);
fsf_req->sbal_limit = (fsf_req->sbal_first + count - 1)
% QDIO_MAX_BUFFERS_PER_Q;
}
static struct qdio_buffer_element *
zfcp_qdio_sbal_chain(struct zfcp_fsf_req *fsf_req, unsigned long sbtype)
{
struct qdio_buffer_element *sbale;
/* set last entry flag in current SBALE of current SBAL */
sbale = zfcp_qdio_sbale_curr(fsf_req);
sbale->flags |= SBAL_FLAGS_LAST_ENTRY;
/* don't exceed last allowed SBAL */
if (fsf_req->sbal_last == fsf_req->sbal_limit)
return NULL;
/* set chaining flag in first SBALE of current SBAL */
sbale = zfcp_qdio_sbale_req(fsf_req);
sbale->flags |= SBAL_FLAGS0_MORE_SBALS;
/* calculate index of next SBAL */
fsf_req->sbal_last++;
fsf_req->sbal_last %= QDIO_MAX_BUFFERS_PER_Q;
/* keep this requests number of SBALs up-to-date */
fsf_req->sbal_number++;
/* start at first SBALE of new SBAL */
fsf_req->sbale_curr = 0;
/* set storage-block type for new SBAL */
sbale = zfcp_qdio_sbale_curr(fsf_req);
sbale->flags |= sbtype;
return sbale;
}
static struct qdio_buffer_element *
zfcp_qdio_sbale_next(struct zfcp_fsf_req *fsf_req, unsigned long sbtype)
{
if (fsf_req->sbale_curr == ZFCP_LAST_SBALE_PER_SBAL)
return zfcp_qdio_sbal_chain(fsf_req, sbtype);
fsf_req->sbale_curr++;
return zfcp_qdio_sbale_curr(fsf_req);
}
static void zfcp_qdio_undo_sbals(struct zfcp_fsf_req *fsf_req)
{
struct qdio_buffer **sbal = fsf_req->adapter->req_q.sbal;
int first = fsf_req->sbal_first;
int last = fsf_req->sbal_last;
int count = (last - first + QDIO_MAX_BUFFERS_PER_Q) %
QDIO_MAX_BUFFERS_PER_Q + 1;
zfcp_qdio_zero_sbals(sbal, first, count);
}
static int zfcp_qdio_fill_sbals(struct zfcp_fsf_req *fsf_req,
unsigned int sbtype, void *start_addr,
unsigned int total_length)
{
struct qdio_buffer_element *sbale;
unsigned long remaining, length;
void *addr;
/* split segment up */
for (addr = start_addr, remaining = total_length; remaining > 0;
addr += length, remaining -= length) {
sbale = zfcp_qdio_sbale_next(fsf_req, sbtype);
if (!sbale) {
atomic_inc(&fsf_req->adapter->qdio_outb_full);
zfcp_qdio_undo_sbals(fsf_req);
return -EINVAL;
}
/* new piece must not exceed next page boundary */
length = min(remaining,
(PAGE_SIZE - ((unsigned long)addr &
(PAGE_SIZE - 1))));
sbale->addr = addr;
sbale->length = length;
}
return 0;
}
/**
* zfcp_qdio_sbals_from_sg - fill SBALs from scatter-gather list
* @fsf_req: request to be processed
* @sbtype: SBALE flags
* @sg: scatter-gather list
* @max_sbals: upper bound for number of SBALs to be used
* Returns: number of bytes, or error (negativ)
*/
int zfcp_qdio_sbals_from_sg(struct zfcp_fsf_req *fsf_req, unsigned long sbtype,
struct scatterlist *sg, int max_sbals)
{
struct qdio_buffer_element *sbale;
int retval, bytes = 0;
/* figure out last allowed SBAL */
zfcp_qdio_sbal_limit(fsf_req, max_sbals);
/* set storage-block type for this request */
sbale = zfcp_qdio_sbale_req(fsf_req);
sbale->flags |= sbtype;
for (; sg; sg = sg_next(sg)) {
retval = zfcp_qdio_fill_sbals(fsf_req, sbtype, sg_virt(sg),
sg->length);
if (retval < 0)
return retval;
bytes += sg->length;
}
/* assume that no other SBALEs are to follow in the same SBAL */
sbale = zfcp_qdio_sbale_curr(fsf_req);
sbale->flags |= SBAL_FLAGS_LAST_ENTRY;
return bytes;
}
/**
* zfcp_qdio_send - set PCI flag in first SBALE and send req to QDIO
* @fsf_req: pointer to struct zfcp_fsf_req
* Returns: 0 on success, error otherwise
*/
int zfcp_qdio_send(struct zfcp_fsf_req *fsf_req)
{
struct zfcp_adapter *adapter = fsf_req->adapter;
struct zfcp_qdio_queue *req_q = &adapter->req_q;
int first = fsf_req->sbal_first;
int count = fsf_req->sbal_number;
int retval, pci, pci_batch;
struct qdio_buffer_element *sbale;
/* acknowledgements for transferred buffers */
pci_batch = adapter->req_q_pci_batch + count;
if (unlikely(pci_batch >= ZFCP_QDIO_PCI_INTERVAL)) {
pci_batch %= ZFCP_QDIO_PCI_INTERVAL;
pci = first + count - (pci_batch + 1);
pci %= QDIO_MAX_BUFFERS_PER_Q;
sbale = zfcp_qdio_sbale(req_q, pci, 0);
sbale->flags |= SBAL_FLAGS0_PCI;
}
zfcp_qdio_account(adapter);
retval = do_QDIO(adapter->ccw_device, QDIO_FLAG_SYNC_OUTPUT, 0, first,
count);
if (unlikely(retval)) {
zfcp_qdio_zero_sbals(req_q->sbal, first, count);
return retval;
}
/* account for transferred buffers */
atomic_sub(count, &req_q->count);
req_q->first += count;
req_q->first %= QDIO_MAX_BUFFERS_PER_Q;
adapter->req_q_pci_batch = pci_batch;
return 0;
}
/**
* zfcp_qdio_allocate - allocate queue memory and initialize QDIO data
* @adapter: pointer to struct zfcp_adapter
* Returns: -ENOMEM on memory allocation error or return value from
* qdio_allocate
*/
int zfcp_qdio_allocate(struct zfcp_adapter *adapter)
{
struct qdio_initialize *init_data;
if (zfcp_qdio_buffers_enqueue(adapter->req_q.sbal) ||
zfcp_qdio_buffers_enqueue(adapter->resp_q.sbal))
return -ENOMEM;
init_data = &adapter->qdio_init_data;
init_data->cdev = adapter->ccw_device;
init_data->q_format = QDIO_ZFCP_QFMT;
memcpy(init_data->adapter_name, dev_name(&adapter->ccw_device->dev), 8);
ASCEBC(init_data->adapter_name, 8);
init_data->qib_param_field_format = 0;
init_data->qib_param_field = NULL;
init_data->input_slib_elements = NULL;
init_data->output_slib_elements = NULL;
init_data->no_input_qs = 1;
init_data->no_output_qs = 1;
init_data->input_handler = zfcp_qdio_int_resp;
init_data->output_handler = zfcp_qdio_int_req;
init_data->int_parm = (unsigned long) adapter;
init_data->flags = QDIO_INBOUND_0COPY_SBALS |
QDIO_OUTBOUND_0COPY_SBALS | QDIO_USE_OUTBOUND_PCIS;
init_data->input_sbal_addr_array =
(void **) (adapter->resp_q.sbal);
init_data->output_sbal_addr_array =
(void **) (adapter->req_q.sbal);
return qdio_allocate(init_data);
}
/**
* zfcp_close_qdio - close qdio queues for an adapter
*/
void zfcp_qdio_close(struct zfcp_adapter *adapter)
{
struct zfcp_qdio_queue *req_q;
int first, count;
if (!(atomic_read(&adapter->status) & ZFCP_STATUS_ADAPTER_QDIOUP))
return;
/* clear QDIOUP flag, thus do_QDIO is not called during qdio_shutdown */
req_q = &adapter->req_q;
spin_lock_bh(&adapter->req_q_lock);
atomic_clear_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status);
spin_unlock_bh(&adapter->req_q_lock);
qdio_shutdown(adapter->ccw_device, QDIO_FLAG_CLEANUP_USING_CLEAR);
/* cleanup used outbound sbals */
count = atomic_read(&req_q->count);
if (count < QDIO_MAX_BUFFERS_PER_Q) {
first = (req_q->first + count) % QDIO_MAX_BUFFERS_PER_Q;
count = QDIO_MAX_BUFFERS_PER_Q - count;
zfcp_qdio_zero_sbals(req_q->sbal, first, count);
}
req_q->first = 0;
atomic_set(&req_q->count, 0);
adapter->req_q_pci_batch = 0;
adapter->resp_q.first = 0;
atomic_set(&adapter->resp_q.count, 0);
}
/**
* zfcp_qdio_open - prepare and initialize response queue
* @adapter: pointer to struct zfcp_adapter
* Returns: 0 on success, otherwise -EIO
*/
int zfcp_qdio_open(struct zfcp_adapter *adapter)
{
struct qdio_buffer_element *sbale;
int cc;
if (atomic_read(&adapter->status) & ZFCP_STATUS_ADAPTER_QDIOUP)
return -EIO;
if (qdio_establish(&adapter->qdio_init_data))
goto failed_establish;
if (qdio_activate(adapter->ccw_device))
goto failed_qdio;
for (cc = 0; cc < QDIO_MAX_BUFFERS_PER_Q; cc++) {
sbale = &(adapter->resp_q.sbal[cc]->element[0]);
sbale->length = 0;
sbale->flags = SBAL_FLAGS_LAST_ENTRY;
sbale->addr = NULL;
}
if (do_QDIO(adapter->ccw_device, QDIO_FLAG_SYNC_INPUT, 0, 0,
QDIO_MAX_BUFFERS_PER_Q))
goto failed_qdio;
/* set index of first avalable SBALS / number of available SBALS */
adapter->req_q.first = 0;
atomic_set(&adapter->req_q.count, QDIO_MAX_BUFFERS_PER_Q);
adapter->req_q_pci_batch = 0;
return 0;
failed_qdio:
qdio_shutdown(adapter->ccw_device, QDIO_FLAG_CLEANUP_USING_CLEAR);
failed_establish:
dev_err(&adapter->ccw_device->dev,
"Setting up the QDIO connection to the FCP adapter failed\n");
return -EIO;
}