18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * virtio_pmem.c: Virtio pmem Driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Discovers persistent memory range information 68c2ecf20Sopenharmony_ci * from host and provides a virtio based flushing 78c2ecf20Sopenharmony_ci * interface. 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci#include "virtio_pmem.h" 108c2ecf20Sopenharmony_ci#include "nd.h" 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci /* The interrupt handler */ 138c2ecf20Sopenharmony_civoid virtio_pmem_host_ack(struct virtqueue *vq) 148c2ecf20Sopenharmony_ci{ 158c2ecf20Sopenharmony_ci struct virtio_pmem *vpmem = vq->vdev->priv; 168c2ecf20Sopenharmony_ci struct virtio_pmem_request *req_data, *req_buf; 178c2ecf20Sopenharmony_ci unsigned long flags; 188c2ecf20Sopenharmony_ci unsigned int len; 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci spin_lock_irqsave(&vpmem->pmem_lock, flags); 218c2ecf20Sopenharmony_ci while ((req_data = virtqueue_get_buf(vq, &len)) != NULL) { 228c2ecf20Sopenharmony_ci req_data->done = true; 238c2ecf20Sopenharmony_ci wake_up(&req_data->host_acked); 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci if (!list_empty(&vpmem->req_list)) { 268c2ecf20Sopenharmony_ci req_buf = list_first_entry(&vpmem->req_list, 278c2ecf20Sopenharmony_ci struct virtio_pmem_request, list); 288c2ecf20Sopenharmony_ci req_buf->wq_buf_avail = true; 298c2ecf20Sopenharmony_ci wake_up(&req_buf->wq_buf); 308c2ecf20Sopenharmony_ci list_del(&req_buf->list); 318c2ecf20Sopenharmony_ci } 328c2ecf20Sopenharmony_ci } 338c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vpmem->pmem_lock, flags); 348c2ecf20Sopenharmony_ci} 358c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(virtio_pmem_host_ack); 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci /* The request submission function */ 388c2ecf20Sopenharmony_cistatic int virtio_pmem_flush(struct nd_region *nd_region) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci struct virtio_device *vdev = nd_region->provider_data; 418c2ecf20Sopenharmony_ci struct virtio_pmem *vpmem = vdev->priv; 428c2ecf20Sopenharmony_ci struct virtio_pmem_request *req_data; 438c2ecf20Sopenharmony_ci struct scatterlist *sgs[2], sg, ret; 448c2ecf20Sopenharmony_ci unsigned long flags; 458c2ecf20Sopenharmony_ci int err, err1; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci might_sleep(); 488c2ecf20Sopenharmony_ci req_data = kmalloc(sizeof(*req_data), GFP_KERNEL); 498c2ecf20Sopenharmony_ci if (!req_data) 508c2ecf20Sopenharmony_ci return -ENOMEM; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci req_data->done = false; 538c2ecf20Sopenharmony_ci init_waitqueue_head(&req_data->host_acked); 548c2ecf20Sopenharmony_ci init_waitqueue_head(&req_data->wq_buf); 558c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&req_data->list); 568c2ecf20Sopenharmony_ci req_data->req.type = cpu_to_le32(VIRTIO_PMEM_REQ_TYPE_FLUSH); 578c2ecf20Sopenharmony_ci sg_init_one(&sg, &req_data->req, sizeof(req_data->req)); 588c2ecf20Sopenharmony_ci sgs[0] = &sg; 598c2ecf20Sopenharmony_ci sg_init_one(&ret, &req_data->resp.ret, sizeof(req_data->resp)); 608c2ecf20Sopenharmony_ci sgs[1] = &ret; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci spin_lock_irqsave(&vpmem->pmem_lock, flags); 638c2ecf20Sopenharmony_ci /* 648c2ecf20Sopenharmony_ci * If virtqueue_add_sgs returns -ENOSPC then req_vq virtual 658c2ecf20Sopenharmony_ci * queue does not have free descriptor. We add the request 668c2ecf20Sopenharmony_ci * to req_list and wait for host_ack to wake us up when free 678c2ecf20Sopenharmony_ci * slots are available. 688c2ecf20Sopenharmony_ci */ 698c2ecf20Sopenharmony_ci while ((err = virtqueue_add_sgs(vpmem->req_vq, sgs, 1, 1, req_data, 708c2ecf20Sopenharmony_ci GFP_ATOMIC)) == -ENOSPC) { 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci dev_info(&vdev->dev, "failed to send command to virtio pmem device, no free slots in the virtqueue\n"); 738c2ecf20Sopenharmony_ci req_data->wq_buf_avail = false; 748c2ecf20Sopenharmony_ci list_add_tail(&req_data->list, &vpmem->req_list); 758c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vpmem->pmem_lock, flags); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci /* A host response results in "host_ack" getting called */ 788c2ecf20Sopenharmony_ci wait_event(req_data->wq_buf, req_data->wq_buf_avail); 798c2ecf20Sopenharmony_ci spin_lock_irqsave(&vpmem->pmem_lock, flags); 808c2ecf20Sopenharmony_ci } 818c2ecf20Sopenharmony_ci err1 = virtqueue_kick(vpmem->req_vq); 828c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vpmem->pmem_lock, flags); 838c2ecf20Sopenharmony_ci /* 848c2ecf20Sopenharmony_ci * virtqueue_add_sgs failed with error different than -ENOSPC, we can't 858c2ecf20Sopenharmony_ci * do anything about that. 868c2ecf20Sopenharmony_ci */ 878c2ecf20Sopenharmony_ci if (err || !err1) { 888c2ecf20Sopenharmony_ci dev_info(&vdev->dev, "failed to send command to virtio pmem device\n"); 898c2ecf20Sopenharmony_ci err = -EIO; 908c2ecf20Sopenharmony_ci } else { 918c2ecf20Sopenharmony_ci /* A host repsonse results in "host_ack" getting called */ 928c2ecf20Sopenharmony_ci wait_event(req_data->host_acked, req_data->done); 938c2ecf20Sopenharmony_ci err = le32_to_cpu(req_data->resp.ret); 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci kfree(req_data); 978c2ecf20Sopenharmony_ci return err; 988c2ecf20Sopenharmony_ci}; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci/* The asynchronous flush callback function */ 1018c2ecf20Sopenharmony_ciint async_pmem_flush(struct nd_region *nd_region, struct bio *bio) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci /* 1048c2ecf20Sopenharmony_ci * Create child bio for asynchronous flush and chain with 1058c2ecf20Sopenharmony_ci * parent bio. Otherwise directly call nd_region flush. 1068c2ecf20Sopenharmony_ci */ 1078c2ecf20Sopenharmony_ci if (bio && bio->bi_iter.bi_sector != -1) { 1088c2ecf20Sopenharmony_ci struct bio *child = bio_alloc(GFP_ATOMIC, 0); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci if (!child) 1118c2ecf20Sopenharmony_ci return -ENOMEM; 1128c2ecf20Sopenharmony_ci bio_copy_dev(child, bio); 1138c2ecf20Sopenharmony_ci child->bi_opf = REQ_PREFLUSH; 1148c2ecf20Sopenharmony_ci child->bi_iter.bi_sector = -1; 1158c2ecf20Sopenharmony_ci bio_chain(child, bio); 1168c2ecf20Sopenharmony_ci submit_bio(child); 1178c2ecf20Sopenharmony_ci return 0; 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci if (virtio_pmem_flush(nd_region)) 1208c2ecf20Sopenharmony_ci return -EIO; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci return 0; 1238c2ecf20Sopenharmony_ci}; 1248c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(async_pmem_flush); 1258c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 126