18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* sunvdc.c: Sun LDOM Virtual Disk Client. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2007, 2008 David S. Miller <davem@davemloft.net> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/module.h> 88c2ecf20Sopenharmony_ci#include <linux/kernel.h> 98c2ecf20Sopenharmony_ci#include <linux/types.h> 108c2ecf20Sopenharmony_ci#include <linux/blk-mq.h> 118c2ecf20Sopenharmony_ci#include <linux/hdreg.h> 128c2ecf20Sopenharmony_ci#include <linux/genhd.h> 138c2ecf20Sopenharmony_ci#include <linux/cdrom.h> 148c2ecf20Sopenharmony_ci#include <linux/slab.h> 158c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 168c2ecf20Sopenharmony_ci#include <linux/completion.h> 178c2ecf20Sopenharmony_ci#include <linux/delay.h> 188c2ecf20Sopenharmony_ci#include <linux/init.h> 198c2ecf20Sopenharmony_ci#include <linux/list.h> 208c2ecf20Sopenharmony_ci#include <linux/scatterlist.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include <asm/vio.h> 238c2ecf20Sopenharmony_ci#include <asm/ldc.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define DRV_MODULE_NAME "sunvdc" 268c2ecf20Sopenharmony_ci#define PFX DRV_MODULE_NAME ": " 278c2ecf20Sopenharmony_ci#define DRV_MODULE_VERSION "1.2" 288c2ecf20Sopenharmony_ci#define DRV_MODULE_RELDATE "November 24, 2014" 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic char version[] = 318c2ecf20Sopenharmony_ci DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n"; 328c2ecf20Sopenharmony_ciMODULE_AUTHOR("David S. Miller (davem@davemloft.net)"); 338c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Sun LDOM virtual disk client driver"); 348c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 358c2ecf20Sopenharmony_ciMODULE_VERSION(DRV_MODULE_VERSION); 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#define VDC_TX_RING_SIZE 512 388c2ecf20Sopenharmony_ci#define VDC_DEFAULT_BLK_SIZE 512 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#define MAX_XFER_BLKS (128 * 1024) 418c2ecf20Sopenharmony_ci#define MAX_XFER_SIZE (MAX_XFER_BLKS / VDC_DEFAULT_BLK_SIZE) 428c2ecf20Sopenharmony_ci#define MAX_RING_COOKIES ((MAX_XFER_BLKS / PAGE_SIZE) + 2) 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#define WAITING_FOR_LINK_UP 0x01 458c2ecf20Sopenharmony_ci#define WAITING_FOR_TX_SPACE 0x02 468c2ecf20Sopenharmony_ci#define WAITING_FOR_GEN_CMD 0x04 478c2ecf20Sopenharmony_ci#define WAITING_FOR_ANY -1 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci#define VDC_MAX_RETRIES 10 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic struct workqueue_struct *sunvdc_wq; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistruct vdc_req_entry { 548c2ecf20Sopenharmony_ci struct request *req; 558c2ecf20Sopenharmony_ci}; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistruct vdc_port { 588c2ecf20Sopenharmony_ci struct vio_driver_state vio; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci struct gendisk *disk; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci struct vdc_completion *cmp; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci u64 req_id; 658c2ecf20Sopenharmony_ci u64 seq; 668c2ecf20Sopenharmony_ci struct vdc_req_entry rq_arr[VDC_TX_RING_SIZE]; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci unsigned long ring_cookies; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci u64 max_xfer_size; 718c2ecf20Sopenharmony_ci u32 vdisk_block_size; 728c2ecf20Sopenharmony_ci u32 drain; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci u64 ldc_timeout; 758c2ecf20Sopenharmony_ci struct delayed_work ldc_reset_timer_work; 768c2ecf20Sopenharmony_ci struct work_struct ldc_reset_work; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci /* The server fills these in for us in the disk attribute 798c2ecf20Sopenharmony_ci * ACK packet. 808c2ecf20Sopenharmony_ci */ 818c2ecf20Sopenharmony_ci u64 operations; 828c2ecf20Sopenharmony_ci u32 vdisk_size; 838c2ecf20Sopenharmony_ci u8 vdisk_type; 848c2ecf20Sopenharmony_ci u8 vdisk_mtype; 858c2ecf20Sopenharmony_ci u32 vdisk_phys_blksz; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci struct blk_mq_tag_set tag_set; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci char disk_name[32]; 908c2ecf20Sopenharmony_ci}; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic void vdc_ldc_reset(struct vdc_port *port); 938c2ecf20Sopenharmony_cistatic void vdc_ldc_reset_work(struct work_struct *work); 948c2ecf20Sopenharmony_cistatic void vdc_ldc_reset_timer_work(struct work_struct *work); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic inline struct vdc_port *to_vdc_port(struct vio_driver_state *vio) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci return container_of(vio, struct vdc_port, vio); 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci/* Ordered from largest major to lowest */ 1028c2ecf20Sopenharmony_cistatic struct vio_version vdc_versions[] = { 1038c2ecf20Sopenharmony_ci { .major = 1, .minor = 2 }, 1048c2ecf20Sopenharmony_ci { .major = 1, .minor = 1 }, 1058c2ecf20Sopenharmony_ci { .major = 1, .minor = 0 }, 1068c2ecf20Sopenharmony_ci}; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic inline int vdc_version_supported(struct vdc_port *port, 1098c2ecf20Sopenharmony_ci u16 major, u16 minor) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci return port->vio.ver.major == major && port->vio.ver.minor >= minor; 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci#define VDCBLK_NAME "vdisk" 1158c2ecf20Sopenharmony_cistatic int vdc_major; 1168c2ecf20Sopenharmony_ci#define PARTITION_SHIFT 3 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic inline u32 vdc_tx_dring_avail(struct vio_dring_state *dr) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci return vio_dring_avail(dr, VDC_TX_RING_SIZE); 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic int vdc_getgeo(struct block_device *bdev, struct hd_geometry *geo) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci struct gendisk *disk = bdev->bd_disk; 1268c2ecf20Sopenharmony_ci sector_t nsect = get_capacity(disk); 1278c2ecf20Sopenharmony_ci sector_t cylinders = nsect; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci geo->heads = 0xff; 1308c2ecf20Sopenharmony_ci geo->sectors = 0x3f; 1318c2ecf20Sopenharmony_ci sector_div(cylinders, geo->heads * geo->sectors); 1328c2ecf20Sopenharmony_ci geo->cylinders = cylinders; 1338c2ecf20Sopenharmony_ci if ((sector_t)(geo->cylinders + 1) * geo->heads * geo->sectors < nsect) 1348c2ecf20Sopenharmony_ci geo->cylinders = 0xffff; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci return 0; 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci/* Add ioctl/CDROM_GET_CAPABILITY to support cdrom_id in udev 1408c2ecf20Sopenharmony_ci * when vdisk_mtype is VD_MEDIA_TYPE_CD or VD_MEDIA_TYPE_DVD. 1418c2ecf20Sopenharmony_ci * Needed to be able to install inside an ldom from an iso image. 1428c2ecf20Sopenharmony_ci */ 1438c2ecf20Sopenharmony_cistatic int vdc_ioctl(struct block_device *bdev, fmode_t mode, 1448c2ecf20Sopenharmony_ci unsigned command, unsigned long argument) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci int i; 1478c2ecf20Sopenharmony_ci struct gendisk *disk; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci switch (command) { 1508c2ecf20Sopenharmony_ci case CDROMMULTISESSION: 1518c2ecf20Sopenharmony_ci pr_debug(PFX "Multisession CDs not supported\n"); 1528c2ecf20Sopenharmony_ci for (i = 0; i < sizeof(struct cdrom_multisession); i++) 1538c2ecf20Sopenharmony_ci if (put_user(0, (char __user *)(argument + i))) 1548c2ecf20Sopenharmony_ci return -EFAULT; 1558c2ecf20Sopenharmony_ci return 0; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci case CDROM_GET_CAPABILITY: 1588c2ecf20Sopenharmony_ci disk = bdev->bd_disk; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci if (bdev->bd_disk && (disk->flags & GENHD_FL_CD)) 1618c2ecf20Sopenharmony_ci return 0; 1628c2ecf20Sopenharmony_ci return -EINVAL; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci default: 1658c2ecf20Sopenharmony_ci pr_debug(PFX "ioctl %08x not supported\n", command); 1668c2ecf20Sopenharmony_ci return -EINVAL; 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic const struct block_device_operations vdc_fops = { 1718c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1728c2ecf20Sopenharmony_ci .getgeo = vdc_getgeo, 1738c2ecf20Sopenharmony_ci .ioctl = vdc_ioctl, 1748c2ecf20Sopenharmony_ci .compat_ioctl = blkdev_compat_ptr_ioctl, 1758c2ecf20Sopenharmony_ci}; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic void vdc_blk_queue_start(struct vdc_port *port) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci struct vio_dring_state *dr = &port->vio.drings[VIO_DRIVER_TX_RING]; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci /* restart blk queue when ring is half emptied. also called after 1828c2ecf20Sopenharmony_ci * handshake completes, so check for initial handshake before we've 1838c2ecf20Sopenharmony_ci * allocated a disk. 1848c2ecf20Sopenharmony_ci */ 1858c2ecf20Sopenharmony_ci if (port->disk && vdc_tx_dring_avail(dr) * 100 / VDC_TX_RING_SIZE >= 50) 1868c2ecf20Sopenharmony_ci blk_mq_start_stopped_hw_queues(port->disk->queue, true); 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic void vdc_finish(struct vio_driver_state *vio, int err, int waiting_for) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci if (vio->cmp && 1928c2ecf20Sopenharmony_ci (waiting_for == -1 || 1938c2ecf20Sopenharmony_ci vio->cmp->waiting_for == waiting_for)) { 1948c2ecf20Sopenharmony_ci vio->cmp->err = err; 1958c2ecf20Sopenharmony_ci complete(&vio->cmp->com); 1968c2ecf20Sopenharmony_ci vio->cmp = NULL; 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistatic void vdc_handshake_complete(struct vio_driver_state *vio) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci struct vdc_port *port = to_vdc_port(vio); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci cancel_delayed_work(&port->ldc_reset_timer_work); 2058c2ecf20Sopenharmony_ci vdc_finish(vio, 0, WAITING_FOR_LINK_UP); 2068c2ecf20Sopenharmony_ci vdc_blk_queue_start(port); 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistatic int vdc_handle_unknown(struct vdc_port *port, void *arg) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci struct vio_msg_tag *pkt = arg; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci printk(KERN_ERR PFX "Received unknown msg [%02x:%02x:%04x:%08x]\n", 2148c2ecf20Sopenharmony_ci pkt->type, pkt->stype, pkt->stype_env, pkt->sid); 2158c2ecf20Sopenharmony_ci printk(KERN_ERR PFX "Resetting connection.\n"); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci ldc_disconnect(port->vio.lp); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci return -ECONNRESET; 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cistatic int vdc_send_attr(struct vio_driver_state *vio) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci struct vdc_port *port = to_vdc_port(vio); 2258c2ecf20Sopenharmony_ci struct vio_disk_attr_info pkt; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci memset(&pkt, 0, sizeof(pkt)); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci pkt.tag.type = VIO_TYPE_CTRL; 2308c2ecf20Sopenharmony_ci pkt.tag.stype = VIO_SUBTYPE_INFO; 2318c2ecf20Sopenharmony_ci pkt.tag.stype_env = VIO_ATTR_INFO; 2328c2ecf20Sopenharmony_ci pkt.tag.sid = vio_send_sid(vio); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci pkt.xfer_mode = VIO_DRING_MODE; 2358c2ecf20Sopenharmony_ci pkt.vdisk_block_size = port->vdisk_block_size; 2368c2ecf20Sopenharmony_ci pkt.max_xfer_size = port->max_xfer_size; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci viodbg(HS, "SEND ATTR xfer_mode[0x%x] blksz[%u] max_xfer[%llu]\n", 2398c2ecf20Sopenharmony_ci pkt.xfer_mode, pkt.vdisk_block_size, pkt.max_xfer_size); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci return vio_ldc_send(&port->vio, &pkt, sizeof(pkt)); 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_cistatic int vdc_handle_attr(struct vio_driver_state *vio, void *arg) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci struct vdc_port *port = to_vdc_port(vio); 2478c2ecf20Sopenharmony_ci struct vio_disk_attr_info *pkt = arg; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci viodbg(HS, "GOT ATTR stype[0x%x] ops[%llx] disk_size[%llu] disk_type[%x] " 2508c2ecf20Sopenharmony_ci "mtype[0x%x] xfer_mode[0x%x] blksz[%u] max_xfer[%llu]\n", 2518c2ecf20Sopenharmony_ci pkt->tag.stype, pkt->operations, 2528c2ecf20Sopenharmony_ci pkt->vdisk_size, pkt->vdisk_type, pkt->vdisk_mtype, 2538c2ecf20Sopenharmony_ci pkt->xfer_mode, pkt->vdisk_block_size, 2548c2ecf20Sopenharmony_ci pkt->max_xfer_size); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci if (pkt->tag.stype == VIO_SUBTYPE_ACK) { 2578c2ecf20Sopenharmony_ci switch (pkt->vdisk_type) { 2588c2ecf20Sopenharmony_ci case VD_DISK_TYPE_DISK: 2598c2ecf20Sopenharmony_ci case VD_DISK_TYPE_SLICE: 2608c2ecf20Sopenharmony_ci break; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci default: 2638c2ecf20Sopenharmony_ci printk(KERN_ERR PFX "%s: Bogus vdisk_type 0x%x\n", 2648c2ecf20Sopenharmony_ci vio->name, pkt->vdisk_type); 2658c2ecf20Sopenharmony_ci return -ECONNRESET; 2668c2ecf20Sopenharmony_ci } 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci if (pkt->vdisk_block_size > port->vdisk_block_size) { 2698c2ecf20Sopenharmony_ci printk(KERN_ERR PFX "%s: BLOCK size increased " 2708c2ecf20Sopenharmony_ci "%u --> %u\n", 2718c2ecf20Sopenharmony_ci vio->name, 2728c2ecf20Sopenharmony_ci port->vdisk_block_size, pkt->vdisk_block_size); 2738c2ecf20Sopenharmony_ci return -ECONNRESET; 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci port->operations = pkt->operations; 2778c2ecf20Sopenharmony_ci port->vdisk_type = pkt->vdisk_type; 2788c2ecf20Sopenharmony_ci if (vdc_version_supported(port, 1, 1)) { 2798c2ecf20Sopenharmony_ci port->vdisk_size = pkt->vdisk_size; 2808c2ecf20Sopenharmony_ci port->vdisk_mtype = pkt->vdisk_mtype; 2818c2ecf20Sopenharmony_ci } 2828c2ecf20Sopenharmony_ci if (pkt->max_xfer_size < port->max_xfer_size) 2838c2ecf20Sopenharmony_ci port->max_xfer_size = pkt->max_xfer_size; 2848c2ecf20Sopenharmony_ci port->vdisk_block_size = pkt->vdisk_block_size; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci port->vdisk_phys_blksz = VDC_DEFAULT_BLK_SIZE; 2878c2ecf20Sopenharmony_ci if (vdc_version_supported(port, 1, 2)) 2888c2ecf20Sopenharmony_ci port->vdisk_phys_blksz = pkt->phys_block_size; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci return 0; 2918c2ecf20Sopenharmony_ci } else { 2928c2ecf20Sopenharmony_ci printk(KERN_ERR PFX "%s: Attribute NACK\n", vio->name); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci return -ECONNRESET; 2958c2ecf20Sopenharmony_ci } 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic void vdc_end_special(struct vdc_port *port, struct vio_disk_desc *desc) 2998c2ecf20Sopenharmony_ci{ 3008c2ecf20Sopenharmony_ci int err = desc->status; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci vdc_finish(&port->vio, -err, WAITING_FOR_GEN_CMD); 3038c2ecf20Sopenharmony_ci} 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_cistatic void vdc_end_one(struct vdc_port *port, struct vio_dring_state *dr, 3068c2ecf20Sopenharmony_ci unsigned int index) 3078c2ecf20Sopenharmony_ci{ 3088c2ecf20Sopenharmony_ci struct vio_disk_desc *desc = vio_dring_entry(dr, index); 3098c2ecf20Sopenharmony_ci struct vdc_req_entry *rqe = &port->rq_arr[index]; 3108c2ecf20Sopenharmony_ci struct request *req; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci if (unlikely(desc->hdr.state != VIO_DESC_DONE)) 3138c2ecf20Sopenharmony_ci return; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci ldc_unmap(port->vio.lp, desc->cookies, desc->ncookies); 3168c2ecf20Sopenharmony_ci desc->hdr.state = VIO_DESC_FREE; 3178c2ecf20Sopenharmony_ci dr->cons = vio_dring_next(dr, index); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci req = rqe->req; 3208c2ecf20Sopenharmony_ci if (req == NULL) { 3218c2ecf20Sopenharmony_ci vdc_end_special(port, desc); 3228c2ecf20Sopenharmony_ci return; 3238c2ecf20Sopenharmony_ci } 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci rqe->req = NULL; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci blk_mq_end_request(req, desc->status ? BLK_STS_IOERR : 0); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci vdc_blk_queue_start(port); 3308c2ecf20Sopenharmony_ci} 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_cistatic int vdc_ack(struct vdc_port *port, void *msgbuf) 3338c2ecf20Sopenharmony_ci{ 3348c2ecf20Sopenharmony_ci struct vio_dring_state *dr = &port->vio.drings[VIO_DRIVER_TX_RING]; 3358c2ecf20Sopenharmony_ci struct vio_dring_data *pkt = msgbuf; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci if (unlikely(pkt->dring_ident != dr->ident || 3388c2ecf20Sopenharmony_ci pkt->start_idx != pkt->end_idx || 3398c2ecf20Sopenharmony_ci pkt->start_idx >= VDC_TX_RING_SIZE)) 3408c2ecf20Sopenharmony_ci return 0; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci vdc_end_one(port, dr, pkt->start_idx); 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci return 0; 3458c2ecf20Sopenharmony_ci} 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_cistatic int vdc_nack(struct vdc_port *port, void *msgbuf) 3488c2ecf20Sopenharmony_ci{ 3498c2ecf20Sopenharmony_ci /* XXX Implement me XXX */ 3508c2ecf20Sopenharmony_ci return 0; 3518c2ecf20Sopenharmony_ci} 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_cistatic void vdc_event(void *arg, int event) 3548c2ecf20Sopenharmony_ci{ 3558c2ecf20Sopenharmony_ci struct vdc_port *port = arg; 3568c2ecf20Sopenharmony_ci struct vio_driver_state *vio = &port->vio; 3578c2ecf20Sopenharmony_ci unsigned long flags; 3588c2ecf20Sopenharmony_ci int err; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci spin_lock_irqsave(&vio->lock, flags); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci if (unlikely(event == LDC_EVENT_RESET)) { 3638c2ecf20Sopenharmony_ci vio_link_state_change(vio, event); 3648c2ecf20Sopenharmony_ci queue_work(sunvdc_wq, &port->ldc_reset_work); 3658c2ecf20Sopenharmony_ci goto out; 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci if (unlikely(event == LDC_EVENT_UP)) { 3698c2ecf20Sopenharmony_ci vio_link_state_change(vio, event); 3708c2ecf20Sopenharmony_ci goto out; 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci if (unlikely(event != LDC_EVENT_DATA_READY)) { 3748c2ecf20Sopenharmony_ci pr_warn(PFX "Unexpected LDC event %d\n", event); 3758c2ecf20Sopenharmony_ci goto out; 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci err = 0; 3798c2ecf20Sopenharmony_ci while (1) { 3808c2ecf20Sopenharmony_ci union { 3818c2ecf20Sopenharmony_ci struct vio_msg_tag tag; 3828c2ecf20Sopenharmony_ci u64 raw[8]; 3838c2ecf20Sopenharmony_ci } msgbuf; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci err = ldc_read(vio->lp, &msgbuf, sizeof(msgbuf)); 3868c2ecf20Sopenharmony_ci if (unlikely(err < 0)) { 3878c2ecf20Sopenharmony_ci if (err == -ECONNRESET) 3888c2ecf20Sopenharmony_ci vio_conn_reset(vio); 3898c2ecf20Sopenharmony_ci break; 3908c2ecf20Sopenharmony_ci } 3918c2ecf20Sopenharmony_ci if (err == 0) 3928c2ecf20Sopenharmony_ci break; 3938c2ecf20Sopenharmony_ci viodbg(DATA, "TAG [%02x:%02x:%04x:%08x]\n", 3948c2ecf20Sopenharmony_ci msgbuf.tag.type, 3958c2ecf20Sopenharmony_ci msgbuf.tag.stype, 3968c2ecf20Sopenharmony_ci msgbuf.tag.stype_env, 3978c2ecf20Sopenharmony_ci msgbuf.tag.sid); 3988c2ecf20Sopenharmony_ci err = vio_validate_sid(vio, &msgbuf.tag); 3998c2ecf20Sopenharmony_ci if (err < 0) 4008c2ecf20Sopenharmony_ci break; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci if (likely(msgbuf.tag.type == VIO_TYPE_DATA)) { 4038c2ecf20Sopenharmony_ci if (msgbuf.tag.stype == VIO_SUBTYPE_ACK) 4048c2ecf20Sopenharmony_ci err = vdc_ack(port, &msgbuf); 4058c2ecf20Sopenharmony_ci else if (msgbuf.tag.stype == VIO_SUBTYPE_NACK) 4068c2ecf20Sopenharmony_ci err = vdc_nack(port, &msgbuf); 4078c2ecf20Sopenharmony_ci else 4088c2ecf20Sopenharmony_ci err = vdc_handle_unknown(port, &msgbuf); 4098c2ecf20Sopenharmony_ci } else if (msgbuf.tag.type == VIO_TYPE_CTRL) { 4108c2ecf20Sopenharmony_ci err = vio_control_pkt_engine(vio, &msgbuf); 4118c2ecf20Sopenharmony_ci } else { 4128c2ecf20Sopenharmony_ci err = vdc_handle_unknown(port, &msgbuf); 4138c2ecf20Sopenharmony_ci } 4148c2ecf20Sopenharmony_ci if (err < 0) 4158c2ecf20Sopenharmony_ci break; 4168c2ecf20Sopenharmony_ci } 4178c2ecf20Sopenharmony_ci if (err < 0) 4188c2ecf20Sopenharmony_ci vdc_finish(&port->vio, err, WAITING_FOR_ANY); 4198c2ecf20Sopenharmony_ciout: 4208c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vio->lock, flags); 4218c2ecf20Sopenharmony_ci} 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_cistatic int __vdc_tx_trigger(struct vdc_port *port) 4248c2ecf20Sopenharmony_ci{ 4258c2ecf20Sopenharmony_ci struct vio_dring_state *dr = &port->vio.drings[VIO_DRIVER_TX_RING]; 4268c2ecf20Sopenharmony_ci struct vio_dring_data hdr = { 4278c2ecf20Sopenharmony_ci .tag = { 4288c2ecf20Sopenharmony_ci .type = VIO_TYPE_DATA, 4298c2ecf20Sopenharmony_ci .stype = VIO_SUBTYPE_INFO, 4308c2ecf20Sopenharmony_ci .stype_env = VIO_DRING_DATA, 4318c2ecf20Sopenharmony_ci .sid = vio_send_sid(&port->vio), 4328c2ecf20Sopenharmony_ci }, 4338c2ecf20Sopenharmony_ci .dring_ident = dr->ident, 4348c2ecf20Sopenharmony_ci .start_idx = dr->prod, 4358c2ecf20Sopenharmony_ci .end_idx = dr->prod, 4368c2ecf20Sopenharmony_ci }; 4378c2ecf20Sopenharmony_ci int err, delay; 4388c2ecf20Sopenharmony_ci int retries = 0; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci hdr.seq = dr->snd_nxt; 4418c2ecf20Sopenharmony_ci delay = 1; 4428c2ecf20Sopenharmony_ci do { 4438c2ecf20Sopenharmony_ci err = vio_ldc_send(&port->vio, &hdr, sizeof(hdr)); 4448c2ecf20Sopenharmony_ci if (err > 0) { 4458c2ecf20Sopenharmony_ci dr->snd_nxt++; 4468c2ecf20Sopenharmony_ci break; 4478c2ecf20Sopenharmony_ci } 4488c2ecf20Sopenharmony_ci udelay(delay); 4498c2ecf20Sopenharmony_ci if ((delay <<= 1) > 128) 4508c2ecf20Sopenharmony_ci delay = 128; 4518c2ecf20Sopenharmony_ci if (retries++ > VDC_MAX_RETRIES) 4528c2ecf20Sopenharmony_ci break; 4538c2ecf20Sopenharmony_ci } while (err == -EAGAIN); 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci if (err == -ENOTCONN) 4568c2ecf20Sopenharmony_ci vdc_ldc_reset(port); 4578c2ecf20Sopenharmony_ci return err; 4588c2ecf20Sopenharmony_ci} 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_cistatic int __send_request(struct request *req) 4618c2ecf20Sopenharmony_ci{ 4628c2ecf20Sopenharmony_ci struct vdc_port *port = req->rq_disk->private_data; 4638c2ecf20Sopenharmony_ci struct vio_dring_state *dr = &port->vio.drings[VIO_DRIVER_TX_RING]; 4648c2ecf20Sopenharmony_ci struct scatterlist sg[MAX_RING_COOKIES]; 4658c2ecf20Sopenharmony_ci struct vdc_req_entry *rqe; 4668c2ecf20Sopenharmony_ci struct vio_disk_desc *desc; 4678c2ecf20Sopenharmony_ci unsigned int map_perm; 4688c2ecf20Sopenharmony_ci int nsg, err, i; 4698c2ecf20Sopenharmony_ci u64 len; 4708c2ecf20Sopenharmony_ci u8 op; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci if (WARN_ON(port->ring_cookies > MAX_RING_COOKIES)) 4738c2ecf20Sopenharmony_ci return -EINVAL; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci map_perm = LDC_MAP_SHADOW | LDC_MAP_DIRECT | LDC_MAP_IO; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci if (rq_data_dir(req) == READ) { 4788c2ecf20Sopenharmony_ci map_perm |= LDC_MAP_W; 4798c2ecf20Sopenharmony_ci op = VD_OP_BREAD; 4808c2ecf20Sopenharmony_ci } else { 4818c2ecf20Sopenharmony_ci map_perm |= LDC_MAP_R; 4828c2ecf20Sopenharmony_ci op = VD_OP_BWRITE; 4838c2ecf20Sopenharmony_ci } 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci sg_init_table(sg, port->ring_cookies); 4868c2ecf20Sopenharmony_ci nsg = blk_rq_map_sg(req->q, req, sg); 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci len = 0; 4898c2ecf20Sopenharmony_ci for (i = 0; i < nsg; i++) 4908c2ecf20Sopenharmony_ci len += sg[i].length; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci desc = vio_dring_cur(dr); 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci err = ldc_map_sg(port->vio.lp, sg, nsg, 4958c2ecf20Sopenharmony_ci desc->cookies, port->ring_cookies, 4968c2ecf20Sopenharmony_ci map_perm); 4978c2ecf20Sopenharmony_ci if (err < 0) { 4988c2ecf20Sopenharmony_ci printk(KERN_ERR PFX "ldc_map_sg() failure, err=%d.\n", err); 4998c2ecf20Sopenharmony_ci return err; 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci rqe = &port->rq_arr[dr->prod]; 5038c2ecf20Sopenharmony_ci rqe->req = req; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci desc->hdr.ack = VIO_ACK_ENABLE; 5068c2ecf20Sopenharmony_ci desc->req_id = port->req_id; 5078c2ecf20Sopenharmony_ci desc->operation = op; 5088c2ecf20Sopenharmony_ci if (port->vdisk_type == VD_DISK_TYPE_DISK) { 5098c2ecf20Sopenharmony_ci desc->slice = 0xff; 5108c2ecf20Sopenharmony_ci } else { 5118c2ecf20Sopenharmony_ci desc->slice = 0; 5128c2ecf20Sopenharmony_ci } 5138c2ecf20Sopenharmony_ci desc->status = ~0; 5148c2ecf20Sopenharmony_ci desc->offset = (blk_rq_pos(req) << 9) / port->vdisk_block_size; 5158c2ecf20Sopenharmony_ci desc->size = len; 5168c2ecf20Sopenharmony_ci desc->ncookies = err; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci /* This has to be a non-SMP write barrier because we are writing 5198c2ecf20Sopenharmony_ci * to memory which is shared with the peer LDOM. 5208c2ecf20Sopenharmony_ci */ 5218c2ecf20Sopenharmony_ci wmb(); 5228c2ecf20Sopenharmony_ci desc->hdr.state = VIO_DESC_READY; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci err = __vdc_tx_trigger(port); 5258c2ecf20Sopenharmony_ci if (err < 0) { 5268c2ecf20Sopenharmony_ci printk(KERN_ERR PFX "vdc_tx_trigger() failure, err=%d\n", err); 5278c2ecf20Sopenharmony_ci } else { 5288c2ecf20Sopenharmony_ci port->req_id++; 5298c2ecf20Sopenharmony_ci dr->prod = vio_dring_next(dr, dr->prod); 5308c2ecf20Sopenharmony_ci } 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci return err; 5338c2ecf20Sopenharmony_ci} 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_cistatic blk_status_t vdc_queue_rq(struct blk_mq_hw_ctx *hctx, 5368c2ecf20Sopenharmony_ci const struct blk_mq_queue_data *bd) 5378c2ecf20Sopenharmony_ci{ 5388c2ecf20Sopenharmony_ci struct vdc_port *port = hctx->queue->queuedata; 5398c2ecf20Sopenharmony_ci struct vio_dring_state *dr; 5408c2ecf20Sopenharmony_ci unsigned long flags; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci dr = &port->vio.drings[VIO_DRIVER_TX_RING]; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci blk_mq_start_request(bd->rq); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci spin_lock_irqsave(&port->vio.lock, flags); 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci /* 5498c2ecf20Sopenharmony_ci * Doing drain, just end the request in error 5508c2ecf20Sopenharmony_ci */ 5518c2ecf20Sopenharmony_ci if (unlikely(port->drain)) { 5528c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&port->vio.lock, flags); 5538c2ecf20Sopenharmony_ci return BLK_STS_IOERR; 5548c2ecf20Sopenharmony_ci } 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci if (unlikely(vdc_tx_dring_avail(dr) < 1)) { 5578c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&port->vio.lock, flags); 5588c2ecf20Sopenharmony_ci blk_mq_stop_hw_queue(hctx); 5598c2ecf20Sopenharmony_ci return BLK_STS_DEV_RESOURCE; 5608c2ecf20Sopenharmony_ci } 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci if (__send_request(bd->rq) < 0) { 5638c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&port->vio.lock, flags); 5648c2ecf20Sopenharmony_ci return BLK_STS_IOERR; 5658c2ecf20Sopenharmony_ci } 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&port->vio.lock, flags); 5688c2ecf20Sopenharmony_ci return BLK_STS_OK; 5698c2ecf20Sopenharmony_ci} 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_cistatic int generic_request(struct vdc_port *port, u8 op, void *buf, int len) 5728c2ecf20Sopenharmony_ci{ 5738c2ecf20Sopenharmony_ci struct vio_dring_state *dr; 5748c2ecf20Sopenharmony_ci struct vio_completion comp; 5758c2ecf20Sopenharmony_ci struct vio_disk_desc *desc; 5768c2ecf20Sopenharmony_ci unsigned int map_perm; 5778c2ecf20Sopenharmony_ci unsigned long flags; 5788c2ecf20Sopenharmony_ci int op_len, err; 5798c2ecf20Sopenharmony_ci void *req_buf; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci if (!(((u64)1 << (u64)op) & port->operations)) 5828c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci switch (op) { 5858c2ecf20Sopenharmony_ci case VD_OP_BREAD: 5868c2ecf20Sopenharmony_ci case VD_OP_BWRITE: 5878c2ecf20Sopenharmony_ci default: 5888c2ecf20Sopenharmony_ci return -EINVAL; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci case VD_OP_FLUSH: 5918c2ecf20Sopenharmony_ci op_len = 0; 5928c2ecf20Sopenharmony_ci map_perm = 0; 5938c2ecf20Sopenharmony_ci break; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci case VD_OP_GET_WCE: 5968c2ecf20Sopenharmony_ci op_len = sizeof(u32); 5978c2ecf20Sopenharmony_ci map_perm = LDC_MAP_W; 5988c2ecf20Sopenharmony_ci break; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci case VD_OP_SET_WCE: 6018c2ecf20Sopenharmony_ci op_len = sizeof(u32); 6028c2ecf20Sopenharmony_ci map_perm = LDC_MAP_R; 6038c2ecf20Sopenharmony_ci break; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci case VD_OP_GET_VTOC: 6068c2ecf20Sopenharmony_ci op_len = sizeof(struct vio_disk_vtoc); 6078c2ecf20Sopenharmony_ci map_perm = LDC_MAP_W; 6088c2ecf20Sopenharmony_ci break; 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci case VD_OP_SET_VTOC: 6118c2ecf20Sopenharmony_ci op_len = sizeof(struct vio_disk_vtoc); 6128c2ecf20Sopenharmony_ci map_perm = LDC_MAP_R; 6138c2ecf20Sopenharmony_ci break; 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci case VD_OP_GET_DISKGEOM: 6168c2ecf20Sopenharmony_ci op_len = sizeof(struct vio_disk_geom); 6178c2ecf20Sopenharmony_ci map_perm = LDC_MAP_W; 6188c2ecf20Sopenharmony_ci break; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci case VD_OP_SET_DISKGEOM: 6218c2ecf20Sopenharmony_ci op_len = sizeof(struct vio_disk_geom); 6228c2ecf20Sopenharmony_ci map_perm = LDC_MAP_R; 6238c2ecf20Sopenharmony_ci break; 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci case VD_OP_SCSICMD: 6268c2ecf20Sopenharmony_ci op_len = 16; 6278c2ecf20Sopenharmony_ci map_perm = LDC_MAP_RW; 6288c2ecf20Sopenharmony_ci break; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci case VD_OP_GET_DEVID: 6318c2ecf20Sopenharmony_ci op_len = sizeof(struct vio_disk_devid); 6328c2ecf20Sopenharmony_ci map_perm = LDC_MAP_W; 6338c2ecf20Sopenharmony_ci break; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci case VD_OP_GET_EFI: 6368c2ecf20Sopenharmony_ci case VD_OP_SET_EFI: 6378c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 6388c2ecf20Sopenharmony_ci } 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci map_perm |= LDC_MAP_SHADOW | LDC_MAP_DIRECT | LDC_MAP_IO; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci op_len = (op_len + 7) & ~7; 6438c2ecf20Sopenharmony_ci req_buf = kzalloc(op_len, GFP_KERNEL); 6448c2ecf20Sopenharmony_ci if (!req_buf) 6458c2ecf20Sopenharmony_ci return -ENOMEM; 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci if (len > op_len) 6488c2ecf20Sopenharmony_ci len = op_len; 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci if (map_perm & LDC_MAP_R) 6518c2ecf20Sopenharmony_ci memcpy(req_buf, buf, len); 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci spin_lock_irqsave(&port->vio.lock, flags); 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci dr = &port->vio.drings[VIO_DRIVER_TX_RING]; 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci /* XXX If we want to use this code generically we have to 6588c2ecf20Sopenharmony_ci * XXX handle TX ring exhaustion etc. 6598c2ecf20Sopenharmony_ci */ 6608c2ecf20Sopenharmony_ci desc = vio_dring_cur(dr); 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci err = ldc_map_single(port->vio.lp, req_buf, op_len, 6638c2ecf20Sopenharmony_ci desc->cookies, port->ring_cookies, 6648c2ecf20Sopenharmony_ci map_perm); 6658c2ecf20Sopenharmony_ci if (err < 0) { 6668c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&port->vio.lock, flags); 6678c2ecf20Sopenharmony_ci kfree(req_buf); 6688c2ecf20Sopenharmony_ci return err; 6698c2ecf20Sopenharmony_ci } 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci init_completion(&comp.com); 6728c2ecf20Sopenharmony_ci comp.waiting_for = WAITING_FOR_GEN_CMD; 6738c2ecf20Sopenharmony_ci port->vio.cmp = ∁ 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci desc->hdr.ack = VIO_ACK_ENABLE; 6768c2ecf20Sopenharmony_ci desc->req_id = port->req_id; 6778c2ecf20Sopenharmony_ci desc->operation = op; 6788c2ecf20Sopenharmony_ci desc->slice = 0; 6798c2ecf20Sopenharmony_ci desc->status = ~0; 6808c2ecf20Sopenharmony_ci desc->offset = 0; 6818c2ecf20Sopenharmony_ci desc->size = op_len; 6828c2ecf20Sopenharmony_ci desc->ncookies = err; 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci /* This has to be a non-SMP write barrier because we are writing 6858c2ecf20Sopenharmony_ci * to memory which is shared with the peer LDOM. 6868c2ecf20Sopenharmony_ci */ 6878c2ecf20Sopenharmony_ci wmb(); 6888c2ecf20Sopenharmony_ci desc->hdr.state = VIO_DESC_READY; 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci err = __vdc_tx_trigger(port); 6918c2ecf20Sopenharmony_ci if (err >= 0) { 6928c2ecf20Sopenharmony_ci port->req_id++; 6938c2ecf20Sopenharmony_ci dr->prod = vio_dring_next(dr, dr->prod); 6948c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&port->vio.lock, flags); 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci wait_for_completion(&comp.com); 6978c2ecf20Sopenharmony_ci err = comp.err; 6988c2ecf20Sopenharmony_ci } else { 6998c2ecf20Sopenharmony_ci port->vio.cmp = NULL; 7008c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&port->vio.lock, flags); 7018c2ecf20Sopenharmony_ci } 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci if (map_perm & LDC_MAP_W) 7048c2ecf20Sopenharmony_ci memcpy(buf, req_buf, len); 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci kfree(req_buf); 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci return err; 7098c2ecf20Sopenharmony_ci} 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_cistatic int vdc_alloc_tx_ring(struct vdc_port *port) 7128c2ecf20Sopenharmony_ci{ 7138c2ecf20Sopenharmony_ci struct vio_dring_state *dr = &port->vio.drings[VIO_DRIVER_TX_RING]; 7148c2ecf20Sopenharmony_ci unsigned long len, entry_size; 7158c2ecf20Sopenharmony_ci int ncookies; 7168c2ecf20Sopenharmony_ci void *dring; 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci entry_size = sizeof(struct vio_disk_desc) + 7198c2ecf20Sopenharmony_ci (sizeof(struct ldc_trans_cookie) * port->ring_cookies); 7208c2ecf20Sopenharmony_ci len = (VDC_TX_RING_SIZE * entry_size); 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci ncookies = VIO_MAX_RING_COOKIES; 7238c2ecf20Sopenharmony_ci dring = ldc_alloc_exp_dring(port->vio.lp, len, 7248c2ecf20Sopenharmony_ci dr->cookies, &ncookies, 7258c2ecf20Sopenharmony_ci (LDC_MAP_SHADOW | 7268c2ecf20Sopenharmony_ci LDC_MAP_DIRECT | 7278c2ecf20Sopenharmony_ci LDC_MAP_RW)); 7288c2ecf20Sopenharmony_ci if (IS_ERR(dring)) 7298c2ecf20Sopenharmony_ci return PTR_ERR(dring); 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci dr->base = dring; 7328c2ecf20Sopenharmony_ci dr->entry_size = entry_size; 7338c2ecf20Sopenharmony_ci dr->num_entries = VDC_TX_RING_SIZE; 7348c2ecf20Sopenharmony_ci dr->prod = dr->cons = 0; 7358c2ecf20Sopenharmony_ci dr->pending = VDC_TX_RING_SIZE; 7368c2ecf20Sopenharmony_ci dr->ncookies = ncookies; 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci return 0; 7398c2ecf20Sopenharmony_ci} 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_cistatic void vdc_free_tx_ring(struct vdc_port *port) 7428c2ecf20Sopenharmony_ci{ 7438c2ecf20Sopenharmony_ci struct vio_dring_state *dr = &port->vio.drings[VIO_DRIVER_TX_RING]; 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci if (dr->base) { 7468c2ecf20Sopenharmony_ci ldc_free_exp_dring(port->vio.lp, dr->base, 7478c2ecf20Sopenharmony_ci (dr->entry_size * dr->num_entries), 7488c2ecf20Sopenharmony_ci dr->cookies, dr->ncookies); 7498c2ecf20Sopenharmony_ci dr->base = NULL; 7508c2ecf20Sopenharmony_ci dr->entry_size = 0; 7518c2ecf20Sopenharmony_ci dr->num_entries = 0; 7528c2ecf20Sopenharmony_ci dr->pending = 0; 7538c2ecf20Sopenharmony_ci dr->ncookies = 0; 7548c2ecf20Sopenharmony_ci } 7558c2ecf20Sopenharmony_ci} 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_cistatic int vdc_port_up(struct vdc_port *port) 7588c2ecf20Sopenharmony_ci{ 7598c2ecf20Sopenharmony_ci struct vio_completion comp; 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci init_completion(&comp.com); 7628c2ecf20Sopenharmony_ci comp.err = 0; 7638c2ecf20Sopenharmony_ci comp.waiting_for = WAITING_FOR_LINK_UP; 7648c2ecf20Sopenharmony_ci port->vio.cmp = ∁ 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci vio_port_up(&port->vio); 7678c2ecf20Sopenharmony_ci wait_for_completion(&comp.com); 7688c2ecf20Sopenharmony_ci return comp.err; 7698c2ecf20Sopenharmony_ci} 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_cistatic void vdc_port_down(struct vdc_port *port) 7728c2ecf20Sopenharmony_ci{ 7738c2ecf20Sopenharmony_ci ldc_disconnect(port->vio.lp); 7748c2ecf20Sopenharmony_ci ldc_unbind(port->vio.lp); 7758c2ecf20Sopenharmony_ci vdc_free_tx_ring(port); 7768c2ecf20Sopenharmony_ci vio_ldc_free(&port->vio); 7778c2ecf20Sopenharmony_ci} 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_cistatic const struct blk_mq_ops vdc_mq_ops = { 7808c2ecf20Sopenharmony_ci .queue_rq = vdc_queue_rq, 7818c2ecf20Sopenharmony_ci}; 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_cistatic void cleanup_queue(struct request_queue *q) 7848c2ecf20Sopenharmony_ci{ 7858c2ecf20Sopenharmony_ci struct vdc_port *port = q->queuedata; 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci blk_cleanup_queue(q); 7888c2ecf20Sopenharmony_ci blk_mq_free_tag_set(&port->tag_set); 7898c2ecf20Sopenharmony_ci} 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_cistatic struct request_queue *init_queue(struct vdc_port *port) 7928c2ecf20Sopenharmony_ci{ 7938c2ecf20Sopenharmony_ci struct request_queue *q; 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci q = blk_mq_init_sq_queue(&port->tag_set, &vdc_mq_ops, VDC_TX_RING_SIZE, 7968c2ecf20Sopenharmony_ci BLK_MQ_F_SHOULD_MERGE); 7978c2ecf20Sopenharmony_ci if (IS_ERR(q)) 7988c2ecf20Sopenharmony_ci return q; 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci q->queuedata = port; 8018c2ecf20Sopenharmony_ci return q; 8028c2ecf20Sopenharmony_ci} 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_cistatic int probe_disk(struct vdc_port *port) 8058c2ecf20Sopenharmony_ci{ 8068c2ecf20Sopenharmony_ci struct request_queue *q; 8078c2ecf20Sopenharmony_ci struct gendisk *g; 8088c2ecf20Sopenharmony_ci int err; 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci err = vdc_port_up(port); 8118c2ecf20Sopenharmony_ci if (err) 8128c2ecf20Sopenharmony_ci return err; 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci /* Using version 1.2 means vdisk_phys_blksz should be set unless the 8158c2ecf20Sopenharmony_ci * disk is reserved by another system. 8168c2ecf20Sopenharmony_ci */ 8178c2ecf20Sopenharmony_ci if (vdc_version_supported(port, 1, 2) && !port->vdisk_phys_blksz) 8188c2ecf20Sopenharmony_ci return -ENODEV; 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci if (vdc_version_supported(port, 1, 1)) { 8218c2ecf20Sopenharmony_ci /* vdisk_size should be set during the handshake, if it wasn't 8228c2ecf20Sopenharmony_ci * then the underlying disk is reserved by another system 8238c2ecf20Sopenharmony_ci */ 8248c2ecf20Sopenharmony_ci if (port->vdisk_size == -1) 8258c2ecf20Sopenharmony_ci return -ENODEV; 8268c2ecf20Sopenharmony_ci } else { 8278c2ecf20Sopenharmony_ci struct vio_disk_geom geom; 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci err = generic_request(port, VD_OP_GET_DISKGEOM, 8308c2ecf20Sopenharmony_ci &geom, sizeof(geom)); 8318c2ecf20Sopenharmony_ci if (err < 0) { 8328c2ecf20Sopenharmony_ci printk(KERN_ERR PFX "VD_OP_GET_DISKGEOM returns " 8338c2ecf20Sopenharmony_ci "error %d\n", err); 8348c2ecf20Sopenharmony_ci return err; 8358c2ecf20Sopenharmony_ci } 8368c2ecf20Sopenharmony_ci port->vdisk_size = ((u64)geom.num_cyl * 8378c2ecf20Sopenharmony_ci (u64)geom.num_hd * 8388c2ecf20Sopenharmony_ci (u64)geom.num_sec); 8398c2ecf20Sopenharmony_ci } 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci q = init_queue(port); 8428c2ecf20Sopenharmony_ci if (IS_ERR(q)) { 8438c2ecf20Sopenharmony_ci printk(KERN_ERR PFX "%s: Could not allocate queue.\n", 8448c2ecf20Sopenharmony_ci port->vio.name); 8458c2ecf20Sopenharmony_ci return PTR_ERR(q); 8468c2ecf20Sopenharmony_ci } 8478c2ecf20Sopenharmony_ci g = alloc_disk(1 << PARTITION_SHIFT); 8488c2ecf20Sopenharmony_ci if (!g) { 8498c2ecf20Sopenharmony_ci printk(KERN_ERR PFX "%s: Could not allocate gendisk.\n", 8508c2ecf20Sopenharmony_ci port->vio.name); 8518c2ecf20Sopenharmony_ci cleanup_queue(q); 8528c2ecf20Sopenharmony_ci return -ENOMEM; 8538c2ecf20Sopenharmony_ci } 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci port->disk = g; 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci /* Each segment in a request is up to an aligned page in size. */ 8588c2ecf20Sopenharmony_ci blk_queue_segment_boundary(q, PAGE_SIZE - 1); 8598c2ecf20Sopenharmony_ci blk_queue_max_segment_size(q, PAGE_SIZE); 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci blk_queue_max_segments(q, port->ring_cookies); 8628c2ecf20Sopenharmony_ci blk_queue_max_hw_sectors(q, port->max_xfer_size); 8638c2ecf20Sopenharmony_ci g->major = vdc_major; 8648c2ecf20Sopenharmony_ci g->first_minor = port->vio.vdev->dev_no << PARTITION_SHIFT; 8658c2ecf20Sopenharmony_ci strcpy(g->disk_name, port->disk_name); 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci g->fops = &vdc_fops; 8688c2ecf20Sopenharmony_ci g->queue = q; 8698c2ecf20Sopenharmony_ci g->private_data = port; 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci set_capacity(g, port->vdisk_size); 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci if (vdc_version_supported(port, 1, 1)) { 8748c2ecf20Sopenharmony_ci switch (port->vdisk_mtype) { 8758c2ecf20Sopenharmony_ci case VD_MEDIA_TYPE_CD: 8768c2ecf20Sopenharmony_ci pr_info(PFX "Virtual CDROM %s\n", port->disk_name); 8778c2ecf20Sopenharmony_ci g->flags |= GENHD_FL_CD; 8788c2ecf20Sopenharmony_ci g->flags |= GENHD_FL_REMOVABLE; 8798c2ecf20Sopenharmony_ci set_disk_ro(g, 1); 8808c2ecf20Sopenharmony_ci break; 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci case VD_MEDIA_TYPE_DVD: 8838c2ecf20Sopenharmony_ci pr_info(PFX "Virtual DVD %s\n", port->disk_name); 8848c2ecf20Sopenharmony_ci g->flags |= GENHD_FL_CD; 8858c2ecf20Sopenharmony_ci g->flags |= GENHD_FL_REMOVABLE; 8868c2ecf20Sopenharmony_ci set_disk_ro(g, 1); 8878c2ecf20Sopenharmony_ci break; 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci case VD_MEDIA_TYPE_FIXED: 8908c2ecf20Sopenharmony_ci pr_info(PFX "Virtual Hard disk %s\n", port->disk_name); 8918c2ecf20Sopenharmony_ci break; 8928c2ecf20Sopenharmony_ci } 8938c2ecf20Sopenharmony_ci } 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci blk_queue_physical_block_size(q, port->vdisk_phys_blksz); 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci pr_info(PFX "%s: %u sectors (%u MB) protocol %d.%d\n", 8988c2ecf20Sopenharmony_ci g->disk_name, 8998c2ecf20Sopenharmony_ci port->vdisk_size, (port->vdisk_size >> (20 - 9)), 9008c2ecf20Sopenharmony_ci port->vio.ver.major, port->vio.ver.minor); 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci device_add_disk(&port->vio.vdev->dev, g, NULL); 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci return 0; 9058c2ecf20Sopenharmony_ci} 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_cistatic struct ldc_channel_config vdc_ldc_cfg = { 9088c2ecf20Sopenharmony_ci .event = vdc_event, 9098c2ecf20Sopenharmony_ci .mtu = 64, 9108c2ecf20Sopenharmony_ci .mode = LDC_MODE_UNRELIABLE, 9118c2ecf20Sopenharmony_ci}; 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_cistatic struct vio_driver_ops vdc_vio_ops = { 9148c2ecf20Sopenharmony_ci .send_attr = vdc_send_attr, 9158c2ecf20Sopenharmony_ci .handle_attr = vdc_handle_attr, 9168c2ecf20Sopenharmony_ci .handshake_complete = vdc_handshake_complete, 9178c2ecf20Sopenharmony_ci}; 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_cistatic void print_version(void) 9208c2ecf20Sopenharmony_ci{ 9218c2ecf20Sopenharmony_ci static int version_printed; 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci if (version_printed++ == 0) 9248c2ecf20Sopenharmony_ci printk(KERN_INFO "%s", version); 9258c2ecf20Sopenharmony_ci} 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_cistruct vdc_check_port_data { 9288c2ecf20Sopenharmony_ci int dev_no; 9298c2ecf20Sopenharmony_ci char *type; 9308c2ecf20Sopenharmony_ci}; 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_cistatic int vdc_device_probed(struct device *dev, void *arg) 9338c2ecf20Sopenharmony_ci{ 9348c2ecf20Sopenharmony_ci struct vio_dev *vdev = to_vio_dev(dev); 9358c2ecf20Sopenharmony_ci struct vdc_check_port_data *port_data; 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci port_data = (struct vdc_check_port_data *)arg; 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci if ((vdev->dev_no == port_data->dev_no) && 9408c2ecf20Sopenharmony_ci (!(strcmp((char *)&vdev->type, port_data->type))) && 9418c2ecf20Sopenharmony_ci dev_get_drvdata(dev)) { 9428c2ecf20Sopenharmony_ci /* This device has already been configured 9438c2ecf20Sopenharmony_ci * by vdc_port_probe() 9448c2ecf20Sopenharmony_ci */ 9458c2ecf20Sopenharmony_ci return 1; 9468c2ecf20Sopenharmony_ci } else { 9478c2ecf20Sopenharmony_ci return 0; 9488c2ecf20Sopenharmony_ci } 9498c2ecf20Sopenharmony_ci} 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci/* Determine whether the VIO device is part of an mpgroup 9528c2ecf20Sopenharmony_ci * by locating all the virtual-device-port nodes associated 9538c2ecf20Sopenharmony_ci * with the parent virtual-device node for the VIO device 9548c2ecf20Sopenharmony_ci * and checking whether any of these nodes are vdc-ports 9558c2ecf20Sopenharmony_ci * which have already been configured. 9568c2ecf20Sopenharmony_ci * 9578c2ecf20Sopenharmony_ci * Returns true if this device is part of an mpgroup and has 9588c2ecf20Sopenharmony_ci * already been probed. 9598c2ecf20Sopenharmony_ci */ 9608c2ecf20Sopenharmony_cistatic bool vdc_port_mpgroup_check(struct vio_dev *vdev) 9618c2ecf20Sopenharmony_ci{ 9628c2ecf20Sopenharmony_ci struct vdc_check_port_data port_data; 9638c2ecf20Sopenharmony_ci struct device *dev; 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci port_data.dev_no = vdev->dev_no; 9668c2ecf20Sopenharmony_ci port_data.type = (char *)&vdev->type; 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci dev = device_find_child(vdev->dev.parent, &port_data, 9698c2ecf20Sopenharmony_ci vdc_device_probed); 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci if (dev) 9728c2ecf20Sopenharmony_ci return true; 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci return false; 9758c2ecf20Sopenharmony_ci} 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_cistatic int vdc_port_probe(struct vio_dev *vdev, const struct vio_device_id *id) 9788c2ecf20Sopenharmony_ci{ 9798c2ecf20Sopenharmony_ci struct mdesc_handle *hp; 9808c2ecf20Sopenharmony_ci struct vdc_port *port; 9818c2ecf20Sopenharmony_ci int err; 9828c2ecf20Sopenharmony_ci const u64 *ldc_timeout; 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci print_version(); 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci hp = mdesc_grab(); 9878c2ecf20Sopenharmony_ci if (!hp) 9888c2ecf20Sopenharmony_ci return -ENODEV; 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci err = -ENODEV; 9918c2ecf20Sopenharmony_ci if ((vdev->dev_no << PARTITION_SHIFT) & ~(u64)MINORMASK) { 9928c2ecf20Sopenharmony_ci printk(KERN_ERR PFX "Port id [%llu] too large.\n", 9938c2ecf20Sopenharmony_ci vdev->dev_no); 9948c2ecf20Sopenharmony_ci goto err_out_release_mdesc; 9958c2ecf20Sopenharmony_ci } 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci /* Check if this device is part of an mpgroup */ 9988c2ecf20Sopenharmony_ci if (vdc_port_mpgroup_check(vdev)) { 9998c2ecf20Sopenharmony_ci printk(KERN_WARNING 10008c2ecf20Sopenharmony_ci "VIO: Ignoring extra vdisk port %s", 10018c2ecf20Sopenharmony_ci dev_name(&vdev->dev)); 10028c2ecf20Sopenharmony_ci goto err_out_release_mdesc; 10038c2ecf20Sopenharmony_ci } 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci port = kzalloc(sizeof(*port), GFP_KERNEL); 10068c2ecf20Sopenharmony_ci err = -ENOMEM; 10078c2ecf20Sopenharmony_ci if (!port) { 10088c2ecf20Sopenharmony_ci printk(KERN_ERR PFX "Cannot allocate vdc_port.\n"); 10098c2ecf20Sopenharmony_ci goto err_out_release_mdesc; 10108c2ecf20Sopenharmony_ci } 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci if (vdev->dev_no >= 26) 10138c2ecf20Sopenharmony_ci snprintf(port->disk_name, sizeof(port->disk_name), 10148c2ecf20Sopenharmony_ci VDCBLK_NAME "%c%c", 10158c2ecf20Sopenharmony_ci 'a' + ((int)vdev->dev_no / 26) - 1, 10168c2ecf20Sopenharmony_ci 'a' + ((int)vdev->dev_no % 26)); 10178c2ecf20Sopenharmony_ci else 10188c2ecf20Sopenharmony_ci snprintf(port->disk_name, sizeof(port->disk_name), 10198c2ecf20Sopenharmony_ci VDCBLK_NAME "%c", 'a' + ((int)vdev->dev_no % 26)); 10208c2ecf20Sopenharmony_ci port->vdisk_size = -1; 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci /* Actual wall time may be double due to do_generic_file_read() doing 10238c2ecf20Sopenharmony_ci * a readahead I/O first, and once that fails it will try to read a 10248c2ecf20Sopenharmony_ci * single page. 10258c2ecf20Sopenharmony_ci */ 10268c2ecf20Sopenharmony_ci ldc_timeout = mdesc_get_property(hp, vdev->mp, "vdc-timeout", NULL); 10278c2ecf20Sopenharmony_ci port->ldc_timeout = ldc_timeout ? *ldc_timeout : 0; 10288c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&port->ldc_reset_timer_work, vdc_ldc_reset_timer_work); 10298c2ecf20Sopenharmony_ci INIT_WORK(&port->ldc_reset_work, vdc_ldc_reset_work); 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci err = vio_driver_init(&port->vio, vdev, VDEV_DISK, 10328c2ecf20Sopenharmony_ci vdc_versions, ARRAY_SIZE(vdc_versions), 10338c2ecf20Sopenharmony_ci &vdc_vio_ops, port->disk_name); 10348c2ecf20Sopenharmony_ci if (err) 10358c2ecf20Sopenharmony_ci goto err_out_free_port; 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_ci port->vdisk_block_size = VDC_DEFAULT_BLK_SIZE; 10388c2ecf20Sopenharmony_ci port->max_xfer_size = MAX_XFER_SIZE; 10398c2ecf20Sopenharmony_ci port->ring_cookies = MAX_RING_COOKIES; 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci err = vio_ldc_alloc(&port->vio, &vdc_ldc_cfg, port); 10428c2ecf20Sopenharmony_ci if (err) 10438c2ecf20Sopenharmony_ci goto err_out_free_port; 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci err = vdc_alloc_tx_ring(port); 10468c2ecf20Sopenharmony_ci if (err) 10478c2ecf20Sopenharmony_ci goto err_out_free_ldc; 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci err = probe_disk(port); 10508c2ecf20Sopenharmony_ci if (err) 10518c2ecf20Sopenharmony_ci goto err_out_free_tx_ring; 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci /* Note that the device driver_data is used to determine 10548c2ecf20Sopenharmony_ci * whether the port has been probed. 10558c2ecf20Sopenharmony_ci */ 10568c2ecf20Sopenharmony_ci dev_set_drvdata(&vdev->dev, port); 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci mdesc_release(hp); 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci return 0; 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_cierr_out_free_tx_ring: 10638c2ecf20Sopenharmony_ci vdc_free_tx_ring(port); 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_cierr_out_free_ldc: 10668c2ecf20Sopenharmony_ci vio_ldc_free(&port->vio); 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_cierr_out_free_port: 10698c2ecf20Sopenharmony_ci kfree(port); 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_cierr_out_release_mdesc: 10728c2ecf20Sopenharmony_ci mdesc_release(hp); 10738c2ecf20Sopenharmony_ci return err; 10748c2ecf20Sopenharmony_ci} 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_cistatic int vdc_port_remove(struct vio_dev *vdev) 10778c2ecf20Sopenharmony_ci{ 10788c2ecf20Sopenharmony_ci struct vdc_port *port = dev_get_drvdata(&vdev->dev); 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci if (port) { 10818c2ecf20Sopenharmony_ci blk_mq_stop_hw_queues(port->disk->queue); 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci flush_work(&port->ldc_reset_work); 10848c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&port->ldc_reset_timer_work); 10858c2ecf20Sopenharmony_ci del_timer_sync(&port->vio.timer); 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci del_gendisk(port->disk); 10888c2ecf20Sopenharmony_ci cleanup_queue(port->disk->queue); 10898c2ecf20Sopenharmony_ci put_disk(port->disk); 10908c2ecf20Sopenharmony_ci port->disk = NULL; 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci vdc_free_tx_ring(port); 10938c2ecf20Sopenharmony_ci vio_ldc_free(&port->vio); 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci dev_set_drvdata(&vdev->dev, NULL); 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci kfree(port); 10988c2ecf20Sopenharmony_ci } 10998c2ecf20Sopenharmony_ci return 0; 11008c2ecf20Sopenharmony_ci} 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_cistatic void vdc_requeue_inflight(struct vdc_port *port) 11038c2ecf20Sopenharmony_ci{ 11048c2ecf20Sopenharmony_ci struct vio_dring_state *dr = &port->vio.drings[VIO_DRIVER_TX_RING]; 11058c2ecf20Sopenharmony_ci u32 idx; 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci for (idx = dr->cons; idx != dr->prod; idx = vio_dring_next(dr, idx)) { 11088c2ecf20Sopenharmony_ci struct vio_disk_desc *desc = vio_dring_entry(dr, idx); 11098c2ecf20Sopenharmony_ci struct vdc_req_entry *rqe = &port->rq_arr[idx]; 11108c2ecf20Sopenharmony_ci struct request *req; 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci ldc_unmap(port->vio.lp, desc->cookies, desc->ncookies); 11138c2ecf20Sopenharmony_ci desc->hdr.state = VIO_DESC_FREE; 11148c2ecf20Sopenharmony_ci dr->cons = vio_dring_next(dr, idx); 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_ci req = rqe->req; 11178c2ecf20Sopenharmony_ci if (req == NULL) { 11188c2ecf20Sopenharmony_ci vdc_end_special(port, desc); 11198c2ecf20Sopenharmony_ci continue; 11208c2ecf20Sopenharmony_ci } 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ci rqe->req = NULL; 11238c2ecf20Sopenharmony_ci blk_mq_requeue_request(req, false); 11248c2ecf20Sopenharmony_ci } 11258c2ecf20Sopenharmony_ci} 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_cistatic void vdc_queue_drain(struct vdc_port *port) 11288c2ecf20Sopenharmony_ci{ 11298c2ecf20Sopenharmony_ci struct request_queue *q = port->disk->queue; 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_ci /* 11328c2ecf20Sopenharmony_ci * Mark the queue as draining, then freeze/quiesce to ensure 11338c2ecf20Sopenharmony_ci * that all existing requests are seen in ->queue_rq() and killed 11348c2ecf20Sopenharmony_ci */ 11358c2ecf20Sopenharmony_ci port->drain = 1; 11368c2ecf20Sopenharmony_ci spin_unlock_irq(&port->vio.lock); 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci blk_mq_freeze_queue(q); 11398c2ecf20Sopenharmony_ci blk_mq_quiesce_queue(q); 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci spin_lock_irq(&port->vio.lock); 11428c2ecf20Sopenharmony_ci port->drain = 0; 11438c2ecf20Sopenharmony_ci blk_mq_unquiesce_queue(q); 11448c2ecf20Sopenharmony_ci blk_mq_unfreeze_queue(q); 11458c2ecf20Sopenharmony_ci} 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_cistatic void vdc_ldc_reset_timer_work(struct work_struct *work) 11488c2ecf20Sopenharmony_ci{ 11498c2ecf20Sopenharmony_ci struct vdc_port *port; 11508c2ecf20Sopenharmony_ci struct vio_driver_state *vio; 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci port = container_of(work, struct vdc_port, ldc_reset_timer_work.work); 11538c2ecf20Sopenharmony_ci vio = &port->vio; 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci spin_lock_irq(&vio->lock); 11568c2ecf20Sopenharmony_ci if (!(port->vio.hs_state & VIO_HS_COMPLETE)) { 11578c2ecf20Sopenharmony_ci pr_warn(PFX "%s ldc down %llu seconds, draining queue\n", 11588c2ecf20Sopenharmony_ci port->disk_name, port->ldc_timeout); 11598c2ecf20Sopenharmony_ci vdc_queue_drain(port); 11608c2ecf20Sopenharmony_ci vdc_blk_queue_start(port); 11618c2ecf20Sopenharmony_ci } 11628c2ecf20Sopenharmony_ci spin_unlock_irq(&vio->lock); 11638c2ecf20Sopenharmony_ci} 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_cistatic void vdc_ldc_reset_work(struct work_struct *work) 11668c2ecf20Sopenharmony_ci{ 11678c2ecf20Sopenharmony_ci struct vdc_port *port; 11688c2ecf20Sopenharmony_ci struct vio_driver_state *vio; 11698c2ecf20Sopenharmony_ci unsigned long flags; 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci port = container_of(work, struct vdc_port, ldc_reset_work); 11728c2ecf20Sopenharmony_ci vio = &port->vio; 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci spin_lock_irqsave(&vio->lock, flags); 11758c2ecf20Sopenharmony_ci vdc_ldc_reset(port); 11768c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vio->lock, flags); 11778c2ecf20Sopenharmony_ci} 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_cistatic void vdc_ldc_reset(struct vdc_port *port) 11808c2ecf20Sopenharmony_ci{ 11818c2ecf20Sopenharmony_ci int err; 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ci assert_spin_locked(&port->vio.lock); 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci pr_warn(PFX "%s ldc link reset\n", port->disk_name); 11868c2ecf20Sopenharmony_ci blk_mq_stop_hw_queues(port->disk->queue); 11878c2ecf20Sopenharmony_ci vdc_requeue_inflight(port); 11888c2ecf20Sopenharmony_ci vdc_port_down(port); 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci err = vio_ldc_alloc(&port->vio, &vdc_ldc_cfg, port); 11918c2ecf20Sopenharmony_ci if (err) { 11928c2ecf20Sopenharmony_ci pr_err(PFX "%s vio_ldc_alloc:%d\n", port->disk_name, err); 11938c2ecf20Sopenharmony_ci return; 11948c2ecf20Sopenharmony_ci } 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci err = vdc_alloc_tx_ring(port); 11978c2ecf20Sopenharmony_ci if (err) { 11988c2ecf20Sopenharmony_ci pr_err(PFX "%s vio_alloc_tx_ring:%d\n", port->disk_name, err); 11998c2ecf20Sopenharmony_ci goto err_free_ldc; 12008c2ecf20Sopenharmony_ci } 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_ci if (port->ldc_timeout) 12038c2ecf20Sopenharmony_ci mod_delayed_work(system_wq, &port->ldc_reset_timer_work, 12048c2ecf20Sopenharmony_ci round_jiffies(jiffies + HZ * port->ldc_timeout)); 12058c2ecf20Sopenharmony_ci mod_timer(&port->vio.timer, round_jiffies(jiffies + HZ)); 12068c2ecf20Sopenharmony_ci return; 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_cierr_free_ldc: 12098c2ecf20Sopenharmony_ci vio_ldc_free(&port->vio); 12108c2ecf20Sopenharmony_ci} 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_cistatic const struct vio_device_id vdc_port_match[] = { 12138c2ecf20Sopenharmony_ci { 12148c2ecf20Sopenharmony_ci .type = "vdc-port", 12158c2ecf20Sopenharmony_ci }, 12168c2ecf20Sopenharmony_ci {}, 12178c2ecf20Sopenharmony_ci}; 12188c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(vio, vdc_port_match); 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_cistatic struct vio_driver vdc_port_driver = { 12218c2ecf20Sopenharmony_ci .id_table = vdc_port_match, 12228c2ecf20Sopenharmony_ci .probe = vdc_port_probe, 12238c2ecf20Sopenharmony_ci .remove = vdc_port_remove, 12248c2ecf20Sopenharmony_ci .name = "vdc_port", 12258c2ecf20Sopenharmony_ci}; 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_cistatic int __init vdc_init(void) 12288c2ecf20Sopenharmony_ci{ 12298c2ecf20Sopenharmony_ci int err; 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_ci sunvdc_wq = alloc_workqueue("sunvdc", 0, 0); 12328c2ecf20Sopenharmony_ci if (!sunvdc_wq) 12338c2ecf20Sopenharmony_ci return -ENOMEM; 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci err = register_blkdev(0, VDCBLK_NAME); 12368c2ecf20Sopenharmony_ci if (err < 0) 12378c2ecf20Sopenharmony_ci goto out_free_wq; 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_ci vdc_major = err; 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_ci err = vio_register_driver(&vdc_port_driver); 12428c2ecf20Sopenharmony_ci if (err) 12438c2ecf20Sopenharmony_ci goto out_unregister_blkdev; 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_ci return 0; 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_ciout_unregister_blkdev: 12488c2ecf20Sopenharmony_ci unregister_blkdev(vdc_major, VDCBLK_NAME); 12498c2ecf20Sopenharmony_ci vdc_major = 0; 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_ciout_free_wq: 12528c2ecf20Sopenharmony_ci destroy_workqueue(sunvdc_wq); 12538c2ecf20Sopenharmony_ci return err; 12548c2ecf20Sopenharmony_ci} 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_cistatic void __exit vdc_exit(void) 12578c2ecf20Sopenharmony_ci{ 12588c2ecf20Sopenharmony_ci vio_unregister_driver(&vdc_port_driver); 12598c2ecf20Sopenharmony_ci unregister_blkdev(vdc_major, VDCBLK_NAME); 12608c2ecf20Sopenharmony_ci destroy_workqueue(sunvdc_wq); 12618c2ecf20Sopenharmony_ci} 12628c2ecf20Sopenharmony_ci 12638c2ecf20Sopenharmony_cimodule_init(vdc_init); 12648c2ecf20Sopenharmony_cimodule_exit(vdc_exit); 1265