xref: /openbmc/linux/drivers/nvdimm/nd_virtio.c (revision 7b7fd0ac7dc1ffcaf24d9bca0f051b0168e43cd4)
1  // SPDX-License-Identifier: GPL-2.0
2  /*
3   * virtio_pmem.c: Virtio pmem Driver
4   *
5   * Discovers persistent memory range information
6   * from host and provides a virtio based flushing
7   * interface.
8   */
9  #include "virtio_pmem.h"
10  #include "nd.h"
11  
12   /* The interrupt handler */
virtio_pmem_host_ack(struct virtqueue * vq)13  void virtio_pmem_host_ack(struct virtqueue *vq)
14  {
15  	struct virtio_pmem *vpmem = vq->vdev->priv;
16  	struct virtio_pmem_request *req_data, *req_buf;
17  	unsigned long flags;
18  	unsigned int len;
19  
20  	spin_lock_irqsave(&vpmem->pmem_lock, flags);
21  	while ((req_data = virtqueue_get_buf(vq, &len)) != NULL) {
22  		req_data->done = true;
23  		wake_up(&req_data->host_acked);
24  
25  		if (!list_empty(&vpmem->req_list)) {
26  			req_buf = list_first_entry(&vpmem->req_list,
27  					struct virtio_pmem_request, list);
28  			req_buf->wq_buf_avail = true;
29  			wake_up(&req_buf->wq_buf);
30  			list_del(&req_buf->list);
31  		}
32  	}
33  	spin_unlock_irqrestore(&vpmem->pmem_lock, flags);
34  }
35  EXPORT_SYMBOL_GPL(virtio_pmem_host_ack);
36  
37   /* The request submission function */
virtio_pmem_flush(struct nd_region * nd_region)38  static int virtio_pmem_flush(struct nd_region *nd_region)
39  {
40  	struct virtio_device *vdev = nd_region->provider_data;
41  	struct virtio_pmem *vpmem  = vdev->priv;
42  	struct virtio_pmem_request *req_data;
43  	struct scatterlist *sgs[2], sg, ret;
44  	unsigned long flags;
45  	int err, err1;
46  
47  	/*
48  	 * Don't bother to submit the request to the device if the device is
49  	 * not activated.
50  	 */
51  	if (vdev->config->get_status(vdev) & VIRTIO_CONFIG_S_NEEDS_RESET) {
52  		dev_info(&vdev->dev, "virtio pmem device needs a reset\n");
53  		return -EIO;
54  	}
55  
56  	might_sleep();
57  	req_data = kmalloc(sizeof(*req_data), GFP_KERNEL);
58  	if (!req_data)
59  		return -ENOMEM;
60  
61  	req_data->done = false;
62  	init_waitqueue_head(&req_data->host_acked);
63  	init_waitqueue_head(&req_data->wq_buf);
64  	INIT_LIST_HEAD(&req_data->list);
65  	req_data->req.type = cpu_to_le32(VIRTIO_PMEM_REQ_TYPE_FLUSH);
66  	sg_init_one(&sg, &req_data->req, sizeof(req_data->req));
67  	sgs[0] = &sg;
68  	sg_init_one(&ret, &req_data->resp.ret, sizeof(req_data->resp));
69  	sgs[1] = &ret;
70  
71  	spin_lock_irqsave(&vpmem->pmem_lock, flags);
72  	 /*
73  	  * If virtqueue_add_sgs returns -ENOSPC then req_vq virtual
74  	  * queue does not have free descriptor. We add the request
75  	  * to req_list and wait for host_ack to wake us up when free
76  	  * slots are available.
77  	  */
78  	while ((err = virtqueue_add_sgs(vpmem->req_vq, sgs, 1, 1, req_data,
79  					GFP_ATOMIC)) == -ENOSPC) {
80  
81  		dev_info(&vdev->dev, "failed to send command to virtio pmem device, no free slots in the virtqueue\n");
82  		req_data->wq_buf_avail = false;
83  		list_add_tail(&req_data->list, &vpmem->req_list);
84  		spin_unlock_irqrestore(&vpmem->pmem_lock, flags);
85  
86  		/* A host response results in "host_ack" getting called */
87  		wait_event(req_data->wq_buf, req_data->wq_buf_avail);
88  		spin_lock_irqsave(&vpmem->pmem_lock, flags);
89  	}
90  	err1 = virtqueue_kick(vpmem->req_vq);
91  	spin_unlock_irqrestore(&vpmem->pmem_lock, flags);
92  	/*
93  	 * virtqueue_add_sgs failed with error different than -ENOSPC, we can't
94  	 * do anything about that.
95  	 */
96  	if (err || !err1) {
97  		dev_info(&vdev->dev, "failed to send command to virtio pmem device\n");
98  		err = -EIO;
99  	} else {
100  		/* A host repsonse results in "host_ack" getting called */
101  		wait_event(req_data->host_acked, req_data->done);
102  		err = le32_to_cpu(req_data->resp.ret);
103  	}
104  
105  	kfree(req_data);
106  	return err;
107  };
108  
109  /* The asynchronous flush callback function */
async_pmem_flush(struct nd_region * nd_region,struct bio * bio)110  int async_pmem_flush(struct nd_region *nd_region, struct bio *bio)
111  {
112  	/*
113  	 * Create child bio for asynchronous flush and chain with
114  	 * parent bio. Otherwise directly call nd_region flush.
115  	 */
116  	if (bio && bio->bi_iter.bi_sector != -1) {
117  		struct bio *child = bio_alloc(bio->bi_bdev, 0,
118  					      REQ_OP_WRITE | REQ_PREFLUSH,
119  					      GFP_ATOMIC);
120  
121  		if (!child)
122  			return -ENOMEM;
123  		bio_clone_blkg_association(child, bio);
124  		child->bi_iter.bi_sector = -1;
125  		bio_chain(child, bio);
126  		submit_bio(child);
127  		return 0;
128  	}
129  	if (virtio_pmem_flush(nd_region))
130  		return -EIO;
131  
132  	return 0;
133  };
134  EXPORT_SYMBOL_GPL(async_pmem_flush);
135  MODULE_LICENSE("GPL");
136