Commit 1451f414 authored by Frank Haverkamp's avatar Frank Haverkamp Committed by Greg Kroah-Hartman

GenWQE: Support blocking when DDCB queue is busy

When the GenWQE hardware queue was busy, the driver returned simply
-EBUSY. This caused polling by applications which increased the load
on the already busy system. This change implements the possiblity to
sleep on a waitqueue instead when the DDCB queue is busy. The
requestor is woken up when there is free space on the queue again.
The old way to get -EBUSY is still available if the device is openend
with O_NONBLOCKING. The default is now blocking behavior.
Signed-off-by: default avatarFrank Haverkamp <haver@linux.vnet.ibm.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 08e4906c
...@@ -201,7 +201,8 @@ static inline void genwqe_mapping_init(struct dma_mapping *m, ...@@ -201,7 +201,8 @@ static inline void genwqe_mapping_init(struct dma_mapping *m,
* @ddcb_seq: Sequence number of last DDCB * @ddcb_seq: Sequence number of last DDCB
* @ddcbs_in_flight: Currently enqueued DDCBs * @ddcbs_in_flight: Currently enqueued DDCBs
* @ddcbs_completed: Number of already completed DDCBs * @ddcbs_completed: Number of already completed DDCBs
* @busy: Number of -EBUSY returns * @return_on_busy: Number of -EBUSY returns on full queue
* @wait_on_busy: Number of waits on full queue
* @ddcb_daddr: DMA address of first DDCB in the queue * @ddcb_daddr: DMA address of first DDCB in the queue
* @ddcb_vaddr: Kernel virtual address of first DDCB in the queue * @ddcb_vaddr: Kernel virtual address of first DDCB in the queue
* @ddcb_req: Associated requests (one per DDCB) * @ddcb_req: Associated requests (one per DDCB)
...@@ -218,7 +219,8 @@ struct ddcb_queue { ...@@ -218,7 +219,8 @@ struct ddcb_queue {
unsigned int ddcbs_in_flight; /* number of ddcbs in processing */ unsigned int ddcbs_in_flight; /* number of ddcbs in processing */
unsigned int ddcbs_completed; unsigned int ddcbs_completed;
unsigned int ddcbs_max_in_flight; unsigned int ddcbs_max_in_flight;
unsigned int busy; /* how many times -EBUSY? */ unsigned int return_on_busy; /* how many times -EBUSY? */
unsigned int wait_on_busy;
dma_addr_t ddcb_daddr; /* DMA address */ dma_addr_t ddcb_daddr; /* DMA address */
struct ddcb *ddcb_vaddr; /* kernel virtual addr for DDCBs */ struct ddcb *ddcb_vaddr; /* kernel virtual addr for DDCBs */
...@@ -226,7 +228,7 @@ struct ddcb_queue { ...@@ -226,7 +228,7 @@ struct ddcb_queue {
wait_queue_head_t *ddcb_waitqs; /* waitqueue per ddcb */ wait_queue_head_t *ddcb_waitqs; /* waitqueue per ddcb */
spinlock_t ddcb_lock; /* exclusive access to queue */ spinlock_t ddcb_lock; /* exclusive access to queue */
wait_queue_head_t ddcb_waitq; /* wait for ddcb processing */ wait_queue_head_t busy_waitq; /* wait for ddcb processing */
/* registers or the respective queue to be used */ /* registers or the respective queue to be used */
u32 IO_QUEUE_CONFIG; u32 IO_QUEUE_CONFIG;
...@@ -508,7 +510,7 @@ static inline bool dma_mapping_used(struct dma_mapping *m) ...@@ -508,7 +510,7 @@ static inline bool dma_mapping_used(struct dma_mapping *m)
* buildup and teardown. * buildup and teardown.
*/ */
int __genwqe_execute_ddcb(struct genwqe_dev *cd, int __genwqe_execute_ddcb(struct genwqe_dev *cd,
struct genwqe_ddcb_cmd *cmd); struct genwqe_ddcb_cmd *cmd, unsigned int f_flags);
/** /**
* __genwqe_execute_raw_ddcb() - Execute DDCB request without addr translation * __genwqe_execute_raw_ddcb() - Execute DDCB request without addr translation
...@@ -520,9 +522,12 @@ int __genwqe_execute_ddcb(struct genwqe_dev *cd, ...@@ -520,9 +522,12 @@ int __genwqe_execute_ddcb(struct genwqe_dev *cd,
* modification. * modification.
*/ */
int __genwqe_execute_raw_ddcb(struct genwqe_dev *cd, int __genwqe_execute_raw_ddcb(struct genwqe_dev *cd,
struct genwqe_ddcb_cmd *cmd); struct genwqe_ddcb_cmd *cmd,
unsigned int f_flags);
int __genwqe_enqueue_ddcb(struct genwqe_dev *cd,
struct ddcb_requ *req,
unsigned int f_flags);
int __genwqe_enqueue_ddcb(struct genwqe_dev *cd, struct ddcb_requ *req);
int __genwqe_wait_ddcb(struct genwqe_dev *cd, struct ddcb_requ *req); int __genwqe_wait_ddcb(struct genwqe_dev *cd, struct ddcb_requ *req);
int __genwqe_purge_ddcb(struct genwqe_dev *cd, struct ddcb_requ *req); int __genwqe_purge_ddcb(struct genwqe_dev *cd, struct ddcb_requ *req);
......
...@@ -448,8 +448,10 @@ static int genwqe_check_ddcb_queue(struct genwqe_dev *cd, ...@@ -448,8 +448,10 @@ static int genwqe_check_ddcb_queue(struct genwqe_dev *cd,
queue->ddcbs_completed++; queue->ddcbs_completed++;
queue->ddcbs_in_flight--; queue->ddcbs_in_flight--;
/* wake up process waiting for this DDCB */ /* wake up process waiting for this DDCB, and
processes on the busy queue */
wake_up_interruptible(&queue->ddcb_waitqs[queue->ddcb_act]); wake_up_interruptible(&queue->ddcb_waitqs[queue->ddcb_act]);
wake_up_interruptible(&queue->busy_waitq);
pick_next_one: pick_next_one:
queue->ddcb_act = (queue->ddcb_act + 1) % queue->ddcb_max; queue->ddcb_act = (queue->ddcb_act + 1) % queue->ddcb_max;
...@@ -745,14 +747,16 @@ int genwqe_init_debug_data(struct genwqe_dev *cd, struct genwqe_debug_data *d) ...@@ -745,14 +747,16 @@ int genwqe_init_debug_data(struct genwqe_dev *cd, struct genwqe_debug_data *d)
/** /**
* __genwqe_enqueue_ddcb() - Enqueue a DDCB * __genwqe_enqueue_ddcb() - Enqueue a DDCB
* @cd: pointer to genwqe device descriptor * @cd: pointer to genwqe device descriptor
* @req: pointer to DDCB execution request * @req: pointer to DDCB execution request
* @f_flags: file mode: blocking, non-blocking
* *
* Return: 0 if enqueuing succeeded * Return: 0 if enqueuing succeeded
* -EIO if card is unusable/PCIe problems * -EIO if card is unusable/PCIe problems
* -EBUSY if enqueuing failed * -EBUSY if enqueuing failed
*/ */
int __genwqe_enqueue_ddcb(struct genwqe_dev *cd, struct ddcb_requ *req) int __genwqe_enqueue_ddcb(struct genwqe_dev *cd, struct ddcb_requ *req,
unsigned int f_flags)
{ {
struct ddcb *pddcb; struct ddcb *pddcb;
unsigned long flags; unsigned long flags;
...@@ -760,6 +764,7 @@ int __genwqe_enqueue_ddcb(struct genwqe_dev *cd, struct ddcb_requ *req) ...@@ -760,6 +764,7 @@ int __genwqe_enqueue_ddcb(struct genwqe_dev *cd, struct ddcb_requ *req)
struct pci_dev *pci_dev = cd->pci_dev; struct pci_dev *pci_dev = cd->pci_dev;
u16 icrc; u16 icrc;
retry:
if (cd->card_state != GENWQE_CARD_USED) { if (cd->card_state != GENWQE_CARD_USED) {
printk_ratelimited(KERN_ERR printk_ratelimited(KERN_ERR
"%s %s: [%s] Card is unusable/PCIe problem Req#%d\n", "%s %s: [%s] Card is unusable/PCIe problem Req#%d\n",
...@@ -785,9 +790,24 @@ int __genwqe_enqueue_ddcb(struct genwqe_dev *cd, struct ddcb_requ *req) ...@@ -785,9 +790,24 @@ int __genwqe_enqueue_ddcb(struct genwqe_dev *cd, struct ddcb_requ *req)
pddcb = get_next_ddcb(cd, queue, &req->num); /* get ptr and num */ pddcb = get_next_ddcb(cd, queue, &req->num); /* get ptr and num */
if (pddcb == NULL) { if (pddcb == NULL) {
int rc;
spin_unlock_irqrestore(&queue->ddcb_lock, flags); spin_unlock_irqrestore(&queue->ddcb_lock, flags);
queue->busy++;
return -EBUSY; if (f_flags & O_NONBLOCK) {
queue->return_on_busy++;
return -EBUSY;
}
queue->wait_on_busy++;
rc = wait_event_interruptible(queue->busy_waitq,
queue_free_ddcbs(queue) != 0);
dev_dbg(&pci_dev->dev, "[%s] waiting for free DDCB: rc=%d\n",
__func__, rc);
if (rc == -ERESTARTSYS)
return rc; /* interrupted by a signal */
goto retry;
} }
if (queue->ddcb_req[req->num] != NULL) { if (queue->ddcb_req[req->num] != NULL) {
...@@ -890,9 +910,11 @@ int __genwqe_enqueue_ddcb(struct genwqe_dev *cd, struct ddcb_requ *req) ...@@ -890,9 +910,11 @@ int __genwqe_enqueue_ddcb(struct genwqe_dev *cd, struct ddcb_requ *req)
* __genwqe_execute_raw_ddcb() - Setup and execute DDCB * __genwqe_execute_raw_ddcb() - Setup and execute DDCB
* @cd: pointer to genwqe device descriptor * @cd: pointer to genwqe device descriptor
* @req: user provided DDCB request * @req: user provided DDCB request
* @f_flags: file mode: blocking, non-blocking
*/ */
int __genwqe_execute_raw_ddcb(struct genwqe_dev *cd, int __genwqe_execute_raw_ddcb(struct genwqe_dev *cd,
struct genwqe_ddcb_cmd *cmd) struct genwqe_ddcb_cmd *cmd,
unsigned int f_flags)
{ {
int rc = 0; int rc = 0;
struct pci_dev *pci_dev = cd->pci_dev; struct pci_dev *pci_dev = cd->pci_dev;
...@@ -908,7 +930,7 @@ int __genwqe_execute_raw_ddcb(struct genwqe_dev *cd, ...@@ -908,7 +930,7 @@ int __genwqe_execute_raw_ddcb(struct genwqe_dev *cd,
__func__, cmd->asiv_length); __func__, cmd->asiv_length);
return -EINVAL; return -EINVAL;
} }
rc = __genwqe_enqueue_ddcb(cd, req); rc = __genwqe_enqueue_ddcb(cd, req, f_flags);
if (rc != 0) if (rc != 0)
return rc; return rc;
...@@ -1014,7 +1036,8 @@ static int setup_ddcb_queue(struct genwqe_dev *cd, struct ddcb_queue *queue) ...@@ -1014,7 +1036,8 @@ static int setup_ddcb_queue(struct genwqe_dev *cd, struct ddcb_queue *queue)
queue->ddcbs_in_flight = 0; /* statistics */ queue->ddcbs_in_flight = 0; /* statistics */
queue->ddcbs_max_in_flight = 0; queue->ddcbs_max_in_flight = 0;
queue->ddcbs_completed = 0; queue->ddcbs_completed = 0;
queue->busy = 0; queue->return_on_busy = 0;
queue->wait_on_busy = 0;
queue->ddcb_seq = 0x100; /* start sequence number */ queue->ddcb_seq = 0x100; /* start sequence number */
queue->ddcb_max = genwqe_ddcb_max; /* module parameter */ queue->ddcb_max = genwqe_ddcb_max; /* module parameter */
...@@ -1054,7 +1077,7 @@ static int setup_ddcb_queue(struct genwqe_dev *cd, struct ddcb_queue *queue) ...@@ -1054,7 +1077,7 @@ static int setup_ddcb_queue(struct genwqe_dev *cd, struct ddcb_queue *queue)
queue->ddcb_next = 0; /* queue is empty */ queue->ddcb_next = 0; /* queue is empty */
spin_lock_init(&queue->ddcb_lock); spin_lock_init(&queue->ddcb_lock);
init_waitqueue_head(&queue->ddcb_waitq); init_waitqueue_head(&queue->busy_waitq);
val64 = ((u64)(queue->ddcb_max - 1) << 8); /* lastptr */ val64 = ((u64)(queue->ddcb_max - 1) << 8); /* lastptr */
__genwqe_writeq(cd, queue->IO_QUEUE_CONFIG, 0x07); /* iCRC/vCRC */ __genwqe_writeq(cd, queue->IO_QUEUE_CONFIG, 0x07); /* iCRC/vCRC */
...@@ -1302,6 +1325,7 @@ static int queue_wake_up_all(struct genwqe_dev *cd) ...@@ -1302,6 +1325,7 @@ static int queue_wake_up_all(struct genwqe_dev *cd)
for (i = 0; i < queue->ddcb_max; i++) for (i = 0; i < queue->ddcb_max; i++)
wake_up_interruptible(&queue->ddcb_waitqs[queue->ddcb_act]); wake_up_interruptible(&queue->ddcb_waitqs[queue->ddcb_act]);
wake_up_interruptible(&queue->busy_waitq);
spin_unlock_irqrestore(&queue->ddcb_lock, flags); spin_unlock_irqrestore(&queue->ddcb_lock, flags);
return 0; return 0;
......
...@@ -244,14 +244,16 @@ static int genwqe_ddcb_info_show(struct seq_file *s, void *unused) ...@@ -244,14 +244,16 @@ static int genwqe_ddcb_info_show(struct seq_file *s, void *unused)
" ddcbs_in_flight: %u\n" " ddcbs_in_flight: %u\n"
" ddcbs_max_in_flight: %u\n" " ddcbs_max_in_flight: %u\n"
" ddcbs_completed: %u\n" " ddcbs_completed: %u\n"
" busy: %u\n" " return_on_busy: %u\n"
" wait_on_busy: %u\n"
" irqs_processed: %u\n", " irqs_processed: %u\n",
queue->ddcb_max, (long long)queue->ddcb_daddr, queue->ddcb_max, (long long)queue->ddcb_daddr,
(long long)queue->ddcb_daddr + (long long)queue->ddcb_daddr +
(queue->ddcb_max * DDCB_LENGTH), (queue->ddcb_max * DDCB_LENGTH),
(long long)queue->ddcb_vaddr, queue->ddcbs_in_flight, (long long)queue->ddcb_vaddr, queue->ddcbs_in_flight,
queue->ddcbs_max_in_flight, queue->ddcbs_completed, queue->ddcbs_max_in_flight, queue->ddcbs_completed,
queue->busy, cd->irqs_processed); queue->return_on_busy, queue->wait_on_busy,
cd->irqs_processed);
/* Hardware State */ /* Hardware State */
seq_printf(s, " 0x%08x 0x%016llx IO_QUEUE_CONFIG\n" seq_printf(s, " 0x%08x 0x%016llx IO_QUEUE_CONFIG\n"
......
...@@ -516,6 +516,7 @@ static int do_flash_update(struct genwqe_file *cfile, ...@@ -516,6 +516,7 @@ static int do_flash_update(struct genwqe_file *cfile,
u32 crc; u32 crc;
u8 cmdopts; u8 cmdopts;
struct genwqe_dev *cd = cfile->cd; struct genwqe_dev *cd = cfile->cd;
struct file *filp = cfile->filp;
struct pci_dev *pci_dev = cd->pci_dev; struct pci_dev *pci_dev = cd->pci_dev;
if ((load->size & 0x3) != 0) if ((load->size & 0x3) != 0)
...@@ -610,7 +611,7 @@ static int do_flash_update(struct genwqe_file *cfile, ...@@ -610,7 +611,7 @@ static int do_flash_update(struct genwqe_file *cfile,
/* For Genwqe5 we get back the calculated CRC */ /* For Genwqe5 we get back the calculated CRC */
*(u64 *)&req->asv[0] = 0ULL; /* 0x80 */ *(u64 *)&req->asv[0] = 0ULL; /* 0x80 */
rc = __genwqe_execute_raw_ddcb(cd, req); rc = __genwqe_execute_raw_ddcb(cd, req, filp->f_flags);
load->retc = req->retc; load->retc = req->retc;
load->attn = req->attn; load->attn = req->attn;
...@@ -650,6 +651,7 @@ static int do_flash_read(struct genwqe_file *cfile, ...@@ -650,6 +651,7 @@ static int do_flash_read(struct genwqe_file *cfile,
u8 *xbuf; u8 *xbuf;
u8 cmdopts; u8 cmdopts;
struct genwqe_dev *cd = cfile->cd; struct genwqe_dev *cd = cfile->cd;
struct file *filp = cfile->filp;
struct pci_dev *pci_dev = cd->pci_dev; struct pci_dev *pci_dev = cd->pci_dev;
struct genwqe_ddcb_cmd *cmd; struct genwqe_ddcb_cmd *cmd;
...@@ -727,7 +729,7 @@ static int do_flash_read(struct genwqe_file *cfile, ...@@ -727,7 +729,7 @@ static int do_flash_read(struct genwqe_file *cfile,
/* we only get back the calculated CRC */ /* we only get back the calculated CRC */
*(u64 *)&cmd->asv[0] = 0ULL; /* 0x80 */ *(u64 *)&cmd->asv[0] = 0ULL; /* 0x80 */
rc = __genwqe_execute_raw_ddcb(cd, cmd); rc = __genwqe_execute_raw_ddcb(cd, cmd, filp->f_flags);
load->retc = cmd->retc; load->retc = cmd->retc;
load->attn = cmd->attn; load->attn = cmd->attn;
...@@ -988,13 +990,14 @@ static int genwqe_execute_ddcb(struct genwqe_file *cfile, ...@@ -988,13 +990,14 @@ static int genwqe_execute_ddcb(struct genwqe_file *cfile,
{ {
int rc; int rc;
struct genwqe_dev *cd = cfile->cd; struct genwqe_dev *cd = cfile->cd;
struct file *filp = cfile->filp;
struct ddcb_requ *req = container_of(cmd, struct ddcb_requ, cmd); struct ddcb_requ *req = container_of(cmd, struct ddcb_requ, cmd);
rc = ddcb_cmd_fixups(cfile, req); rc = ddcb_cmd_fixups(cfile, req);
if (rc != 0) if (rc != 0)
return rc; return rc;
rc = __genwqe_execute_raw_ddcb(cd, cmd); rc = __genwqe_execute_raw_ddcb(cd, cmd, filp->f_flags);
ddcb_cmd_cleanup(cfile, req); ddcb_cmd_cleanup(cfile, req);
return rc; return rc;
} }
...@@ -1006,6 +1009,7 @@ static int do_execute_ddcb(struct genwqe_file *cfile, ...@@ -1006,6 +1009,7 @@ static int do_execute_ddcb(struct genwqe_file *cfile,
struct genwqe_ddcb_cmd *cmd; struct genwqe_ddcb_cmd *cmd;
struct ddcb_requ *req; struct ddcb_requ *req;
struct genwqe_dev *cd = cfile->cd; struct genwqe_dev *cd = cfile->cd;
struct file *filp = cfile->filp;
cmd = ddcb_requ_alloc(); cmd = ddcb_requ_alloc();
if (cmd == NULL) if (cmd == NULL)
...@@ -1021,7 +1025,7 @@ static int do_execute_ddcb(struct genwqe_file *cfile, ...@@ -1021,7 +1025,7 @@ static int do_execute_ddcb(struct genwqe_file *cfile,
if (!raw) if (!raw)
rc = genwqe_execute_ddcb(cfile, cmd); rc = genwqe_execute_ddcb(cfile, cmd);
else else
rc = __genwqe_execute_raw_ddcb(cd, cmd); rc = __genwqe_execute_raw_ddcb(cd, cmd, filp->f_flags);
/* Copy back only the modifed fields. Do not copy ASIV /* Copy back only the modifed fields. Do not copy ASIV
back since the copy got modified by the driver. */ back since the copy got modified by the driver. */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment