2005-04-17 05:20:36 +07:00
|
|
|
/*
|
2008-06-10 23:20:57 +07:00
|
|
|
* zfcp device driver
|
2005-04-17 05:20:36 +07:00
|
|
|
*
|
2008-06-10 23:20:57 +07:00
|
|
|
* Setup and helper functions to access QDIO.
|
2005-04-17 05:20:36 +07:00
|
|
|
*
|
2010-04-30 23:09:34 +07:00
|
|
|
* Copyright IBM Corporation 2002, 2010
|
2005-04-17 05:20:36 +07:00
|
|
|
*/
|
|
|
|
|
2008-12-25 19:39:53 +07:00
|
|
|
#define KMSG_COMPONENT "zfcp"
|
|
|
|
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
|
|
|
|
|
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files. percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.
percpu.h -> slab.h dependency is about to be removed. Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability. As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.
http://userweb.kernel.org/~tj/misc/slabh-sweep.py
The script does the followings.
* Scan files for gfp and slab usages and update includes such that
only the necessary includes are there. ie. if only gfp is used,
gfp.h, if slab is used, slab.h.
* When the script inserts a new include, it looks at the include
blocks and try to put the new include such that its order conforms
to its surrounding. It's put in the include block which contains
core kernel includes, in the same order that the rest are ordered -
alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
doesn't seem to be any matching order.
* If the script can't find a place to put a new include (mostly
because the file doesn't have fitting include block), it prints out
an error message indicating which .h file needs to be added to the
file.
The conversion was done in the following steps.
1. The initial automatic conversion of all .c files updated slightly
over 4000 files, deleting around 700 includes and adding ~480 gfp.h
and ~3000 slab.h inclusions. The script emitted errors for ~400
files.
2. Each error was manually checked. Some didn't need the inclusion,
some needed manual addition while adding it to implementation .h or
embedding .c file was more appropriate for others. This step added
inclusions to around 150 files.
3. The script was run again and the output was compared to the edits
from #2 to make sure no file was left behind.
4. Several build tests were done and a couple of problems were fixed.
e.g. lib/decompress_*.c used malloc/free() wrappers around slab
APIs requiring slab.h to be added manually.
5. The script was run on all .h files but without automatically
editing them as sprinkling gfp.h and slab.h inclusions around .h
files could easily lead to inclusion dependency hell. Most gfp.h
inclusion directives were ignored as stuff from gfp.h was usually
wildly available and often used in preprocessor macros. Each
slab.h inclusion directive was examined and added manually as
necessary.
6. percpu.h was updated not to include slab.h.
7. Build test were done on the following configurations and failures
were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my
distributed build env didn't work with gcov compiles) and a few
more options had to be turned off depending on archs to make things
build (like ipr on powerpc/64 which failed due to missing writeq).
* x86 and x86_64 UP and SMP allmodconfig and a custom test config.
* powerpc and powerpc64 SMP allmodconfig
* sparc and sparc64 SMP allmodconfig
* ia64 SMP allmodconfig
* s390 SMP allmodconfig
* alpha SMP allmodconfig
* um on x86_64 SMP allmodconfig
8. percpu.h modifications were reverted so that it could be applied as
a separate patch and serve as bisection point.
Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 15:04:11 +07:00
|
|
|
#include <linux/slab.h>
|
2005-04-17 05:20:36 +07:00
|
|
|
#include "zfcp_ext.h"
|
2010-02-17 17:18:59 +07:00
|
|
|
#include "zfcp_qdio.h"
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2008-07-02 15:56:34 +07:00
|
|
|
#define QBUFF_PER_PAGE (PAGE_SIZE / sizeof(struct qdio_buffer))
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2008-06-10 23:20:57 +07:00
|
|
|
static int zfcp_qdio_buffers_enqueue(struct qdio_buffer **sbal)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2007-07-18 15:55:13 +07:00
|
|
|
int pos;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2007-07-18 15:55:13 +07:00
|
|
|
for (pos = 0; pos < QDIO_MAX_BUFFERS_PER_Q; pos += QBUFF_PER_PAGE) {
|
2008-06-10 23:20:57 +07:00
|
|
|
sbal[pos] = (struct qdio_buffer *) get_zeroed_page(GFP_KERNEL);
|
|
|
|
if (!sbal[pos])
|
2007-07-18 15:55:13 +07:00
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
for (pos = 0; pos < QDIO_MAX_BUFFERS_PER_Q; pos++)
|
|
|
|
if (pos % QBUFF_PER_PAGE)
|
2008-06-10 23:20:57 +07:00
|
|
|
sbal[pos] = sbal[pos - 1] + 1;
|
2007-07-18 15:55:13 +07:00
|
|
|
return 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2009-08-18 20:43:19 +07:00
|
|
|
static void zfcp_qdio_handler_error(struct zfcp_qdio *qdio, char *id)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2009-08-18 20:43:19 +07:00
|
|
|
struct zfcp_adapter *adapter = qdio->adapter;
|
|
|
|
|
2008-10-01 17:42:15 +07:00
|
|
|
dev_warn(&adapter->ccw_device->dev, "A QDIO problem occurred\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2008-06-10 23:20:57 +07:00
|
|
|
zfcp_erp_adapter_reopen(adapter,
|
|
|
|
ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED |
|
|
|
|
ZFCP_STATUS_COMMON_ERP_FAILED, id, NULL);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2008-07-02 15:56:34 +07:00
|
|
|
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));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-02 19:08:56 +07:00
|
|
|
/* this needs to be called prior to updating the queue fill level */
|
2009-08-18 20:43:32 +07:00
|
|
|
static inline void zfcp_qdio_account(struct zfcp_qdio *qdio)
|
2009-03-02 19:08:56 +07:00
|
|
|
{
|
2009-08-18 20:43:32 +07:00
|
|
|
unsigned long long now, span;
|
2009-03-02 19:08:56 +07:00
|
|
|
int free, used;
|
|
|
|
|
2009-08-18 20:43:19 +07:00
|
|
|
spin_lock(&qdio->stat_lock);
|
2009-08-18 20:43:32 +07:00
|
|
|
now = get_clock_monotonic();
|
|
|
|
span = (now - qdio->req_q_time) >> 12;
|
|
|
|
free = atomic_read(&qdio->req_q.count);
|
2009-03-02 19:08:56 +07:00
|
|
|
used = QDIO_MAX_BUFFERS_PER_Q - free;
|
2009-08-18 20:43:19 +07:00
|
|
|
qdio->req_q_util += used * span;
|
|
|
|
qdio->req_q_time = now;
|
|
|
|
spin_unlock(&qdio->stat_lock);
|
2009-03-02 19:08:56 +07:00
|
|
|
}
|
|
|
|
|
2008-07-17 22:16:48 +07:00
|
|
|
static void zfcp_qdio_int_req(struct ccw_device *cdev, unsigned int qdio_err,
|
|
|
|
int queue_no, int first, int count,
|
2008-06-10 23:20:57 +07:00
|
|
|
unsigned long parm)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2009-08-18 20:43:19 +07:00
|
|
|
struct zfcp_qdio *qdio = (struct zfcp_qdio *) parm;
|
|
|
|
struct zfcp_qdio_queue *queue = &qdio->req_q;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2008-07-17 22:16:48 +07:00
|
|
|
if (unlikely(qdio_err)) {
|
2009-08-18 20:43:21 +07:00
|
|
|
zfcp_dbf_hba_qdio(qdio->adapter->dbf, qdio_err, first,
|
|
|
|
count);
|
2009-08-18 20:43:19 +07:00
|
|
|
zfcp_qdio_handler_error(qdio, "qdireq1");
|
2008-06-10 23:20:57 +07:00
|
|
|
return;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* cleanup all SBALs being program-owned now */
|
2008-06-10 23:20:57 +07:00
|
|
|
zfcp_qdio_zero_sbals(queue->sbal, first, count);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2009-08-18 20:43:19 +07:00
|
|
|
zfcp_qdio_account(qdio);
|
2008-06-10 23:20:57 +07:00
|
|
|
atomic_add(count, &queue->count);
|
2009-08-18 20:43:19 +07:00
|
|
|
wake_up(&qdio->req_q_wq);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2009-08-18 20:43:19 +07:00
|
|
|
static void zfcp_qdio_resp_put_back(struct zfcp_qdio *qdio, int processed)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2009-08-18 20:43:19 +07:00
|
|
|
struct zfcp_qdio_queue *queue = &qdio->resp_q;
|
|
|
|
struct ccw_device *cdev = qdio->adapter->ccw_device;
|
2008-06-10 23:20:57 +07:00
|
|
|
u8 count, start = queue->first;
|
|
|
|
unsigned int retval;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2008-06-10 23:20:57 +07:00
|
|
|
count = atomic_read(&queue->count) + processed;
|
|
|
|
|
2008-07-17 22:16:48 +07:00
|
|
|
retval = do_QDIO(cdev, QDIO_FLAG_SYNC_INPUT, 0, start, count);
|
2008-06-10 23:20:57 +07:00
|
|
|
|
|
|
|
if (unlikely(retval)) {
|
|
|
|
atomic_set(&queue->count, count);
|
2010-02-17 17:18:51 +07:00
|
|
|
zfcp_erp_adapter_reopen(qdio->adapter, 0, "qdrpb_1", NULL);
|
2008-06-10 23:20:57 +07:00
|
|
|
} else {
|
|
|
|
queue->first += count;
|
|
|
|
queue->first %= QDIO_MAX_BUFFERS_PER_Q;
|
|
|
|
atomic_set(&queue->count, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-07-17 22:16:48 +07:00
|
|
|
static void zfcp_qdio_int_resp(struct ccw_device *cdev, unsigned int qdio_err,
|
|
|
|
int queue_no, int first, int count,
|
2008-06-10 23:20:57 +07:00
|
|
|
unsigned long parm)
|
|
|
|
{
|
2009-08-18 20:43:19 +07:00
|
|
|
struct zfcp_qdio *qdio = (struct zfcp_qdio *) parm;
|
2009-08-18 20:43:13 +07:00
|
|
|
int sbal_idx, sbal_no;
|
2008-06-10 23:20:57 +07:00
|
|
|
|
2008-07-17 22:16:48 +07:00
|
|
|
if (unlikely(qdio_err)) {
|
2009-08-18 20:43:21 +07:00
|
|
|
zfcp_dbf_hba_qdio(qdio->adapter->dbf, qdio_err, first,
|
|
|
|
count);
|
2009-08-18 20:43:19 +07:00
|
|
|
zfcp_qdio_handler_error(qdio, "qdires1");
|
2008-06-10 23:20:57 +07:00
|
|
|
return;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* go through all SBALs from input queue currently
|
|
|
|
* returned by QDIO layer
|
|
|
|
*/
|
2008-06-10 23:20:57 +07:00
|
|
|
for (sbal_no = 0; sbal_no < count; sbal_no++) {
|
|
|
|
sbal_idx = (first + sbal_no) % QDIO_MAX_BUFFERS_PER_Q;
|
2005-04-17 05:20:36 +07:00
|
|
|
/* go through all SBALEs of SBAL */
|
2009-08-18 20:43:19 +07:00
|
|
|
zfcp_fsf_reqid_check(qdio, sbal_idx);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* put range of SBALs back to response queue
|
|
|
|
* (including SBALs which have already been free before)
|
|
|
|
*/
|
2009-08-18 20:43:19 +07:00
|
|
|
zfcp_qdio_resp_put_back(qdio, count);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2009-08-18 20:43:19 +07:00
|
|
|
static void zfcp_qdio_sbal_limit(struct zfcp_qdio *qdio,
|
2010-02-17 17:18:59 +07:00
|
|
|
struct zfcp_qdio_req *q_req, int max_sbals)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2009-08-18 20:43:19 +07:00
|
|
|
int count = atomic_read(&qdio->req_q.count);
|
2005-04-17 05:20:36 +07:00
|
|
|
count = min(count, max_sbals);
|
2009-08-18 20:43:18 +07:00
|
|
|
q_req->sbal_limit = (q_req->sbal_first + count - 1)
|
2008-06-10 23:20:57 +07:00
|
|
|
% QDIO_MAX_BUFFERS_PER_Q;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2008-10-01 17:42:16 +07:00
|
|
|
static struct qdio_buffer_element *
|
2010-04-30 23:09:34 +07:00
|
|
|
zfcp_qdio_sbal_chain(struct zfcp_qdio *qdio, struct zfcp_qdio_req *q_req)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2008-10-01 17:42:16 +07:00
|
|
|
struct qdio_buffer_element *sbale;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* set last entry flag in current SBALE of current SBAL */
|
2009-08-18 20:43:19 +07:00
|
|
|
sbale = zfcp_qdio_sbale_curr(qdio, q_req);
|
2005-04-17 05:20:36 +07:00
|
|
|
sbale->flags |= SBAL_FLAGS_LAST_ENTRY;
|
|
|
|
|
|
|
|
/* don't exceed last allowed SBAL */
|
2009-08-18 20:43:18 +07:00
|
|
|
if (q_req->sbal_last == q_req->sbal_limit)
|
2005-04-17 05:20:36 +07:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* set chaining flag in first SBALE of current SBAL */
|
2009-08-18 20:43:19 +07:00
|
|
|
sbale = zfcp_qdio_sbale_req(qdio, q_req);
|
2005-04-17 05:20:36 +07:00
|
|
|
sbale->flags |= SBAL_FLAGS0_MORE_SBALS;
|
|
|
|
|
|
|
|
/* calculate index of next SBAL */
|
2009-08-18 20:43:18 +07:00
|
|
|
q_req->sbal_last++;
|
|
|
|
q_req->sbal_last %= QDIO_MAX_BUFFERS_PER_Q;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* keep this requests number of SBALs up-to-date */
|
2009-08-18 20:43:18 +07:00
|
|
|
q_req->sbal_number++;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* start at first SBALE of new SBAL */
|
2009-08-18 20:43:18 +07:00
|
|
|
q_req->sbale_curr = 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* set storage-block type for new SBAL */
|
2009-08-18 20:43:19 +07:00
|
|
|
sbale = zfcp_qdio_sbale_curr(qdio, q_req);
|
2010-04-30 23:09:34 +07:00
|
|
|
sbale->flags |= q_req->sbtype;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
return sbale;
|
|
|
|
}
|
|
|
|
|
2008-10-01 17:42:16 +07:00
|
|
|
static struct qdio_buffer_element *
|
2010-04-30 23:09:34 +07:00
|
|
|
zfcp_qdio_sbale_next(struct zfcp_qdio *qdio, struct zfcp_qdio_req *q_req)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2010-04-30 23:09:34 +07:00
|
|
|
if (q_req->sbale_curr == ZFCP_QDIO_LAST_SBALE_PER_SBAL)
|
|
|
|
return zfcp_qdio_sbal_chain(qdio, q_req);
|
2009-08-18 20:43:18 +07:00
|
|
|
q_req->sbale_curr++;
|
2009-08-18 20:43:19 +07:00
|
|
|
return zfcp_qdio_sbale_curr(qdio, q_req);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2009-08-18 20:43:19 +07:00
|
|
|
static void zfcp_qdio_undo_sbals(struct zfcp_qdio *qdio,
|
2010-02-17 17:18:59 +07:00
|
|
|
struct zfcp_qdio_req *q_req)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2009-08-18 20:43:19 +07:00
|
|
|
struct qdio_buffer **sbal = qdio->req_q.sbal;
|
2009-08-18 20:43:18 +07:00
|
|
|
int first = q_req->sbal_first;
|
|
|
|
int last = q_req->sbal_last;
|
2008-06-10 23:20:57 +07:00
|
|
|
int count = (last - first + QDIO_MAX_BUFFERS_PER_Q) %
|
|
|
|
QDIO_MAX_BUFFERS_PER_Q + 1;
|
|
|
|
zfcp_qdio_zero_sbals(sbal, first, count);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* zfcp_qdio_sbals_from_sg - fill SBALs from scatter-gather list
|
2010-04-30 23:09:34 +07:00
|
|
|
* @qdio: pointer to struct zfcp_qdio
|
|
|
|
* @q_req: pointer to struct zfcp_qdio_req
|
2005-04-17 05:20:36 +07:00
|
|
|
* @sg: scatter-gather list
|
|
|
|
* @max_sbals: upper bound for number of SBALs to be used
|
2008-06-10 23:20:57 +07:00
|
|
|
* Returns: number of bytes, or error (negativ)
|
2005-04-17 05:20:36 +07:00
|
|
|
*/
|
2010-02-17 17:18:59 +07:00
|
|
|
int zfcp_qdio_sbals_from_sg(struct zfcp_qdio *qdio, struct zfcp_qdio_req *q_req,
|
2010-04-30 23:09:34 +07:00
|
|
|
struct scatterlist *sg, int max_sbals)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2008-10-01 17:42:16 +07:00
|
|
|
struct qdio_buffer_element *sbale;
|
2010-04-30 23:09:33 +07:00
|
|
|
int bytes = 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* figure out last allowed SBAL */
|
2009-08-18 20:43:19 +07:00
|
|
|
zfcp_qdio_sbal_limit(qdio, q_req, max_sbals);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2008-06-10 23:20:57 +07:00
|
|
|
/* set storage-block type for this request */
|
2009-08-18 20:43:19 +07:00
|
|
|
sbale = zfcp_qdio_sbale_req(qdio, q_req);
|
2010-04-30 23:09:34 +07:00
|
|
|
sbale->flags |= q_req->sbtype;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2008-06-10 23:20:57 +07:00
|
|
|
for (; sg; sg = sg_next(sg)) {
|
2010-04-30 23:09:34 +07:00
|
|
|
sbale = zfcp_qdio_sbale_next(qdio, q_req);
|
2010-04-30 23:09:33 +07:00
|
|
|
if (!sbale) {
|
|
|
|
atomic_inc(&qdio->req_q_full);
|
|
|
|
zfcp_qdio_undo_sbals(qdio, q_req);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
sbale->addr = sg_virt(sg);
|
|
|
|
sbale->length = sg->length;
|
|
|
|
|
2008-06-10 23:20:57 +07:00
|
|
|
bytes += sg->length;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2008-06-10 23:20:57 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/* assume that no other SBALEs are to follow in the same SBAL */
|
2009-08-18 20:43:19 +07:00
|
|
|
sbale = zfcp_qdio_sbale_curr(qdio, q_req);
|
2005-04-17 05:20:36 +07:00
|
|
|
sbale->flags |= SBAL_FLAGS_LAST_ENTRY;
|
2008-06-10 23:20:57 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
return bytes;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2008-06-10 23:20:57 +07:00
|
|
|
* zfcp_qdio_send - set PCI flag in first SBALE and send req to QDIO
|
2009-08-18 20:43:19 +07:00
|
|
|
* @qdio: pointer to struct zfcp_qdio
|
2010-02-17 17:18:59 +07:00
|
|
|
* @q_req: pointer to struct zfcp_qdio_req
|
2008-06-10 23:20:57 +07:00
|
|
|
* Returns: 0 on success, error otherwise
|
2005-04-17 05:20:36 +07:00
|
|
|
*/
|
2010-02-17 17:18:59 +07:00
|
|
|
int zfcp_qdio_send(struct zfcp_qdio *qdio, struct zfcp_qdio_req *q_req)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2009-08-18 20:43:19 +07:00
|
|
|
struct zfcp_qdio_queue *req_q = &qdio->req_q;
|
2009-08-18 20:43:18 +07:00
|
|
|
int first = q_req->sbal_first;
|
|
|
|
int count = q_req->sbal_number;
|
2009-03-02 19:09:05 +07:00
|
|
|
int retval;
|
|
|
|
unsigned int qdio_flags = QDIO_FLAG_SYNC_OUTPUT;
|
2008-06-10 23:20:57 +07:00
|
|
|
|
2009-08-18 20:43:19 +07:00
|
|
|
zfcp_qdio_account(qdio);
|
2009-03-02 19:08:56 +07:00
|
|
|
|
2009-08-18 20:43:19 +07:00
|
|
|
retval = do_QDIO(qdio->adapter->ccw_device, qdio_flags, 0, first,
|
|
|
|
count);
|
2008-06-10 23:20:57 +07:00
|
|
|
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;
|
|
|
|
return 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2009-08-18 20:43:19 +07:00
|
|
|
|
|
|
|
static void zfcp_qdio_setup_init_data(struct qdio_initialize *id,
|
|
|
|
struct zfcp_qdio *qdio)
|
|
|
|
{
|
|
|
|
|
|
|
|
id->cdev = qdio->adapter->ccw_device;
|
|
|
|
id->q_format = QDIO_ZFCP_QFMT;
|
|
|
|
memcpy(id->adapter_name, dev_name(&id->cdev->dev), 8);
|
|
|
|
ASCEBC(id->adapter_name, 8);
|
|
|
|
id->qib_param_field_format = 0;
|
|
|
|
id->qib_param_field = NULL;
|
|
|
|
id->input_slib_elements = NULL;
|
|
|
|
id->output_slib_elements = NULL;
|
|
|
|
id->no_input_qs = 1;
|
|
|
|
id->no_output_qs = 1;
|
|
|
|
id->input_handler = zfcp_qdio_int_resp;
|
|
|
|
id->output_handler = zfcp_qdio_int_req;
|
|
|
|
id->int_parm = (unsigned long) qdio;
|
|
|
|
id->input_sbal_addr_array = (void **) (qdio->resp_q.sbal);
|
|
|
|
id->output_sbal_addr_array = (void **) (qdio->req_q.sbal);
|
|
|
|
|
|
|
|
}
|
2008-06-10 23:20:57 +07:00
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
*/
|
2009-08-18 20:43:22 +07:00
|
|
|
static int zfcp_qdio_allocate(struct zfcp_qdio *qdio)
|
2008-06-10 23:20:57 +07:00
|
|
|
{
|
2009-08-18 20:43:19 +07:00
|
|
|
struct qdio_initialize init_data;
|
2008-06-10 23:20:57 +07:00
|
|
|
|
2009-08-18 20:43:19 +07:00
|
|
|
if (zfcp_qdio_buffers_enqueue(qdio->req_q.sbal) ||
|
|
|
|
zfcp_qdio_buffers_enqueue(qdio->resp_q.sbal))
|
2008-06-10 23:20:57 +07:00
|
|
|
return -ENOMEM;
|
|
|
|
|
2009-08-18 20:43:19 +07:00
|
|
|
zfcp_qdio_setup_init_data(&init_data, qdio);
|
|
|
|
|
|
|
|
return qdio_allocate(&init_data);
|
2008-06-10 23:20:57 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* zfcp_close_qdio - close qdio queues for an adapter
|
2009-08-18 20:43:19 +07:00
|
|
|
* @qdio: pointer to structure zfcp_qdio
|
2005-04-17 05:20:36 +07:00
|
|
|
*/
|
2009-08-18 20:43:19 +07:00
|
|
|
void zfcp_qdio_close(struct zfcp_qdio *qdio)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2008-06-10 23:20:57 +07:00
|
|
|
struct zfcp_qdio_queue *req_q;
|
|
|
|
int first, count;
|
|
|
|
|
2009-08-18 20:43:19 +07:00
|
|
|
if (!(atomic_read(&qdio->adapter->status) & ZFCP_STATUS_ADAPTER_QDIOUP))
|
2008-06-10 23:20:57 +07:00
|
|
|
return;
|
|
|
|
|
|
|
|
/* clear QDIOUP flag, thus do_QDIO is not called during qdio_shutdown */
|
2009-08-18 20:43:19 +07:00
|
|
|
req_q = &qdio->req_q;
|
|
|
|
spin_lock_bh(&qdio->req_q_lock);
|
|
|
|
atomic_clear_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &qdio->adapter->status);
|
|
|
|
spin_unlock_bh(&qdio->req_q_lock);
|
2008-06-10 23:20:57 +07:00
|
|
|
|
2009-08-18 20:43:19 +07:00
|
|
|
qdio_shutdown(qdio->adapter->ccw_device,
|
|
|
|
QDIO_FLAG_CLEANUP_USING_CLEAR);
|
2008-06-10 23:20:57 +07:00
|
|
|
|
|
|
|
/* 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);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2008-06-10 23:20:57 +07:00
|
|
|
req_q->first = 0;
|
|
|
|
atomic_set(&req_q->count, 0);
|
2009-08-18 20:43:19 +07:00
|
|
|
qdio->resp_q.first = 0;
|
|
|
|
atomic_set(&qdio->resp_q.count, 0);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2008-06-10 23:20:57 +07:00
|
|
|
/**
|
|
|
|
* zfcp_qdio_open - prepare and initialize response queue
|
2009-08-18 20:43:19 +07:00
|
|
|
* @qdio: pointer to struct zfcp_qdio
|
2008-06-10 23:20:57 +07:00
|
|
|
* Returns: 0 on success, otherwise -EIO
|
|
|
|
*/
|
2009-08-18 20:43:19 +07:00
|
|
|
int zfcp_qdio_open(struct zfcp_qdio *qdio)
|
2008-06-10 23:20:57 +07:00
|
|
|
{
|
2008-10-01 17:42:16 +07:00
|
|
|
struct qdio_buffer_element *sbale;
|
2009-08-18 20:43:19 +07:00
|
|
|
struct qdio_initialize init_data;
|
|
|
|
struct ccw_device *cdev = qdio->adapter->ccw_device;
|
2008-06-10 23:20:57 +07:00
|
|
|
int cc;
|
|
|
|
|
2009-08-18 20:43:19 +07:00
|
|
|
if (atomic_read(&qdio->adapter->status) & ZFCP_STATUS_ADAPTER_QDIOUP)
|
2008-06-10 23:20:57 +07:00
|
|
|
return -EIO;
|
|
|
|
|
2009-08-18 20:43:19 +07:00
|
|
|
zfcp_qdio_setup_init_data(&init_data, qdio);
|
|
|
|
|
|
|
|
if (qdio_establish(&init_data))
|
2008-10-01 17:42:15 +07:00
|
|
|
goto failed_establish;
|
2008-06-10 23:20:57 +07:00
|
|
|
|
2009-08-18 20:43:19 +07:00
|
|
|
if (qdio_activate(cdev))
|
2008-06-10 23:20:57 +07:00
|
|
|
goto failed_qdio;
|
|
|
|
|
|
|
|
for (cc = 0; cc < QDIO_MAX_BUFFERS_PER_Q; cc++) {
|
2009-08-18 20:43:19 +07:00
|
|
|
sbale = &(qdio->resp_q.sbal[cc]->element[0]);
|
2008-06-10 23:20:57 +07:00
|
|
|
sbale->length = 0;
|
|
|
|
sbale->flags = SBAL_FLAGS_LAST_ENTRY;
|
|
|
|
sbale->addr = NULL;
|
|
|
|
}
|
|
|
|
|
2009-08-18 20:43:19 +07:00
|
|
|
if (do_QDIO(cdev, QDIO_FLAG_SYNC_INPUT, 0, 0,
|
2008-10-01 17:42:15 +07:00
|
|
|
QDIO_MAX_BUFFERS_PER_Q))
|
2008-06-10 23:20:57 +07:00
|
|
|
goto failed_qdio;
|
|
|
|
|
|
|
|
/* set index of first avalable SBALS / number of available SBALS */
|
2009-08-18 20:43:19 +07:00
|
|
|
qdio->req_q.first = 0;
|
|
|
|
atomic_set(&qdio->req_q.count, QDIO_MAX_BUFFERS_PER_Q);
|
2008-06-10 23:20:57 +07:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
failed_qdio:
|
2009-08-18 20:43:19 +07:00
|
|
|
qdio_shutdown(cdev, QDIO_FLAG_CLEANUP_USING_CLEAR);
|
2008-10-01 17:42:15 +07:00
|
|
|
failed_establish:
|
2009-08-18 20:43:19 +07:00
|
|
|
dev_err(&cdev->dev,
|
2008-10-01 17:42:15 +07:00
|
|
|
"Setting up the QDIO connection to the FCP adapter failed\n");
|
2008-06-10 23:20:57 +07:00
|
|
|
return -EIO;
|
|
|
|
}
|
2009-08-18 20:43:22 +07:00
|
|
|
|
|
|
|
void zfcp_qdio_destroy(struct zfcp_qdio *qdio)
|
|
|
|
{
|
|
|
|
struct qdio_buffer **sbal_req, **sbal_resp;
|
|
|
|
int p;
|
|
|
|
|
|
|
|
if (!qdio)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (qdio->adapter->ccw_device)
|
|
|
|
qdio_free(qdio->adapter->ccw_device);
|
|
|
|
|
|
|
|
sbal_req = qdio->req_q.sbal;
|
|
|
|
sbal_resp = qdio->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]);
|
|
|
|
}
|
|
|
|
|
|
|
|
kfree(qdio);
|
|
|
|
}
|
|
|
|
|
|
|
|
int zfcp_qdio_setup(struct zfcp_adapter *adapter)
|
|
|
|
{
|
|
|
|
struct zfcp_qdio *qdio;
|
|
|
|
|
|
|
|
qdio = kzalloc(sizeof(struct zfcp_qdio), GFP_KERNEL);
|
|
|
|
if (!qdio)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
qdio->adapter = adapter;
|
|
|
|
|
|
|
|
if (zfcp_qdio_allocate(qdio)) {
|
|
|
|
zfcp_qdio_destroy(qdio);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
spin_lock_init(&qdio->req_q_lock);
|
|
|
|
spin_lock_init(&qdio->stat_lock);
|
|
|
|
|
|
|
|
adapter->qdio = qdio;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|