mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-15 16:24:13 +08:00
b3f0a1ee9e
The qdio layer currently provides its own infrastructure to scan for Request Queue completions & to report them to the device driver. This comes with several drawbacks - having an async tasklet & timer construct in qdio introduces additional lifetime complexity, and makes it harder to integrate them with the rest of the device driver. The timeouts are also currently hard-coded, and can't be tweaked without affecting other qdio drivers (ie. qeth). But due to recent enhancements to the qdio layer, zfcp can actually take full control of the Request Queue completion processing. It merely needs to opt-out from the qdio layer mechanisms by setting the scan_threshold to 0, and then use qdio_inspect_queue() to scan for completions. So re-implement the tasklet & timer mechanism in zfcp, while initially copying the scan conditions from qdio's handle_outbound() and qdio_outbound_tasklet(). One minor behavioural change is that zfcp_qdio_send() will unconditionally reduce the timeout to 1 HZ, rather than leaving it at 10 Hz if it was last armed by the tasklet. This just makes things more consistent. Also note that we can drop a lot of the accumulated cruft in qdio_outbound_tasklet(), as zfcp doesn't even use PCI interrupt requests any longer. This also slightly touches the Response Queue processing, as qdio_get_next_buffers() will no longer implicitly scan for Request Queue completions. So complete the migration to qdio_inspect_queue() here as well and make the tasklet_schedule() visible. Link: https://lore.kernel.org/r/018d3ddd029f8d6ac00cf4184880288c637c4fd1.1618417667.git.bblock@linux.ibm.com Reviewed-by: Benjamin Block <bblock@linux.ibm.com> Signed-off-by: Julian Wiedmann <jwi@linux.ibm.com> Signed-off-by: Benjamin Block <bblock@linux.ibm.com> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
268 lines
7.3 KiB
C
268 lines
7.3 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
/*
|
|
* zfcp device driver
|
|
*
|
|
* Header file for zfcp qdio interface
|
|
*
|
|
* Copyright IBM Corp. 2010
|
|
*/
|
|
|
|
#ifndef ZFCP_QDIO_H
|
|
#define ZFCP_QDIO_H
|
|
|
|
#include <linux/interrupt.h>
|
|
#include <asm/qdio.h>
|
|
|
|
#define ZFCP_QDIO_SBALE_LEN PAGE_SIZE
|
|
|
|
/* Max SBALS for chaining */
|
|
#define ZFCP_QDIO_MAX_SBALS_PER_REQ 36
|
|
|
|
/**
|
|
* struct zfcp_qdio - basic qdio data structure
|
|
* @res_q: response queue
|
|
* @req_q: request queue
|
|
* @req_q_idx: index of next free buffer
|
|
* @req_q_free: number of free buffers in queue
|
|
* @stat_lock: lock to protect req_q_util and req_q_time
|
|
* @req_q_lock: lock to serialize access to request queue
|
|
* @req_q_time: time of last fill level change
|
|
* @req_q_util: used for accounting
|
|
* @req_q_full: queue full incidents
|
|
* @req_q_wq: used to wait for SBAL availability
|
|
* @irq_tasklet: used for QDIO interrupt processing
|
|
* @request_tasklet: used for Request Queue completion processing
|
|
* @request_timer: used to trigger the Request Queue completion processing
|
|
* @adapter: adapter used in conjunction with this qdio structure
|
|
* @max_sbale_per_sbal: qdio limit per sbal
|
|
* @max_sbale_per_req: qdio limit per request
|
|
*/
|
|
struct zfcp_qdio {
|
|
struct qdio_buffer *res_q[QDIO_MAX_BUFFERS_PER_Q];
|
|
struct qdio_buffer *req_q[QDIO_MAX_BUFFERS_PER_Q];
|
|
u8 req_q_idx;
|
|
atomic_t req_q_free;
|
|
spinlock_t stat_lock;
|
|
spinlock_t req_q_lock;
|
|
unsigned long long req_q_time;
|
|
u64 req_q_util;
|
|
atomic_t req_q_full;
|
|
wait_queue_head_t req_q_wq;
|
|
struct tasklet_struct irq_tasklet;
|
|
struct tasklet_struct request_tasklet;
|
|
struct timer_list request_timer;
|
|
struct zfcp_adapter *adapter;
|
|
u16 max_sbale_per_sbal;
|
|
u16 max_sbale_per_req;
|
|
};
|
|
|
|
/**
|
|
* struct zfcp_qdio_req - qdio queue related values for a request
|
|
* @sbtype: sbal type flags for sbale 0
|
|
* @sbal_number: number of free sbals
|
|
* @sbal_first: first sbal for this request
|
|
* @sbal_last: last sbal for this request
|
|
* @sbal_limit: last possible sbal for this request
|
|
* @sbale_curr: current sbale at creation of this request
|
|
* @qdio_outb_usage: usage of outbound queue
|
|
*/
|
|
struct zfcp_qdio_req {
|
|
u8 sbtype;
|
|
u8 sbal_number;
|
|
u8 sbal_first;
|
|
u8 sbal_last;
|
|
u8 sbal_limit;
|
|
u8 sbale_curr;
|
|
u16 qdio_outb_usage;
|
|
};
|
|
|
|
/**
|
|
* zfcp_qdio_sbale_req - return pointer to sbale on req_q for a request
|
|
* @qdio: pointer to struct zfcp_qdio
|
|
* @q_req: pointer to struct zfcp_qdio_req
|
|
* Returns: pointer to qdio_buffer_element (sbale) structure
|
|
*/
|
|
static inline struct qdio_buffer_element *
|
|
zfcp_qdio_sbale_req(struct zfcp_qdio *qdio, struct zfcp_qdio_req *q_req)
|
|
{
|
|
return &qdio->req_q[q_req->sbal_last]->element[0];
|
|
}
|
|
|
|
/**
|
|
* zfcp_qdio_sbale_curr - return current sbale on req_q for a request
|
|
* @qdio: pointer to struct zfcp_qdio
|
|
* @q_req: pointer to struct zfcp_qdio_req
|
|
* Returns: pointer to qdio_buffer_element (sbale) structure
|
|
*/
|
|
static inline struct qdio_buffer_element *
|
|
zfcp_qdio_sbale_curr(struct zfcp_qdio *qdio, struct zfcp_qdio_req *q_req)
|
|
{
|
|
return &qdio->req_q[q_req->sbal_last]->element[q_req->sbale_curr];
|
|
}
|
|
|
|
/**
|
|
* zfcp_qdio_req_init - initialize qdio request
|
|
* @qdio: request queue where to start putting the request
|
|
* @q_req: the qdio request to start
|
|
* @req_id: The request id
|
|
* @sbtype: type flags to set for all sbals
|
|
* @data: First data block
|
|
* @len: Length of first data block
|
|
*
|
|
* This is the start of putting the request into the queue, the last
|
|
* step is passing the request to zfcp_qdio_send. The request queue
|
|
* lock must be held during the whole process from init to send.
|
|
*/
|
|
static inline
|
|
void zfcp_qdio_req_init(struct zfcp_qdio *qdio, struct zfcp_qdio_req *q_req,
|
|
unsigned long req_id, u8 sbtype, void *data, u32 len)
|
|
{
|
|
struct qdio_buffer_element *sbale;
|
|
int count = min(atomic_read(&qdio->req_q_free),
|
|
ZFCP_QDIO_MAX_SBALS_PER_REQ);
|
|
|
|
q_req->sbal_first = q_req->sbal_last = qdio->req_q_idx;
|
|
q_req->sbal_number = 1;
|
|
q_req->sbtype = sbtype;
|
|
q_req->sbale_curr = 1;
|
|
q_req->sbal_limit = (q_req->sbal_first + count - 1)
|
|
% QDIO_MAX_BUFFERS_PER_Q;
|
|
|
|
sbale = zfcp_qdio_sbale_req(qdio, q_req);
|
|
sbale->addr = req_id;
|
|
sbale->eflags = 0;
|
|
sbale->sflags = SBAL_SFLAGS0_COMMAND | sbtype;
|
|
|
|
if (unlikely(!data))
|
|
return;
|
|
sbale++;
|
|
sbale->addr = virt_to_phys(data);
|
|
sbale->length = len;
|
|
}
|
|
|
|
/**
|
|
* zfcp_qdio_fill_next - Fill next sbale, only for single sbal requests
|
|
* @qdio: pointer to struct zfcp_qdio
|
|
* @q_req: pointer to struct zfcp_queue_req
|
|
* @data: pointer to data
|
|
* @len: length of data
|
|
*
|
|
* This is only required for single sbal requests, calling it when
|
|
* wrapping around to the next sbal is a bug.
|
|
*/
|
|
static inline
|
|
void zfcp_qdio_fill_next(struct zfcp_qdio *qdio, struct zfcp_qdio_req *q_req,
|
|
void *data, u32 len)
|
|
{
|
|
struct qdio_buffer_element *sbale;
|
|
|
|
BUG_ON(q_req->sbale_curr == qdio->max_sbale_per_sbal - 1);
|
|
q_req->sbale_curr++;
|
|
sbale = zfcp_qdio_sbale_curr(qdio, q_req);
|
|
sbale->addr = virt_to_phys(data);
|
|
sbale->length = len;
|
|
}
|
|
|
|
/**
|
|
* zfcp_qdio_set_sbale_last - set last entry flag in current sbale
|
|
* @qdio: pointer to struct zfcp_qdio
|
|
* @q_req: pointer to struct zfcp_queue_req
|
|
*/
|
|
static inline
|
|
void zfcp_qdio_set_sbale_last(struct zfcp_qdio *qdio,
|
|
struct zfcp_qdio_req *q_req)
|
|
{
|
|
struct qdio_buffer_element *sbale;
|
|
|
|
sbale = zfcp_qdio_sbale_curr(qdio, q_req);
|
|
sbale->eflags |= SBAL_EFLAGS_LAST_ENTRY;
|
|
}
|
|
|
|
/**
|
|
* zfcp_qdio_sg_one_sbal - check if one sbale is enough for sg data
|
|
* @sg: The scatterlist where to check the data size
|
|
*
|
|
* Returns: 1 when one sbale is enough for the data in the scatterlist,
|
|
* 0 if not.
|
|
*/
|
|
static inline
|
|
int zfcp_qdio_sg_one_sbale(struct scatterlist *sg)
|
|
{
|
|
return sg_is_last(sg) && sg->length <= ZFCP_QDIO_SBALE_LEN;
|
|
}
|
|
|
|
/**
|
|
* zfcp_qdio_skip_to_last_sbale - skip to last sbale in sbal
|
|
* @qdio: pointer to struct zfcp_qdio
|
|
* @q_req: The current zfcp_qdio_req
|
|
*/
|
|
static inline
|
|
void zfcp_qdio_skip_to_last_sbale(struct zfcp_qdio *qdio,
|
|
struct zfcp_qdio_req *q_req)
|
|
{
|
|
q_req->sbale_curr = qdio->max_sbale_per_sbal - 1;
|
|
}
|
|
|
|
/**
|
|
* zfcp_qdio_sbal_limit - set the sbal limit for a request in q_req
|
|
* @qdio: pointer to struct zfcp_qdio
|
|
* @q_req: The current zfcp_qdio_req
|
|
* @max_sbals: maximum number of SBALs allowed
|
|
*/
|
|
static inline
|
|
void zfcp_qdio_sbal_limit(struct zfcp_qdio *qdio,
|
|
struct zfcp_qdio_req *q_req, int max_sbals)
|
|
{
|
|
int count = min(atomic_read(&qdio->req_q_free), max_sbals);
|
|
|
|
q_req->sbal_limit = (q_req->sbal_first + count - 1) %
|
|
QDIO_MAX_BUFFERS_PER_Q;
|
|
}
|
|
|
|
/**
|
|
* zfcp_qdio_set_data_div - set data division count
|
|
* @qdio: pointer to struct zfcp_qdio
|
|
* @q_req: The current zfcp_qdio_req
|
|
* @count: The data division count
|
|
*/
|
|
static inline
|
|
void zfcp_qdio_set_data_div(struct zfcp_qdio *qdio,
|
|
struct zfcp_qdio_req *q_req, u32 count)
|
|
{
|
|
struct qdio_buffer_element *sbale;
|
|
|
|
sbale = qdio->req_q[q_req->sbal_first]->element;
|
|
sbale->length = count;
|
|
}
|
|
|
|
/**
|
|
* zfcp_qdio_real_bytes - count bytes used
|
|
* @sg: pointer to struct scatterlist
|
|
*/
|
|
static inline
|
|
unsigned int zfcp_qdio_real_bytes(struct scatterlist *sg)
|
|
{
|
|
unsigned int real_bytes = 0;
|
|
|
|
for (; sg; sg = sg_next(sg))
|
|
real_bytes += sg->length;
|
|
|
|
return real_bytes;
|
|
}
|
|
|
|
/**
|
|
* zfcp_qdio_set_scount - set SBAL count value
|
|
* @qdio: pointer to struct zfcp_qdio
|
|
* @q_req: The current zfcp_qdio_req
|
|
*/
|
|
static inline
|
|
void zfcp_qdio_set_scount(struct zfcp_qdio *qdio, struct zfcp_qdio_req *q_req)
|
|
{
|
|
struct qdio_buffer_element *sbale;
|
|
|
|
sbale = qdio->req_q[q_req->sbal_first]->element;
|
|
sbale->scount = q_req->sbal_number - 1;
|
|
}
|
|
|
|
#endif /* ZFCP_QDIO_H */
|