sunvdc: convert to blk-mq
Convert from the old request_fn style driver to blk-mq. Cc: David Miller <davem@davemloft.net> Reviewed-by: Hannes Reinecke <hare@suse.com> Tested-by: Ming Lei <ming.lei@redhat.com> Reviewed-by: Omar Sandoval <osandov@fb.com> Signed-off-by: Jens Axboe <axboe@kernel.dk>
This commit is contained in:
Родитель
ea2c18e104
Коммит
fa182a1fa9
|
@ -6,7 +6,7 @@
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/blkdev.h>
|
#include <linux/blk-mq.h>
|
||||||
#include <linux/hdreg.h>
|
#include <linux/hdreg.h>
|
||||||
#include <linux/genhd.h>
|
#include <linux/genhd.h>
|
||||||
#include <linux/cdrom.h>
|
#include <linux/cdrom.h>
|
||||||
|
@ -66,9 +66,10 @@ struct vdc_port {
|
||||||
|
|
||||||
u64 max_xfer_size;
|
u64 max_xfer_size;
|
||||||
u32 vdisk_block_size;
|
u32 vdisk_block_size;
|
||||||
|
u32 drain;
|
||||||
|
|
||||||
u64 ldc_timeout;
|
u64 ldc_timeout;
|
||||||
struct timer_list ldc_reset_timer;
|
struct delayed_work ldc_reset_timer_work;
|
||||||
struct work_struct ldc_reset_work;
|
struct work_struct ldc_reset_work;
|
||||||
|
|
||||||
/* The server fills these in for us in the disk attribute
|
/* The server fills these in for us in the disk attribute
|
||||||
|
@ -80,12 +81,14 @@ struct vdc_port {
|
||||||
u8 vdisk_mtype;
|
u8 vdisk_mtype;
|
||||||
u32 vdisk_phys_blksz;
|
u32 vdisk_phys_blksz;
|
||||||
|
|
||||||
|
struct blk_mq_tag_set tag_set;
|
||||||
|
|
||||||
char disk_name[32];
|
char disk_name[32];
|
||||||
};
|
};
|
||||||
|
|
||||||
static void vdc_ldc_reset(struct vdc_port *port);
|
static void vdc_ldc_reset(struct vdc_port *port);
|
||||||
static void vdc_ldc_reset_work(struct work_struct *work);
|
static void vdc_ldc_reset_work(struct work_struct *work);
|
||||||
static void vdc_ldc_reset_timer(struct timer_list *t);
|
static void vdc_ldc_reset_timer_work(struct work_struct *work);
|
||||||
|
|
||||||
static inline struct vdc_port *to_vdc_port(struct vio_driver_state *vio)
|
static inline struct vdc_port *to_vdc_port(struct vio_driver_state *vio)
|
||||||
{
|
{
|
||||||
|
@ -175,11 +178,8 @@ static void vdc_blk_queue_start(struct vdc_port *port)
|
||||||
* handshake completes, so check for initial handshake before we've
|
* handshake completes, so check for initial handshake before we've
|
||||||
* allocated a disk.
|
* allocated a disk.
|
||||||
*/
|
*/
|
||||||
if (port->disk && blk_queue_stopped(port->disk->queue) &&
|
if (port->disk && vdc_tx_dring_avail(dr) * 100 / VDC_TX_RING_SIZE >= 50)
|
||||||
vdc_tx_dring_avail(dr) * 100 / VDC_TX_RING_SIZE >= 50) {
|
blk_mq_start_hw_queues(port->disk->queue);
|
||||||
blk_start_queue(port->disk->queue);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void vdc_finish(struct vio_driver_state *vio, int err, int waiting_for)
|
static void vdc_finish(struct vio_driver_state *vio, int err, int waiting_for)
|
||||||
|
@ -197,7 +197,7 @@ static void vdc_handshake_complete(struct vio_driver_state *vio)
|
||||||
{
|
{
|
||||||
struct vdc_port *port = to_vdc_port(vio);
|
struct vdc_port *port = to_vdc_port(vio);
|
||||||
|
|
||||||
del_timer(&port->ldc_reset_timer);
|
cancel_delayed_work(&port->ldc_reset_timer_work);
|
||||||
vdc_finish(vio, 0, WAITING_FOR_LINK_UP);
|
vdc_finish(vio, 0, WAITING_FOR_LINK_UP);
|
||||||
vdc_blk_queue_start(port);
|
vdc_blk_queue_start(port);
|
||||||
}
|
}
|
||||||
|
@ -320,7 +320,7 @@ static void vdc_end_one(struct vdc_port *port, struct vio_dring_state *dr,
|
||||||
|
|
||||||
rqe->req = NULL;
|
rqe->req = NULL;
|
||||||
|
|
||||||
__blk_end_request(req, (desc->status ? BLK_STS_IOERR : 0), desc->size);
|
blk_mq_end_request(req, desc->status ? BLK_STS_IOERR : 0);
|
||||||
|
|
||||||
vdc_blk_queue_start(port);
|
vdc_blk_queue_start(port);
|
||||||
}
|
}
|
||||||
|
@ -525,29 +525,40 @@ static int __send_request(struct request *req)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void do_vdc_request(struct request_queue *rq)
|
static blk_status_t vdc_queue_rq(struct blk_mq_hw_ctx *hctx,
|
||||||
|
const struct blk_mq_queue_data *bd)
|
||||||
{
|
{
|
||||||
struct request *req;
|
struct vdc_port *port = hctx->queue->queuedata;
|
||||||
|
|
||||||
while ((req = blk_peek_request(rq)) != NULL) {
|
|
||||||
struct vdc_port *port;
|
|
||||||
struct vio_dring_state *dr;
|
struct vio_dring_state *dr;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
port = req->rq_disk->private_data;
|
|
||||||
dr = &port->vio.drings[VIO_DRIVER_TX_RING];
|
dr = &port->vio.drings[VIO_DRIVER_TX_RING];
|
||||||
if (unlikely(vdc_tx_dring_avail(dr) < 1))
|
|
||||||
goto wait;
|
|
||||||
|
|
||||||
blk_start_request(req);
|
blk_mq_start_request(bd->rq);
|
||||||
|
|
||||||
if (__send_request(req) < 0) {
|
spin_lock_irqsave(&port->vio.lock, flags);
|
||||||
blk_requeue_request(rq, req);
|
|
||||||
wait:
|
/*
|
||||||
/* Avoid pointless unplugs. */
|
* Doing drain, just end the request in error
|
||||||
blk_stop_queue(rq);
|
*/
|
||||||
break;
|
if (unlikely(port->drain)) {
|
||||||
|
spin_unlock_irqrestore(&port->vio.lock, flags);
|
||||||
|
return BLK_STS_IOERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (unlikely(vdc_tx_dring_avail(dr) < 1)) {
|
||||||
|
spin_unlock_irqrestore(&port->vio.lock, flags);
|
||||||
|
blk_mq_stop_hw_queue(hctx);
|
||||||
|
return BLK_STS_DEV_RESOURCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (__send_request(bd->rq) < 0) {
|
||||||
|
spin_unlock_irqrestore(&port->vio.lock, flags);
|
||||||
|
return BLK_STS_IOERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&port->vio.lock, flags);
|
||||||
|
return BLK_STS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int generic_request(struct vdc_port *port, u8 op, void *buf, int len)
|
static int generic_request(struct vdc_port *port, u8 op, void *buf, int len)
|
||||||
|
@ -759,6 +770,32 @@ static void vdc_port_down(struct vdc_port *port)
|
||||||
vio_ldc_free(&port->vio);
|
vio_ldc_free(&port->vio);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const struct blk_mq_ops vdc_mq_ops = {
|
||||||
|
.queue_rq = vdc_queue_rq,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void cleanup_queue(struct request_queue *q)
|
||||||
|
{
|
||||||
|
struct vdc_port *port = q->queuedata;
|
||||||
|
|
||||||
|
blk_cleanup_queue(q);
|
||||||
|
blk_mq_free_tag_set(&port->tag_set);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct request_queue *init_queue(struct vdc_port *port)
|
||||||
|
{
|
||||||
|
struct request_queue *q;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
q = blk_mq_init_sq_queue(&port->tag_set, &vdc_mq_ops, VDC_TX_RING_SIZE,
|
||||||
|
BLK_MQ_F_SHOULD_MERGE);
|
||||||
|
if (IS_ERR(q))
|
||||||
|
return q;
|
||||||
|
|
||||||
|
q->queuedata = port;
|
||||||
|
return q;
|
||||||
|
}
|
||||||
|
|
||||||
static int probe_disk(struct vdc_port *port)
|
static int probe_disk(struct vdc_port *port)
|
||||||
{
|
{
|
||||||
struct request_queue *q;
|
struct request_queue *q;
|
||||||
|
@ -796,17 +833,17 @@ static int probe_disk(struct vdc_port *port)
|
||||||
(u64)geom.num_sec);
|
(u64)geom.num_sec);
|
||||||
}
|
}
|
||||||
|
|
||||||
q = blk_init_queue(do_vdc_request, &port->vio.lock);
|
q = init_queue(port);
|
||||||
if (!q) {
|
if (IS_ERR(q)) {
|
||||||
printk(KERN_ERR PFX "%s: Could not allocate queue.\n",
|
printk(KERN_ERR PFX "%s: Could not allocate queue.\n",
|
||||||
port->vio.name);
|
port->vio.name);
|
||||||
return -ENOMEM;
|
return PTR_ERR(q);
|
||||||
}
|
}
|
||||||
g = alloc_disk(1 << PARTITION_SHIFT);
|
g = alloc_disk(1 << PARTITION_SHIFT);
|
||||||
if (!g) {
|
if (!g) {
|
||||||
printk(KERN_ERR PFX "%s: Could not allocate gendisk.\n",
|
printk(KERN_ERR PFX "%s: Could not allocate gendisk.\n",
|
||||||
port->vio.name);
|
port->vio.name);
|
||||||
blk_cleanup_queue(q);
|
cleanup_queue(q);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -981,7 +1018,7 @@ static int vdc_port_probe(struct vio_dev *vdev, const struct vio_device_id *id)
|
||||||
*/
|
*/
|
||||||
ldc_timeout = mdesc_get_property(hp, vdev->mp, "vdc-timeout", NULL);
|
ldc_timeout = mdesc_get_property(hp, vdev->mp, "vdc-timeout", NULL);
|
||||||
port->ldc_timeout = ldc_timeout ? *ldc_timeout : 0;
|
port->ldc_timeout = ldc_timeout ? *ldc_timeout : 0;
|
||||||
timer_setup(&port->ldc_reset_timer, vdc_ldc_reset_timer, 0);
|
INIT_DELAYED_WORK(&port->ldc_reset_timer_work, vdc_ldc_reset_timer_work);
|
||||||
INIT_WORK(&port->ldc_reset_work, vdc_ldc_reset_work);
|
INIT_WORK(&port->ldc_reset_work, vdc_ldc_reset_work);
|
||||||
|
|
||||||
err = vio_driver_init(&port->vio, vdev, VDEV_DISK,
|
err = vio_driver_init(&port->vio, vdev, VDEV_DISK,
|
||||||
|
@ -1034,18 +1071,14 @@ static int vdc_port_remove(struct vio_dev *vdev)
|
||||||
struct vdc_port *port = dev_get_drvdata(&vdev->dev);
|
struct vdc_port *port = dev_get_drvdata(&vdev->dev);
|
||||||
|
|
||||||
if (port) {
|
if (port) {
|
||||||
unsigned long flags;
|
blk_mq_stop_hw_queues(port->disk->queue);
|
||||||
|
|
||||||
spin_lock_irqsave(&port->vio.lock, flags);
|
|
||||||
blk_stop_queue(port->disk->queue);
|
|
||||||
spin_unlock_irqrestore(&port->vio.lock, flags);
|
|
||||||
|
|
||||||
flush_work(&port->ldc_reset_work);
|
flush_work(&port->ldc_reset_work);
|
||||||
del_timer_sync(&port->ldc_reset_timer);
|
cancel_delayed_work_sync(&port->ldc_reset_timer_work);
|
||||||
del_timer_sync(&port->vio.timer);
|
del_timer_sync(&port->vio.timer);
|
||||||
|
|
||||||
del_gendisk(port->disk);
|
del_gendisk(port->disk);
|
||||||
blk_cleanup_queue(port->disk->queue);
|
cleanup_queue(port->disk->queue);
|
||||||
put_disk(port->disk);
|
put_disk(port->disk);
|
||||||
port->disk = NULL;
|
port->disk = NULL;
|
||||||
|
|
||||||
|
@ -1080,32 +1113,46 @@ static void vdc_requeue_inflight(struct vdc_port *port)
|
||||||
}
|
}
|
||||||
|
|
||||||
rqe->req = NULL;
|
rqe->req = NULL;
|
||||||
blk_requeue_request(port->disk->queue, req);
|
blk_mq_requeue_request(req, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void vdc_queue_drain(struct vdc_port *port)
|
static void vdc_queue_drain(struct vdc_port *port)
|
||||||
{
|
{
|
||||||
struct request *req;
|
struct request_queue *q = port->disk->queue;
|
||||||
|
|
||||||
while ((req = blk_fetch_request(port->disk->queue)) != NULL)
|
/*
|
||||||
__blk_end_request_all(req, BLK_STS_IOERR);
|
* Mark the queue as draining, then freeze/quiesce to ensure
|
||||||
|
* that all existing requests are seen in ->queue_rq() and killed
|
||||||
|
*/
|
||||||
|
port->drain = 1;
|
||||||
|
spin_unlock_irq(&port->vio.lock);
|
||||||
|
|
||||||
|
blk_mq_freeze_queue(q);
|
||||||
|
blk_mq_quiesce_queue(q);
|
||||||
|
|
||||||
|
spin_lock_irq(&port->vio.lock);
|
||||||
|
port->drain = 0;
|
||||||
|
blk_mq_unquiesce_queue(q);
|
||||||
|
blk_mq_unfreeze_queue(q);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void vdc_ldc_reset_timer(struct timer_list *t)
|
static void vdc_ldc_reset_timer_work(struct work_struct *work)
|
||||||
{
|
{
|
||||||
struct vdc_port *port = from_timer(port, t, ldc_reset_timer);
|
struct vdc_port *port;
|
||||||
struct vio_driver_state *vio = &port->vio;
|
struct vio_driver_state *vio;
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
spin_lock_irqsave(&vio->lock, flags);
|
port = container_of(work, struct vdc_port, ldc_reset_timer_work.work);
|
||||||
|
vio = &port->vio;
|
||||||
|
|
||||||
|
spin_lock_irq(&vio->lock);
|
||||||
if (!(port->vio.hs_state & VIO_HS_COMPLETE)) {
|
if (!(port->vio.hs_state & VIO_HS_COMPLETE)) {
|
||||||
pr_warn(PFX "%s ldc down %llu seconds, draining queue\n",
|
pr_warn(PFX "%s ldc down %llu seconds, draining queue\n",
|
||||||
port->disk_name, port->ldc_timeout);
|
port->disk_name, port->ldc_timeout);
|
||||||
vdc_queue_drain(port);
|
vdc_queue_drain(port);
|
||||||
vdc_blk_queue_start(port);
|
vdc_blk_queue_start(port);
|
||||||
}
|
}
|
||||||
spin_unlock_irqrestore(&vio->lock, flags);
|
spin_unlock_irq(&vio->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void vdc_ldc_reset_work(struct work_struct *work)
|
static void vdc_ldc_reset_work(struct work_struct *work)
|
||||||
|
@ -1129,7 +1176,7 @@ static void vdc_ldc_reset(struct vdc_port *port)
|
||||||
assert_spin_locked(&port->vio.lock);
|
assert_spin_locked(&port->vio.lock);
|
||||||
|
|
||||||
pr_warn(PFX "%s ldc link reset\n", port->disk_name);
|
pr_warn(PFX "%s ldc link reset\n", port->disk_name);
|
||||||
blk_stop_queue(port->disk->queue);
|
blk_mq_stop_hw_queues(port->disk->queue);
|
||||||
vdc_requeue_inflight(port);
|
vdc_requeue_inflight(port);
|
||||||
vdc_port_down(port);
|
vdc_port_down(port);
|
||||||
|
|
||||||
|
@ -1146,7 +1193,7 @@ static void vdc_ldc_reset(struct vdc_port *port)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (port->ldc_timeout)
|
if (port->ldc_timeout)
|
||||||
mod_timer(&port->ldc_reset_timer,
|
mod_delayed_work(system_wq, &port->ldc_reset_timer_work,
|
||||||
round_jiffies(jiffies + HZ * port->ldc_timeout));
|
round_jiffies(jiffies + HZ * port->ldc_timeout));
|
||||||
mod_timer(&port->vio.timer, round_jiffies(jiffies + HZ));
|
mod_timer(&port->vio.timer, round_jiffies(jiffies + HZ));
|
||||||
return;
|
return;
|
||||||
|
|
Загрузка…
Ссылка в новой задаче