Merge tag 'for-linus-5.15-rc1-tag' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6-microblaze.git] / drivers / block / xen-blkfront.c
index 715bfa8..7290210 100644 (file)
@@ -80,6 +80,7 @@ enum blkif_state {
        BLKIF_STATE_DISCONNECTED,
        BLKIF_STATE_CONNECTED,
        BLKIF_STATE_SUSPENDED,
+       BLKIF_STATE_ERROR,
 };
 
 struct grant {
@@ -89,6 +90,7 @@ struct grant {
 };
 
 enum blk_req_status {
+       REQ_PROCESSING,
        REQ_WAITING,
        REQ_DONE,
        REQ_ERROR,
@@ -530,10 +532,10 @@ static unsigned long blkif_ring_get_request(struct blkfront_ring_info *rinfo,
 
        id = get_id_from_freelist(rinfo);
        rinfo->shadow[id].request = req;
-       rinfo->shadow[id].status = REQ_WAITING;
+       rinfo->shadow[id].status = REQ_PROCESSING;
        rinfo->shadow[id].associated_id = NO_ASSOCIATED_ID;
 
-       (*ring_req)->u.rw.id = id;
+       rinfo->shadow[id].req.u.rw.id = id;
 
        return id;
 }
@@ -541,11 +543,12 @@ static unsigned long blkif_ring_get_request(struct blkfront_ring_info *rinfo,
 static int blkif_queue_discard_req(struct request *req, struct blkfront_ring_info *rinfo)
 {
        struct blkfront_info *info = rinfo->dev_info;
-       struct blkif_request *ring_req;
+       struct blkif_request *ring_req, *final_ring_req;
        unsigned long id;
 
        /* Fill out a communications ring structure. */
-       id = blkif_ring_get_request(rinfo, req, &ring_req);
+       id = blkif_ring_get_request(rinfo, req, &final_ring_req);
+       ring_req = &rinfo->shadow[id].req;
 
        ring_req->operation = BLKIF_OP_DISCARD;
        ring_req->u.discard.nr_sectors = blk_rq_sectors(req);
@@ -556,8 +559,9 @@ static int blkif_queue_discard_req(struct request *req, struct blkfront_ring_inf
        else
                ring_req->u.discard.flag = 0;
 
-       /* Keep a private copy so we can reissue requests when recovering. */
-       rinfo->shadow[id].req = *ring_req;
+       /* Copy the request to the ring page. */
+       *final_ring_req = *ring_req;
+       rinfo->shadow[id].status = REQ_WAITING;
 
        return 0;
 }
@@ -690,6 +694,7 @@ static int blkif_queue_rw_req(struct request *req, struct blkfront_ring_info *ri
 {
        struct blkfront_info *info = rinfo->dev_info;
        struct blkif_request *ring_req, *extra_ring_req = NULL;
+       struct blkif_request *final_ring_req, *final_extra_ring_req = NULL;
        unsigned long id, extra_id = NO_ASSOCIATED_ID;
        bool require_extra_req = false;
        int i;
@@ -734,7 +739,8 @@ static int blkif_queue_rw_req(struct request *req, struct blkfront_ring_info *ri
        }
 
        /* Fill out a communications ring structure. */
-       id = blkif_ring_get_request(rinfo, req, &ring_req);
+       id = blkif_ring_get_request(rinfo, req, &final_ring_req);
+       ring_req = &rinfo->shadow[id].req;
 
        num_sg = blk_rq_map_sg(req->q, req, rinfo->shadow[id].sg);
        num_grant = 0;
@@ -785,7 +791,9 @@ static int blkif_queue_rw_req(struct request *req, struct blkfront_ring_info *ri
                ring_req->u.rw.nr_segments = num_grant;
                if (unlikely(require_extra_req)) {
                        extra_id = blkif_ring_get_request(rinfo, req,
-                                                         &extra_ring_req);
+                                                         &final_extra_ring_req);
+                       extra_ring_req = &rinfo->shadow[extra_id].req;
+
                        /*
                         * Only the first request contains the scatter-gather
                         * list.
@@ -827,10 +835,13 @@ static int blkif_queue_rw_req(struct request *req, struct blkfront_ring_info *ri
        if (setup.segments)
                kunmap_atomic(setup.segments);
 
-       /* Keep a private copy so we can reissue requests when recovering. */
-       rinfo->shadow[id].req = *ring_req;
-       if (unlikely(require_extra_req))
-               rinfo->shadow[extra_id].req = *extra_ring_req;
+       /* Copy request(s) to the ring page. */
+       *final_ring_req = *ring_req;
+       rinfo->shadow[id].status = REQ_WAITING;
+       if (unlikely(require_extra_req)) {
+               *final_extra_ring_req = *extra_ring_req;
+               rinfo->shadow[extra_id].status = REQ_WAITING;
+       }
 
        if (new_persistent_gnts)
                gnttab_free_grant_references(setup.gref_head);
@@ -1353,8 +1364,8 @@ static enum blk_req_status blkif_rsp_to_req_status(int rsp)
 static int blkif_get_final_status(enum blk_req_status s1,
                                  enum blk_req_status s2)
 {
-       BUG_ON(s1 == REQ_WAITING);
-       BUG_ON(s2 == REQ_WAITING);
+       BUG_ON(s1 < REQ_DONE);
+       BUG_ON(s2 < REQ_DONE);
 
        if (s1 == REQ_ERROR || s2 == REQ_ERROR)
                return BLKIF_RSP_ERROR;
@@ -1387,7 +1398,7 @@ static bool blkif_completion(unsigned long *id,
                s->status = blkif_rsp_to_req_status(bret->status);
 
                /* Wait the second response if not yet here. */
-               if (s2->status == REQ_WAITING)
+               if (s2->status < REQ_DONE)
                        return false;
 
                bret->status = blkif_get_final_status(s->status,
@@ -1495,7 +1506,7 @@ static bool blkif_completion(unsigned long *id,
 static irqreturn_t blkif_interrupt(int irq, void *dev_id)
 {
        struct request *req;
-       struct blkif_response *bret;
+       struct blkif_response bret;
        RING_IDX i, rp;
        unsigned long flags;
        struct blkfront_ring_info *rinfo = (struct blkfront_ring_info *)dev_id;
@@ -1506,54 +1517,76 @@ static irqreturn_t blkif_interrupt(int irq, void *dev_id)
 
        spin_lock_irqsave(&rinfo->ring_lock, flags);
  again:
-       rp = rinfo->ring.sring->rsp_prod;
-       rmb(); /* Ensure we see queued responses up to 'rp'. */
+       rp = READ_ONCE(rinfo->ring.sring->rsp_prod);
+       virt_rmb(); /* Ensure we see queued responses up to 'rp'. */
+       if (RING_RESPONSE_PROD_OVERFLOW(&rinfo->ring, rp)) {
+               pr_alert("%s: illegal number of responses %u\n",
+                        info->gd->disk_name, rp - rinfo->ring.rsp_cons);
+               goto err;
+       }
 
        for (i = rinfo->ring.rsp_cons; i != rp; i++) {
                unsigned long id;
+               unsigned int op;
+
+               RING_COPY_RESPONSE(&rinfo->ring, i, &bret);
+               id = bret.id;
 
-               bret = RING_GET_RESPONSE(&rinfo->ring, i);
-               id   = bret->id;
                /*
                 * The backend has messed up and given us an id that we would
                 * never have given to it (we stamp it up to BLK_RING_SIZE -
                 * look in get_id_from_freelist.
                 */
                if (id >= BLK_RING_SIZE(info)) {
-                       WARN(1, "%s: response to %s has incorrect id (%ld)\n",
-                            info->gd->disk_name, op_name(bret->operation), id);
-                       /* We can't safely get the 'struct request' as
-                        * the id is busted. */
-                       continue;
+                       pr_alert("%s: response has incorrect id (%ld)\n",
+                                info->gd->disk_name, id);
+                       goto err;
                }
+               if (rinfo->shadow[id].status != REQ_WAITING) {
+                       pr_alert("%s: response references no pending request\n",
+                                info->gd->disk_name);
+                       goto err;
+               }
+
+               rinfo->shadow[id].status = REQ_PROCESSING;
                req  = rinfo->shadow[id].request;
 
-               if (bret->operation != BLKIF_OP_DISCARD) {
+               op = rinfo->shadow[id].req.operation;
+               if (op == BLKIF_OP_INDIRECT)
+                       op = rinfo->shadow[id].req.u.indirect.indirect_op;
+               if (bret.operation != op) {
+                       pr_alert("%s: response has wrong operation (%u instead of %u)\n",
+                                info->gd->disk_name, bret.operation, op);
+                       goto err;
+               }
+
+               if (bret.operation != BLKIF_OP_DISCARD) {
                        /*
                         * We may need to wait for an extra response if the
                         * I/O request is split in 2
                         */
-                       if (!blkif_completion(&id, rinfo, bret))
+                       if (!blkif_completion(&id, rinfo, &bret))
                                continue;
                }
 
                if (add_id_to_freelist(rinfo, id)) {
                        WARN(1, "%s: response to %s (id %ld) couldn't be recycled!\n",
-                            info->gd->disk_name, op_name(bret->operation), id);
+                            info->gd->disk_name, op_name(bret.operation), id);
                        continue;
                }
 
-               if (bret->status == BLKIF_RSP_OKAY)
+               if (bret.status == BLKIF_RSP_OKAY)
                        blkif_req(req)->error = BLK_STS_OK;
                else
                        blkif_req(req)->error = BLK_STS_IOERR;
 
-               switch (bret->operation) {
+               switch (bret.operation) {
                case BLKIF_OP_DISCARD:
-                       if (unlikely(bret->status == BLKIF_RSP_EOPNOTSUPP)) {
+                       if (unlikely(bret.status == BLKIF_RSP_EOPNOTSUPP)) {
                                struct request_queue *rq = info->rq;
-                               printk(KERN_WARNING "blkfront: %s: %s op failed\n",
-                                          info->gd->disk_name, op_name(bret->operation));
+
+                               pr_warn_ratelimited("blkfront: %s: %s op failed\n",
+                                          info->gd->disk_name, op_name(bret.operation));
                                blkif_req(req)->error = BLK_STS_NOTSUPP;
                                info->feature_discard = 0;
                                info->feature_secdiscard = 0;
@@ -1563,15 +1596,15 @@ static irqreturn_t blkif_interrupt(int irq, void *dev_id)
                        break;
                case BLKIF_OP_FLUSH_DISKCACHE:
                case BLKIF_OP_WRITE_BARRIER:
-                       if (unlikely(bret->status == BLKIF_RSP_EOPNOTSUPP)) {
-                               printk(KERN_WARNING "blkfront: %s: %s op failed\n",
-                                      info->gd->disk_name, op_name(bret->operation));
+                       if (unlikely(bret.status == BLKIF_RSP_EOPNOTSUPP)) {
+                               pr_warn_ratelimited("blkfront: %s: %s op failed\n",
+                                      info->gd->disk_name, op_name(bret.operation));
                                blkif_req(req)->error = BLK_STS_NOTSUPP;
                        }
-                       if (unlikely(bret->status == BLKIF_RSP_ERROR &&
+                       if (unlikely(bret.status == BLKIF_RSP_ERROR &&
                                     rinfo->shadow[id].req.u.rw.nr_segments == 0)) {
-                               printk(KERN_WARNING "blkfront: %s: empty %s op failed\n",
-                                      info->gd->disk_name, op_name(bret->operation));
+                               pr_warn_ratelimited("blkfront: %s: empty %s op failed\n",
+                                      info->gd->disk_name, op_name(bret.operation));
                                blkif_req(req)->error = BLK_STS_NOTSUPP;
                        }
                        if (unlikely(blkif_req(req)->error)) {
@@ -1584,9 +1617,10 @@ static irqreturn_t blkif_interrupt(int irq, void *dev_id)
                        fallthrough;
                case BLKIF_OP_READ:
                case BLKIF_OP_WRITE:
-                       if (unlikely(bret->status != BLKIF_RSP_OKAY))
-                               dev_dbg(&info->xbdev->dev, "Bad return from blkdev data "
-                                       "request: %x\n", bret->status);
+                       if (unlikely(bret.status != BLKIF_RSP_OKAY))
+                               dev_dbg_ratelimited(&info->xbdev->dev,
+                                       "Bad return from blkdev data request: %#x\n",
+                                       bret.status);
 
                        break;
                default:
@@ -1612,6 +1646,14 @@ static irqreturn_t blkif_interrupt(int irq, void *dev_id)
        spin_unlock_irqrestore(&rinfo->ring_lock, flags);
 
        return IRQ_HANDLED;
+
+ err:
+       info->connected = BLKIF_STATE_ERROR;
+
+       spin_unlock_irqrestore(&rinfo->ring_lock, flags);
+
+       pr_alert("%s disabled for further use\n", info->gd->disk_name);
+       return IRQ_HANDLED;
 }