From 13049537007dee73a76f0a30fcbc24d02c6fa9e4 Mon Sep 17 00:00:00 2001 From: Joseph Handzik Date: Mon, 8 Aug 2011 11:40:15 +0200 Subject: cciss: Adds simple mode functionality Signed-off-by: Joseph Handzik Acked-by: Stephen M. Cameron Signed-off-by: Jens Axboe diff --git a/Documentation/blockdev/cciss.txt b/Documentation/blockdev/cciss.txt index c00c6a5..71464e0 100644 --- a/Documentation/blockdev/cciss.txt +++ b/Documentation/blockdev/cciss.txt @@ -78,6 +78,16 @@ The device naming scheme is: /dev/cciss/c1d1p2 Controller 1, disk 1, partition 2 /dev/cciss/c1d1p3 Controller 1, disk 1, partition 3 +CCISS simple mode support +------------------------- + +The "cciss_simple_mode=1" boot parameter may be used to prevent the driver +from putting the controller into "performant" mode. The difference is that +with simple mode, each command completion requires an interrupt, while with +"performant mode" (the default, and ordinarily better performing) it is +possible to have multiple command completions indicated by a single +interrupt. + SCSI tape drive and medium changer support ------------------------------------------ diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index 8f4ef65..61f0b5b 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -68,6 +68,10 @@ static int cciss_tape_cmds = 6; module_param(cciss_tape_cmds, int, 0644); MODULE_PARM_DESC(cciss_tape_cmds, "number of commands to allocate for tape devices (default: 6)"); +static int cciss_simple_mode; +module_param(cciss_simple_mode, int, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(cciss_simple_mode, + "Use 'simple mode' rather than 'performant mode'"); static DEFINE_MUTEX(cciss_mutex); static struct proc_dir_entry *proc_cciss; @@ -176,6 +180,7 @@ static void cciss_geometry_inquiry(ctlr_info_t *h, int logvol, unsigned int block_size, InquiryData_struct *inq_buff, drive_info_struct *drv); static void __devinit cciss_interrupt_mode(ctlr_info_t *); +static int __devinit cciss_enter_simple_mode(struct ctlr_info *h); static void start_io(ctlr_info_t *h); static int sendcmd_withirq(ctlr_info_t *h, __u8 cmd, void *buff, size_t size, __u8 page_code, unsigned char scsi3addr[], @@ -388,7 +393,7 @@ static void cciss_seq_show_header(struct seq_file *seq) h->product_name, (unsigned long)h->board_id, h->firm_ver[0], h->firm_ver[1], h->firm_ver[2], - h->firm_ver[3], (unsigned int)h->intr[PERF_MODE_INT], + h->firm_ver[3], (unsigned int)h->intr[h->intr_mode], h->num_luns, h->Qdepth, h->commands_outstanding, h->maxQsinceinit, h->max_outstanding, h->maxSG); @@ -3984,6 +3989,9 @@ static void __devinit cciss_put_controller_into_performant_mode(ctlr_info_t *h) { __u32 trans_support; + if (cciss_simple_mode) + return; + dev_dbg(&h->pdev->dev, "Trying to put board into Performant mode\n"); /* Attempt to put controller into performant mode if supported */ /* Does board support performant mode? */ @@ -4081,7 +4089,7 @@ static void __devinit cciss_interrupt_mode(ctlr_info_t *h) default_int_mode: #endif /* CONFIG_PCI_MSI */ /* if we get here we're going to use the default interrupt mode */ - h->intr[PERF_MODE_INT] = h->pdev->irq; + h->intr[h->intr_mode] = h->pdev->irq; return; } @@ -4341,6 +4349,9 @@ static int __devinit cciss_pci_init(ctlr_info_t *h) } cciss_enable_scsi_prefetch(h); cciss_p600_dma_prefetch_quirk(h); + err = cciss_enter_simple_mode(h); + if (err) + goto err_out_free_res; cciss_put_controller_into_performant_mode(h); return 0; @@ -4843,20 +4854,20 @@ static int cciss_request_irq(ctlr_info_t *h, irqreturn_t (*intxhandler)(int, void *)) { if (h->msix_vector || h->msi_vector) { - if (!request_irq(h->intr[PERF_MODE_INT], msixhandler, + if (!request_irq(h->intr[h->intr_mode], msixhandler, IRQF_DISABLED, h->devname, h)) return 0; dev_err(&h->pdev->dev, "Unable to get msi irq %d" - " for %s\n", h->intr[PERF_MODE_INT], + " for %s\n", h->intr[h->intr_mode], h->devname); return -1; } - if (!request_irq(h->intr[PERF_MODE_INT], intxhandler, + if (!request_irq(h->intr[h->intr_mode], intxhandler, IRQF_DISABLED, h->devname, h)) return 0; dev_err(&h->pdev->dev, "Unable to get irq %d for %s\n", - h->intr[PERF_MODE_INT], h->devname); + h->intr[h->intr_mode], h->devname); return -1; } @@ -4887,7 +4898,7 @@ static void cciss_undo_allocations_after_kdump_soft_reset(ctlr_info_t *h) { int ctlr = h->ctlr; - free_irq(h->intr[PERF_MODE_INT], h); + free_irq(h->intr[h->intr_mode], h); #ifdef CONFIG_PCI_MSI if (h->msix_vector) pci_disable_msix(h->pdev); @@ -4953,6 +4964,7 @@ reinit_after_soft_reset: h = hba[i]; h->pdev = pdev; h->busy_initializing = 1; + h->intr_mode = cciss_simple_mode ? SIMPLE_MODE_INT : PERF_MODE_INT; INIT_LIST_HEAD(&h->cmpQ); INIT_LIST_HEAD(&h->reqQ); mutex_init(&h->busy_shutting_down); @@ -5009,7 +5021,7 @@ reinit_after_soft_reset: dev_info(&h->pdev->dev, "%s: <0x%x> at PCI %s IRQ %d%s using DAC\n", h->devname, pdev->device, pci_name(pdev), - h->intr[PERF_MODE_INT], dac ? "" : " not"); + h->intr[h->intr_mode], dac ? "" : " not"); if (cciss_allocate_cmd_pool(h)) goto clean4; @@ -5056,7 +5068,7 @@ reinit_after_soft_reset: spin_lock_irqsave(&h->lock, flags); h->access.set_intr_mask(h, CCISS_INTR_OFF); spin_unlock_irqrestore(&h->lock, flags); - free_irq(h->intr[PERF_MODE_INT], h); + free_irq(h->intr[h->intr_mode], h); rc = cciss_request_irq(h, cciss_msix_discard_completions, cciss_intx_discard_completions); if (rc) { @@ -5133,7 +5145,7 @@ clean4: cciss_free_cmd_pool(h); cciss_free_scatterlists(h); cciss_free_sg_chain_blocks(h->cmd_sg_list, h->nr_cmds); - free_irq(h->intr[PERF_MODE_INT], h); + free_irq(h->intr[h->intr_mode], h); clean2: unregister_blkdev(h->major, h->devname); clean1: @@ -5172,9 +5184,31 @@ static void cciss_shutdown(struct pci_dev *pdev) if (return_code != IO_OK) dev_warn(&h->pdev->dev, "Error flushing cache\n"); h->access.set_intr_mask(h, CCISS_INTR_OFF); - free_irq(h->intr[PERF_MODE_INT], h); + free_irq(h->intr[h->intr_mode], h); } +static int __devinit cciss_enter_simple_mode(struct ctlr_info *h) +{ + u32 trans_support; + + trans_support = readl(&(h->cfgtable->TransportSupport)); + if (!(trans_support & SIMPLE_MODE)) + return -ENOTSUPP; + + h->max_commands = readl(&(h->cfgtable->CmdsOutMax)); + writel(CFGTBL_Trans_Simple, &(h->cfgtable->HostWrite.TransportRequest)); + writel(CFGTBL_ChangeReq, h->vaddr + SA5_DOORBELL); + cciss_wait_for_mode_change_ack(h); + print_cfg_table(h); + if (!(readl(&(h->cfgtable->TransportActive)) & CFGTBL_Trans_Simple)) { + dev_warn(&h->pdev->dev, "unable to get board into simple mode\n"); + return -ENODEV; + } + h->transMethod = CFGTBL_Trans_Simple; + return 0; +} + + static void __devexit cciss_remove_one(struct pci_dev *pdev) { ctlr_info_t *h; diff --git a/drivers/block/cciss.h b/drivers/block/cciss.h index c049548..7fda30e 100644 --- a/drivers/block/cciss.h +++ b/drivers/block/cciss.h @@ -92,6 +92,7 @@ struct ctlr_info unsigned int intr[4]; unsigned int msix_vector; unsigned int msi_vector; + int intr_mode; int cciss_max_sectors; BYTE cciss_read; BYTE cciss_write; -- cgit v0.10.2 From f963d270cb7bbb8eeb57901d02b22a493e664fd2 Mon Sep 17 00:00:00 2001 From: Joe Handzik Date: Mon, 8 Aug 2011 11:40:17 +0200 Subject: cciss: add transport mode attribute to sys Signed-off-by: Joseph Handzik Acked-by: Stephen M. Cameron Signed-off-by: Jens Axboe diff --git a/Documentation/ABI/testing/sysfs-bus-pci-devices-cciss b/Documentation/ABI/testing/sysfs-bus-pci-devices-cciss index f5bb0a3..53d99ed 100644 --- a/Documentation/ABI/testing/sysfs-bus-pci-devices-cciss +++ b/Documentation/ABI/testing/sysfs-bus-pci-devices-cciss @@ -71,3 +71,10 @@ Description: Value of 1 indicates the controller can honor the reset_devices a dump device, as kdump requires resetting the device in order to work reliably. +Where: /sys/bus/pci/devices//ccissX/transport_mode +Date: July 2011 +Kernel Version: 3.0 +Contact: iss_storagedev@hp.com +Description: Value of "simple" indicates that the controller has been placed + in "simple mode". Value of "performant" indicates that the + controller has been placed in "performant mode". diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index 61f0b5b..6da7ede 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -641,6 +641,18 @@ static ssize_t host_store_rescan(struct device *dev, } static DEVICE_ATTR(rescan, S_IWUSR, NULL, host_store_rescan); +static ssize_t host_show_transport_mode(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct ctlr_info *h = to_hba(dev); + + return snprintf(buf, 20, "%s\n", + h->transMethod & CFGTBL_Trans_Performant ? + "performant" : "simple"); +} +static DEVICE_ATTR(transport_mode, S_IRUGO, host_show_transport_mode, NULL); + static ssize_t dev_show_unique_id(struct device *dev, struct device_attribute *attr, char *buf) @@ -813,6 +825,7 @@ static DEVICE_ATTR(usage_count, S_IRUGO, cciss_show_usage_count, NULL); static struct attribute *cciss_host_attrs[] = { &dev_attr_rescan.attr, &dev_attr_resettable.attr, + &dev_attr_transport_mode.attr, NULL }; -- cgit v0.10.2 From 25ac0c2b971235d3e8c7af0b6889a1eb6988b559 Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Fri, 19 Aug 2011 14:48:17 +0200 Subject: nbd: use task_pid_nr() to get current pid Signed-off-by: WANG Cong Cc: Paul Clements Signed-off-by: Andrew Morton Signed-off-by: Jens Axboe diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index f533f33..a928287 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -405,7 +405,7 @@ static int nbd_do_it(struct nbd_device *lo) BUG_ON(lo->magic != LO_MAGIC); - lo->pid = current->pid; + lo->pid = task_pid_nr(current); ret = sysfs_create_file(&disk_to_dev(lo->disk)->kobj, &pid_attr.attr); if (ret) { printk(KERN_ERR "nbd: sysfs_create_file failed!"); -- cgit v0.10.2 From 1695b87f7dd152b866f0dd867c8e599025fc4965 Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Fri, 19 Aug 2011 14:48:21 +0200 Subject: nbd: replace sysfs_create_file() with device_create_file() Signed-off-by: WANG Cong Cc: Paul Clements Signed-off-by: Andrew Morton Signed-off-by: Jens Axboe diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index a928287..da98360 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -406,9 +406,9 @@ static int nbd_do_it(struct nbd_device *lo) BUG_ON(lo->magic != LO_MAGIC); lo->pid = task_pid_nr(current); - ret = sysfs_create_file(&disk_to_dev(lo->disk)->kobj, &pid_attr.attr); + ret = device_create_file(disk_to_dev(lo->disk), &pid_attr); if (ret) { - printk(KERN_ERR "nbd: sysfs_create_file failed!"); + printk(KERN_ERR "nbd: device_create_file failed!"); lo->pid = 0; return ret; } @@ -416,7 +416,7 @@ static int nbd_do_it(struct nbd_device *lo) while ((req = nbd_read_stat(lo)) != NULL) nbd_end_request(req); - sysfs_remove_file(&disk_to_dev(lo->disk)->kobj, &pid_attr.attr); + device_remove_file(disk_to_dev(lo->disk), &pid_attr); lo->pid = 0; return 0; } -- cgit v0.10.2 From 7f1b90f99a2d4253f8eb1221d39da072178adbc5 Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Fri, 19 Aug 2011 14:48:22 +0200 Subject: nbd: replace printk KERN_ERR with dev_err() Signed-off-by: WANG Cong Cc: Paul Clements Signed-off-by: Andrew Morton Signed-off-by: Jens Axboe diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index da98360..1b8e09f 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -158,8 +158,9 @@ static int sock_xmit(struct nbd_device *lo, int send, void *buf, int size, sigset_t blocked, oldset; if (unlikely(!sock)) { - printk(KERN_ERR "%s: Attempted %s on closed socket in sock_xmit\n", - lo->disk->disk_name, (send ? "send" : "recv")); + dev_err(disk_to_dev(lo->disk), + "Attempted %s on closed socket in sock_xmit\n", + (send ? "send" : "recv")); return -EINVAL; } @@ -250,8 +251,8 @@ static int nbd_send_req(struct nbd_device *lo, struct request *req) result = sock_xmit(lo, 1, &request, sizeof(request), (nbd_cmd(req) == NBD_CMD_WRITE) ? MSG_MORE : 0); if (result <= 0) { - printk(KERN_ERR "%s: Send control failed (result %d)\n", - lo->disk->disk_name, result); + dev_err(disk_to_dev(lo->disk), + "Send control failed (result %d)\n", result); goto error_out; } @@ -270,8 +271,9 @@ static int nbd_send_req(struct nbd_device *lo, struct request *req) lo->disk->disk_name, req, bvec->bv_len); result = sock_send_bvec(lo, bvec, flags); if (result <= 0) { - printk(KERN_ERR "%s: Send data failed (result %d)\n", - lo->disk->disk_name, result); + dev_err(disk_to_dev(lo->disk), + "Send data failed (result %d)\n", + result); goto error_out; } } @@ -328,14 +330,13 @@ static struct request *nbd_read_stat(struct nbd_device *lo) reply.magic = 0; result = sock_xmit(lo, 0, &reply, sizeof(reply), MSG_WAITALL); if (result <= 0) { - printk(KERN_ERR "%s: Receive control failed (result %d)\n", - lo->disk->disk_name, result); + dev_err(disk_to_dev(lo->disk), + "Receive control failed (result %d)\n", result); goto harderror; } if (ntohl(reply.magic) != NBD_REPLY_MAGIC) { - printk(KERN_ERR "%s: Wrong magic (0x%lx)\n", - lo->disk->disk_name, + dev_err(disk_to_dev(lo->disk), "Wrong magic (0x%lx)\n", (unsigned long)ntohl(reply.magic)); result = -EPROTO; goto harderror; @@ -347,15 +348,15 @@ static struct request *nbd_read_stat(struct nbd_device *lo) if (result != -ENOENT) goto harderror; - printk(KERN_ERR "%s: Unexpected reply (%p)\n", - lo->disk->disk_name, reply.handle); + dev_err(disk_to_dev(lo->disk), "Unexpected reply (%p)\n", + reply.handle); result = -EBADR; goto harderror; } if (ntohl(reply.error)) { - printk(KERN_ERR "%s: Other side returned error (%d)\n", - lo->disk->disk_name, ntohl(reply.error)); + dev_err(disk_to_dev(lo->disk), "Other side returned error (%d)\n", + ntohl(reply.error)); req->errors++; return req; } @@ -369,8 +370,8 @@ static struct request *nbd_read_stat(struct nbd_device *lo) rq_for_each_segment(bvec, req, iter) { result = sock_recv_bvec(lo, bvec); if (result <= 0) { - printk(KERN_ERR "%s: Receive data failed (result %d)\n", - lo->disk->disk_name, result); + dev_err(disk_to_dev(lo->disk), "Receive data failed (result %d)\n", + result); req->errors++; return req; } @@ -408,7 +409,7 @@ static int nbd_do_it(struct nbd_device *lo) lo->pid = task_pid_nr(current); ret = device_create_file(disk_to_dev(lo->disk), &pid_attr); if (ret) { - printk(KERN_ERR "nbd: device_create_file failed!"); + dev_err(disk_to_dev(lo->disk), "device_create_file failed!\n"); lo->pid = 0; return ret; } @@ -457,8 +458,8 @@ static void nbd_handle_req(struct nbd_device *lo, struct request *req) if (rq_data_dir(req) == WRITE) { nbd_cmd(req) = NBD_CMD_WRITE; if (lo->flags & NBD_READ_ONLY) { - printk(KERN_ERR "%s: Write on read-only\n", - lo->disk->disk_name); + dev_err(disk_to_dev(lo->disk), + "Write on read-only\n"); goto error_out; } } @@ -468,16 +469,15 @@ static void nbd_handle_req(struct nbd_device *lo, struct request *req) mutex_lock(&lo->tx_lock); if (unlikely(!lo->sock)) { mutex_unlock(&lo->tx_lock); - printk(KERN_ERR "%s: Attempted send on closed socket\n", - lo->disk->disk_name); + dev_err(disk_to_dev(lo->disk), + "Attempted send on closed socket\n"); goto error_out; } lo->active_req = req; if (nbd_send_req(lo, req) != 0) { - printk(KERN_ERR "%s: Request send failed\n", - lo->disk->disk_name); + dev_err(disk_to_dev(lo->disk), "Request send failed\n"); req->errors++; nbd_end_request(req); } else { @@ -549,8 +549,8 @@ static void do_nbd_request(struct request_queue *q) BUG_ON(lo->magic != LO_MAGIC); if (unlikely(!lo->sock)) { - printk(KERN_ERR "%s: Attempted send on closed socket\n", - lo->disk->disk_name); + dev_err(disk_to_dev(lo->disk), + "Attempted send on closed socket\n"); req->errors++; nbd_end_request(req); spin_lock_irq(q->queue_lock); -- cgit v0.10.2 From 7742ce4ab49976851ce7f0185dcbe491935371a2 Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Fri, 19 Aug 2011 14:48:28 +0200 Subject: nbd: lower the loglevel of an error message This is only an error, no need to use KERN_CRIT log level. Signed-off-by: WANG Cong Cc: Paul Clements Signed-off-by: Andrew Morton Signed-off-by: Jens Axboe diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index 1b8e09f..2b5fc11 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -745,7 +745,7 @@ static int __init nbd_init(void) BUILD_BUG_ON(sizeof(struct nbd_request) != 28); if (max_part < 0) { - printk(KERN_CRIT "nbd: max_part must be >= 0\n"); + printk(KERN_ERR "nbd: max_part must be >= 0\n"); return -EINVAL; } -- cgit v0.10.2 From 5eedf5415cd57f8db8642a5db4cf8e5507390030 Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Fri, 19 Aug 2011 14:48:28 +0200 Subject: nbd: replace some printk with dev_warn() and dev_info() Signed-off-by: WANG Cong Cc: Paul Clements Signed-off-by: Andrew Morton Signed-off-by: Jens Axboe diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index 2b5fc11..3353d98 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -127,8 +127,7 @@ static void sock_shutdown(struct nbd_device *lo, int lock) if (lock) mutex_lock(&lo->tx_lock); if (lo->sock) { - printk(KERN_WARNING "%s: shutting down socket\n", - lo->disk->disk_name); + dev_warn(disk_to_dev(lo->disk), "shutting down socket\n"); kernel_sock_shutdown(lo->sock, SHUT_RDWR); lo->sock = NULL; } @@ -576,7 +575,7 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *lo, case NBD_DISCONNECT: { struct request sreq; - printk(KERN_INFO "%s: NBD_DISCONNECT\n", lo->disk->disk_name); + dev_info(disk_to_dev(lo->disk), "NBD_DISCONNECT\n"); blk_rq_init(NULL, &sreq); sreq.cmd_type = REQ_TYPE_SPECIAL; @@ -674,7 +673,7 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *lo, file = lo->file; lo->file = NULL; nbd_clear_que(lo); - printk(KERN_WARNING "%s: queue cleared\n", lo->disk->disk_name); + dev_warn(disk_to_dev(lo->disk), "queue cleared\n"); if (file) fput(file); lo->bytesize = 0; @@ -694,8 +693,8 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *lo, return 0; case NBD_PRINT_DEBUG: - printk(KERN_INFO "%s: next = %p, prev = %p, head = %p\n", - bdev->bd_disk->disk_name, + dev_info(disk_to_dev(lo->disk), + "next = %p, prev = %p, head = %p\n", lo->queue_head.next, lo->queue_head.prev, &lo->queue_head); return 0; -- cgit v0.10.2 From 548ef6cc26ca1c81f19855d57d3fb0f9a7ce3385 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Fri, 19 Aug 2011 14:48:28 +0200 Subject: nbd-replace-some-printk-with-dev_warn-and-dev_info-checkpatch-fixes ERROR: code indent should use tabs where possible #30: FILE: drivers/block/nbd.c:578: +^I dev_info(disk_to_dev(lo->disk), "NBD_DISCONNECT\n");$ total: 1 errors, 0 warnings, 35 lines checked NOTE: whitespace errors detected, you may wish to use scripts/cleanpatch or scripts/cleanfile ./patches/nbd-replace-some-printk-with-dev_warn-and-dev_info.patch has style problems, please review. If any of these errors are false positives, please report them to the maintainer, see CHECKPATCH in MAINTAINERS. Please run checkpatch prior to sending patches Cc: Paul Clements Cc: WANG Cong Signed-off-by: Andrew Morton Signed-off-by: Jens Axboe diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index 3353d98..c3f0ee1 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -575,7 +575,7 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *lo, case NBD_DISCONNECT: { struct request sreq; - dev_info(disk_to_dev(lo->disk), "NBD_DISCONNECT\n"); + dev_info(disk_to_dev(lo->disk), "NBD_DISCONNECT\n"); blk_rq_init(NULL, &sreq); sreq.cmd_type = REQ_TYPE_SPECIAL; -- cgit v0.10.2 From dfaa2ef68e80c378e610e3c8c536f1c239e8d3ef Mon Sep 17 00:00:00 2001 From: Lukas Czerner Date: Fri, 19 Aug 2011 14:50:46 +0200 Subject: loop: add discard support for loop devices This commit adds discard support for loop devices. Discard is usually supported by SSD and thinly provisioned devices as a method for reclaiming unused space. This is no different than trying to reclaim back space which is not used by the file system on the image, but it still occupies space on the host file system. We can do the reclamation on file system which does support hole punching. So when discard request gets to the loop driver we can translate that to punch a hole to the underlying file, hence reclaim the free space. This is very useful for trimming down the size of the image to only what is really used by the file system on that image. Fstrim may be used for that purpose. It has been tested on ext4, xfs and btrfs with the image file systems ext4, ext3, xfs and btrfs. ext4, or ext6 image on ext4 file system has some problems but it seems that ext4 punch hole implementation is somewhat flawed and it is unrelated to this commit. Also this is a very good method of validating file systems punch hole implementation. Note that when encryption is used, discard support is disabled, because using it might leak some information useful for possible attacker. Signed-off-by: Lukas Czerner Reviewed-by: Jeff Moyer Signed-off-by: Jens Axboe diff --git a/drivers/block/loop.c b/drivers/block/loop.c index 76c8da7..936cac3 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -75,6 +75,7 @@ #include #include #include +#include #include @@ -484,6 +485,29 @@ static int do_bio_filebacked(struct loop_device *lo, struct bio *bio) } } + /* + * We use punch hole to reclaim the free space used by the + * image a.k.a. discard. However we do support discard if + * encryption is enabled, because it may give an attacker + * useful information. + */ + if (bio->bi_rw & REQ_DISCARD) { + struct file *file = lo->lo_backing_file; + int mode = FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE; + + if ((!file->f_op->fallocate) || + lo->lo_encrypt_key_size) { + ret = -EOPNOTSUPP; + goto out; + } + ret = file->f_op->fallocate(file, mode, pos, + bio->bi_size); + if (unlikely(ret && ret != -EINVAL && + ret != -EOPNOTSUPP)) + ret = -EIO; + goto out; + } + ret = lo_send(lo, bio, pos); if ((bio->bi_rw & REQ_FUA) && !ret) { @@ -814,6 +838,35 @@ static void loop_sysfs_exit(struct loop_device *lo) &loop_attribute_group); } +static void loop_config_discard(struct loop_device *lo) +{ + struct file *file = lo->lo_backing_file; + struct inode *inode = file->f_mapping->host; + struct request_queue *q = lo->lo_queue; + + /* + * We use punch hole to reclaim the free space used by the + * image a.k.a. discard. However we do support discard if + * encryption is enabled, because it may give an attacker + * useful information. + */ + if ((!file->f_op->fallocate) || + lo->lo_encrypt_key_size) { + q->limits.discard_granularity = 0; + q->limits.discard_alignment = 0; + q->limits.max_discard_sectors = 0; + q->limits.discard_zeroes_data = 0; + queue_flag_clear_unlocked(QUEUE_FLAG_DISCARD, q); + return; + } + + q->limits.discard_granularity = inode->i_sb->s_blocksize; + q->limits.discard_alignment = inode->i_sb->s_blocksize; + q->limits.max_discard_sectors = UINT_MAX >> 9; + q->limits.discard_zeroes_data = 1; + queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, q); +} + static int loop_set_fd(struct loop_device *lo, fmode_t mode, struct block_device *bdev, unsigned int arg) { @@ -1090,6 +1143,7 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info) if (figure_loop_size(lo)) return -EFBIG; } + loop_config_discard(lo); memcpy(lo->lo_file_name, info->lo_file_name, LO_NAME_SIZE); memcpy(lo->lo_crypt_name, info->lo_crypt_name, LO_NAME_SIZE); -- cgit v0.10.2 From d27769ec3df1a8de9ca450d2dcd72d1ab259ba32 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 23 Aug 2011 20:01:04 +0200 Subject: block: add GENHD_FL_NO_PART_SCAN There are cases where suppressing partition scan is useful - e.g. for lo devices and pseudo SATA devices which advertise to be a disk but get upset on partition scan (some port multiplier control devices show such behavior). This patch adds GENHD_FL_NO_PART_SCAN which suppresses partition scan regardless of the number of possible partitions. disk_partitionable() is renamed to disk_part_scan_enabled() as suppressing partition scan doesn't imply the device can't be partitioned using BLKPG_ADD/DEL_PARTITION calls from userland. show_partition() now directly tests disk_max_parts() to maintain backward-compatibility. -v2: Updated to make it clear that only partition scan is suppressed not partitioning itself as suggested by Kay Sievers. Signed-off-by: Tejun Heo Cc: Kay Sievers Signed-off-by: Jens Axboe diff --git a/block/genhd.c b/block/genhd.c index 5cb51c5..2429ecb 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -536,7 +536,7 @@ void register_disk(struct gendisk *disk) disk->slave_dir = kobject_create_and_add("slaves", &ddev->kobj); /* No minors to use for partitions */ - if (!disk_partitionable(disk)) + if (!disk_part_scan_enabled(disk)) goto exit; /* No such device (e.g., media were just removed) */ @@ -841,7 +841,7 @@ static int show_partition(struct seq_file *seqf, void *v) char buf[BDEVNAME_SIZE]; /* Don't show non-partitionable removeable devices or empty devices */ - if (!get_capacity(sgp) || (!disk_partitionable(sgp) && + if (!get_capacity(sgp) || (!disk_max_parts(sgp) && (sgp->flags & GENHD_FL_REMOVABLE))) return 0; if (sgp->flags & GENHD_FL_SUPPRESS_PARTITION_INFO) diff --git a/block/ioctl.c b/block/ioctl.c index 1124cd2..5c74efc 100644 --- a/block/ioctl.c +++ b/block/ioctl.c @@ -101,7 +101,7 @@ static int blkdev_reread_part(struct block_device *bdev) struct gendisk *disk = bdev->bd_disk; int res; - if (!disk_partitionable(disk) || bdev != bdev->bd_contains) + if (!disk_part_scan_enabled(disk) || bdev != bdev->bd_contains) return -EINVAL; if (!capable(CAP_SYS_ADMIN)) return -EACCES; diff --git a/fs/block_dev.c b/fs/block_dev.c index ff77262..0bed0d4 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -971,7 +971,7 @@ static void flush_disk(struct block_device *bdev, bool kill_dirty) if (!bdev->bd_disk) return; - if (disk_partitionable(bdev->bd_disk)) + if (disk_part_scan_enabled(bdev->bd_disk)) bdev->bd_invalidated = 1; } diff --git a/include/linux/genhd.h b/include/linux/genhd.h index 02fa469..6d18f35 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -128,6 +128,7 @@ struct hd_struct { #define GENHD_FL_EXT_DEVT 64 /* allow extended devt */ #define GENHD_FL_NATIVE_CAPACITY 128 #define GENHD_FL_BLOCK_EVENTS_ON_EXCL_WRITE 256 +#define GENHD_FL_NO_PART_SCAN 512 enum { DISK_EVENT_MEDIA_CHANGE = 1 << 0, /* media changed */ @@ -234,9 +235,10 @@ static inline int disk_max_parts(struct gendisk *disk) return disk->minors; } -static inline bool disk_partitionable(struct gendisk *disk) +static inline bool disk_part_scan_enabled(struct gendisk *disk) { - return disk_max_parts(disk) > 1; + return disk_max_parts(disk) > 1 && + !(disk->flags & GENHD_FL_NO_PART_SCAN); } static inline dev_t disk_devt(struct gendisk *disk) -- cgit v0.10.2 From e03c8dd14915fabc101aa495828d58598dc5af98 Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Tue, 23 Aug 2011 20:12:04 +0200 Subject: loop: always allow userspace partitions and optionally support automatic scanning Automatic partition scanning can be requested individually per loop device during its setup by setting LO_FLAGS_PARTSCAN. By default, no partition tables are scanned. Userspace can now always add and remove partitions from all loop devices, regardless if the in-kernel partition scanner is enabled or not. The needed partition minor numbers are allocated from the extended minors space, the main loop device numbers will continue to match the loop minors, regardless of the number of partitions used. # grep . /sys/class/block/loop1/loop/* /sys/block/loop1/loop/autoclear:0 /sys/block/loop1/loop/backing_file:/home/kay/data/stuff/part.img /sys/block/loop1/loop/offset:0 /sys/block/loop1/loop/partscan:1 /sys/block/loop1/loop/sizelimit:0 # ls -l /dev/loop* brw-rw---- 1 root disk 7, 0 Aug 14 20:22 /dev/loop0 brw-rw---- 1 root disk 7, 1 Aug 14 20:23 /dev/loop1 brw-rw---- 1 root disk 259, 0 Aug 14 20:23 /dev/loop1p1 brw-rw---- 1 root disk 259, 1 Aug 14 20:23 /dev/loop1p2 brw-rw---- 1 root disk 7, 99 Aug 14 20:23 /dev/loop99 brw-rw---- 1 root disk 259, 2 Aug 14 20:23 /dev/loop99p1 brw-rw---- 1 root disk 259, 3 Aug 14 20:23 /dev/loop99p2 crw------T 1 root root 10, 237 Aug 14 20:22 /dev/loop-control Cc: Karel Zak Cc: Davidlohr Bueso Acked-By: Tejun Heo Signed-off-by: Kay Sievers Signed-off-by: Jens Axboe diff --git a/drivers/block/loop.c b/drivers/block/loop.c index 936cac3..b336433 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -724,7 +724,7 @@ static int loop_change_fd(struct loop_device *lo, struct block_device *bdev, goto out_putf; fput(old_file); - if (max_part > 0) + if (lo->lo_flags & LO_FLAGS_PARTSCAN) ioctl_by_bdev(bdev, BLKRRPART, 0); return 0; @@ -808,16 +808,25 @@ static ssize_t loop_attr_autoclear_show(struct loop_device *lo, char *buf) return sprintf(buf, "%s\n", autoclear ? "1" : "0"); } +static ssize_t loop_attr_partscan_show(struct loop_device *lo, char *buf) +{ + int partscan = (lo->lo_flags & LO_FLAGS_PARTSCAN); + + return sprintf(buf, "%s\n", partscan ? "1" : "0"); +} + LOOP_ATTR_RO(backing_file); LOOP_ATTR_RO(offset); LOOP_ATTR_RO(sizelimit); LOOP_ATTR_RO(autoclear); +LOOP_ATTR_RO(partscan); static struct attribute *loop_attrs[] = { &loop_attr_backing_file.attr, &loop_attr_offset.attr, &loop_attr_sizelimit.attr, &loop_attr_autoclear.attr, + &loop_attr_partscan.attr, NULL, }; @@ -979,7 +988,9 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode, } lo->lo_state = Lo_bound; wake_up_process(lo->lo_thread); - if (max_part > 0) + if (part_shift) + lo->lo_flags |= LO_FLAGS_PARTSCAN; + if (lo->lo_flags & LO_FLAGS_PARTSCAN) ioctl_by_bdev(bdev, BLKRRPART, 0); return 0; @@ -1070,7 +1081,6 @@ static int loop_clr_fd(struct loop_device *lo, struct block_device *bdev) lo->lo_offset = 0; lo->lo_sizelimit = 0; lo->lo_encrypt_key_size = 0; - lo->lo_flags = 0; lo->lo_thread = NULL; memset(lo->lo_encrypt_key, 0, LO_KEY_SIZE); memset(lo->lo_crypt_name, 0, LO_NAME_SIZE); @@ -1088,8 +1098,11 @@ static int loop_clr_fd(struct loop_device *lo, struct block_device *bdev) lo->lo_state = Lo_unbound; /* This is safe: open() is still holding a reference. */ module_put(THIS_MODULE); - if (max_part > 0 && bdev) + if (lo->lo_flags & LO_FLAGS_PARTSCAN && bdev) ioctl_by_bdev(bdev, BLKRRPART, 0); + lo->lo_flags = 0; + if (!part_shift) + lo->lo_disk->flags |= GENHD_FL_NO_PART_SCAN; mutex_unlock(&lo->lo_ctl_mutex); /* * Need not hold lo_ctl_mutex to fput backing file. @@ -1159,6 +1172,13 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info) (info->lo_flags & LO_FLAGS_AUTOCLEAR)) lo->lo_flags ^= LO_FLAGS_AUTOCLEAR; + if ((info->lo_flags & LO_FLAGS_PARTSCAN) && + !(lo->lo_flags & LO_FLAGS_PARTSCAN)) { + lo->lo_flags |= LO_FLAGS_PARTSCAN; + lo->lo_disk->flags &= ~GENHD_FL_NO_PART_SCAN; + ioctl_by_bdev(lo->lo_device, BLKRRPART, 0); + } + lo->lo_encrypt_key_size = info->lo_encrypt_key_size; lo->lo_init[0] = info->lo_init[0]; lo->lo_init[1] = info->lo_init[1]; @@ -1654,6 +1674,27 @@ static struct loop_device *loop_alloc(int i) if (!disk) goto out_free_queue; + /* + * Disable partition scanning by default. The in-kernel partition + * scanning can be requested individually per-device during its + * setup. Userspace can always add and remove partitions from all + * devices. The needed partition minors are allocated from the + * extended minor space, the main loop device numbers will continue + * to match the loop minors, regardless of the number of partitions + * used. + * + * If max_part is given, partition scanning is globally enabled for + * all loop devices. The minors for the main loop devices will be + * multiples of max_part. + * + * Note: Global-for-all-devices, set-only-at-init, read-only module + * parameteters like 'max_loop' and 'max_part' make things needlessly + * complicated, are too static, inflexible and may surprise + * userspace tools. Parameters like this in general should be avoided. + */ + if (!part_shift) + disk->flags |= GENHD_FL_NO_PART_SCAN; + disk->flags |= GENHD_FL_EXT_DEVT; mutex_init(&lo->lo_ctl_mutex); lo->lo_number = i; lo->lo_thread = NULL; diff --git a/include/linux/loop.h b/include/linux/loop.h index 66c194e..4367fc5 100644 --- a/include/linux/loop.h +++ b/include/linux/loop.h @@ -76,6 +76,7 @@ enum { LO_FLAGS_READ_ONLY = 1, LO_FLAGS_USE_AOPS = 2, LO_FLAGS_AUTOCLEAR = 4, + LO_FLAGS_PARTSCAN = 8, }; #include /* for __kernel_old_dev_t */ -- cgit v0.10.2 From 5a3a76e6c35fa236fc234d17a98edeb41d49130f Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Wed, 21 Sep 2011 10:02:13 +0200 Subject: drivers/block/cpqarray.c: use pci_dev->revision This driver uses PCI_CLASS_REVISION instead of PCI_REVISION_ID, so it wasn't converted by commit 44c10138fd4bbc4b6 ("PCI: Change all drivers to use pci_device->revision"). Signed-off-by: Sergei Shtylyov Acked-by: Mike Miller Cc: Chirag Kantharia Cc: Jens Axboe Signed-off-by: Andrew Morton Signed-off-by: Jens Axboe diff --git a/drivers/block/cpqarray.c b/drivers/block/cpqarray.c index b2fceb5..9125bbe 100644 --- a/drivers/block/cpqarray.c +++ b/drivers/block/cpqarray.c @@ -620,6 +620,7 @@ static int cpqarray_pci_init(ctlr_info_t *c, struct pci_dev *pdev) } vendor_id = pdev->vendor; device_id = pdev->device; + revision = pdev->revision; irq = pdev->irq; for(i=0; i<6; i++) @@ -632,7 +633,6 @@ static int cpqarray_pci_init(ctlr_info_t *c, struct pci_dev *pdev) } pci_read_config_word(pdev, PCI_COMMAND, &command); - pci_read_config_byte(pdev, PCI_CLASS_REVISION, &revision); pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &cache_line_size); pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &latency_timer); -- cgit v0.10.2 From 8a9c594422ecad912d6470888acdee9a1236ad68 Mon Sep 17 00:00:00 2001 From: Phillip Susi Date: Wed, 21 Sep 2011 10:02:13 +0200 Subject: drivers/block/loop.c: emit uevent on auto release The loopback driver failed to emit the change uevent when auto releasing the device. Fixed lo_release() to pass the bdev to loop_clr_fd() so it can emit the event. Signed-off-by: Phillip Susi Cc: Jens Axboe Cc: Ayan George Signed-off-by: Andrew Morton Signed-off-by: Jens Axboe diff --git a/drivers/block/loop.c b/drivers/block/loop.c index b336433..c2ce03c 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -1583,7 +1583,7 @@ static int lo_release(struct gendisk *disk, fmode_t mode) * In autoclear mode, stop the loop thread * and remove configuration after last close. */ - err = loop_clr_fd(lo, NULL); + err = loop_clr_fd(lo, lo->lo_device); if (!err) goto out_unlocked; } else { -- cgit v0.10.2 From 4c823cc3d568277aa6340d8df6981e34f4c4dee5 Mon Sep 17 00:00:00 2001 From: Ayan George Date: Wed, 21 Sep 2011 10:02:13 +0200 Subject: drivers/block/loop.c: remove unnecessary bdev argument from loop_clr_fd() If the loop device is associated (lo->lo_state == Lo_bound), it will have a valid bdev pointed to by lo->lo_device. There is no reason to ever pass an additional block_device pointer. Signed-off-by: Ayan George Cc: Phillip Susi Cc: Jens Axboe Signed-off-by: Andrew Morton Signed-off-by: Jens Axboe diff --git a/drivers/block/loop.c b/drivers/block/loop.c index c2ce03c..9b2f5d3 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -1051,10 +1051,11 @@ loop_init_xfer(struct loop_device *lo, struct loop_func_table *xfer, return err; } -static int loop_clr_fd(struct loop_device *lo, struct block_device *bdev) +static int loop_clr_fd(struct loop_device *lo) { struct file *filp = lo->lo_backing_file; gfp_t gfp = lo->old_gfp_mask; + struct block_device *bdev = lo->lo_device; if (lo->lo_state != Lo_bound) return -ENXIO; @@ -1372,7 +1373,7 @@ static int lo_ioctl(struct block_device *bdev, fmode_t mode, break; case LOOP_CLR_FD: /* loop_clr_fd would have unlocked lo_ctl_mutex on success */ - err = loop_clr_fd(lo, bdev); + err = loop_clr_fd(lo); if (!err) goto out_unlocked; break; @@ -1583,7 +1584,7 @@ static int lo_release(struct gendisk *disk, fmode_t mode) * In autoclear mode, stop the loop thread * and remove configuration after last close. */ - err = loop_clr_fd(lo, lo->lo_device); + err = loop_clr_fd(lo); if (!err) goto out_unlocked; } else { -- cgit v0.10.2 From 32a8d26cc9b96629269e04ee6c583e14398f6f47 Mon Sep 17 00:00:00 2001 From: Li Dongyang Date: Thu, 1 Sep 2011 18:39:08 +0800 Subject: xen-blkfront: add BLKIF_OP_DISCARD and discard request struct Now we use BLKIF_OP_DISCARD and add blkif_request_discard to blkif_request union, the patch is taken from Owen Smith and Konrad, Thanks Signed-off-by: Owen Smith Signed-off-by: Li Dongyang Signed-off-by: Konrad Rzeszutek Wilk diff --git a/include/xen/interface/io/blkif.h b/include/xen/interface/io/blkif.h index 3d5d6db..9324488 100644 --- a/include/xen/interface/io/blkif.h +++ b/include/xen/interface/io/blkif.h @@ -57,6 +57,36 @@ typedef uint64_t blkif_sector_t; * "feature-flush-cache" node! */ #define BLKIF_OP_FLUSH_DISKCACHE 3 + +/* + * Recognised only if "feature-discard" is present in backend xenbus info. + * The "feature-discard" node contains a boolean indicating whether trim + * (ATA) or unmap (SCSI) - conviently called discard requests are likely + * to succeed or fail. Either way, a discard request + * may fail at any time with BLKIF_RSP_EOPNOTSUPP if it is unsupported by + * the underlying block-device hardware. The boolean simply indicates whether + * or not it is worthwhile for the frontend to attempt discard requests. + * If a backend does not recognise BLKIF_OP_DISCARD, it should *not* + * create the "feature-discard" node! + * + * Discard operation is a request for the underlying block device to mark + * extents to be erased. However, discard does not guarantee that the blocks + * will be erased from the device - it is just a hint to the device + * controller that these blocks are no longer in use. What the device + * controller does with that information is left to the controller. + * Discard operations are passed with sector_number as the + * sector index to begin discard operations at and nr_sectors as the number of + * sectors to be discarded. The specified sectors should be discarded if the + * underlying block device supports trim (ATA) or unmap (SCSI) operations, + * or a BLKIF_RSP_EOPNOTSUPP should be returned. + * More information about trim/unmap operations at: + * http://t13.org/Documents/UploadedDocuments/docs2008/ + * e07154r6-Data_Set_Management_Proposal_for_ATA-ACS2.doc + * http://www.seagate.com/staticfiles/support/disc/manuals/ + * Interface%20manuals/100293068c.pdf + */ +#define BLKIF_OP_DISCARD 5 + /* * Maximum scatter/gather segments per request. * This is carefully chosen so that sizeof(struct blkif_ring) <= PAGE_SIZE. @@ -74,6 +104,11 @@ struct blkif_request_rw { } seg[BLKIF_MAX_SEGMENTS_PER_REQUEST]; }; +struct blkif_request_discard { + blkif_sector_t sector_number; + uint64_t nr_sectors; +}; + struct blkif_request { uint8_t operation; /* BLKIF_OP_??? */ uint8_t nr_segments; /* number of segments */ @@ -81,6 +116,7 @@ struct blkif_request { uint64_t id; /* private guest value, echoed in resp */ union { struct blkif_request_rw rw; + struct blkif_request_discard discard; } u; }; -- cgit v0.10.2 From b3cb0d6adc4bbc70b5e37e49a6068e973545ead7 Mon Sep 17 00:00:00 2001 From: Li Dongyang Date: Thu, 1 Sep 2011 18:39:10 +0800 Subject: xen-blkback: Implement discard requests ('feature-discard') ..aka ATA TRIM/SCSI UNMAP command to be passed through the frontend and used as appropiately by the backend. We also advertise certain granulity parameters to the frontend so it can plug them in. If the backend is a realy device - we just end up using 'blkdev_issue_discard' while for loopback devices - we just punch a hole in the image file. Signed-off-by: Li Dongyang [v1: Fixed up pr_debug and commit description] Signed-off-by: Konrad Rzeszutek Wilk diff --git a/drivers/block/xen-blkback/blkback.c b/drivers/block/xen-blkback/blkback.c index 2330a9a..9713d5a 100644 --- a/drivers/block/xen-blkback/blkback.c +++ b/drivers/block/xen-blkback/blkback.c @@ -39,6 +39,9 @@ #include #include #include +#include +#include +#include #include #include @@ -258,13 +261,16 @@ irqreturn_t xen_blkif_be_int(int irq, void *dev_id) static void print_stats(struct xen_blkif *blkif) { - pr_info("xen-blkback (%s): oo %3d | rd %4d | wr %4d | f %4d\n", + pr_info("xen-blkback (%s): oo %3d | rd %4d | wr %4d | f %4d" + " | ds %4d\n", current->comm, blkif->st_oo_req, - blkif->st_rd_req, blkif->st_wr_req, blkif->st_f_req); + blkif->st_rd_req, blkif->st_wr_req, + blkif->st_f_req, blkif->st_ds_req); blkif->st_print = jiffies + msecs_to_jiffies(10 * 1000); blkif->st_rd_req = 0; blkif->st_wr_req = 0; blkif->st_oo_req = 0; + blkif->st_ds_req = 0; } int xen_blkif_schedule(void *arg) @@ -410,6 +416,42 @@ static int xen_blkbk_map(struct blkif_request *req, return ret; } +static void xen_blk_discard(struct xen_blkif *blkif, struct blkif_request *req) +{ + int err = 0; + int status = BLKIF_RSP_OKAY; + struct block_device *bdev = blkif->vbd.bdev; + + if (blkif->blk_backend_type == BLKIF_BACKEND_PHY) + /* just forward the discard request */ + err = blkdev_issue_discard(bdev, + req->u.discard.sector_number, + req->u.discard.nr_sectors, + GFP_KERNEL, 0); + else if (blkif->blk_backend_type == BLKIF_BACKEND_FILE) { + /* punch a hole in the backing file */ + struct loop_device *lo = bdev->bd_disk->private_data; + struct file *file = lo->lo_backing_file; + + if (file->f_op->fallocate) + err = file->f_op->fallocate(file, + FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE, + req->u.discard.sector_number << 9, + req->u.discard.nr_sectors << 9); + else + err = -EOPNOTSUPP; + } else + err = -EOPNOTSUPP; + + if (err == -EOPNOTSUPP) { + pr_debug(DRV_PFX "discard op failed, not supported\n"); + status = BLKIF_RSP_EOPNOTSUPP; + } else if (err) + status = BLKIF_RSP_ERROR; + + make_response(blkif, req->id, req->operation, status); +} + /* * Completion callback on the bio's. Called as bh->b_end_io() */ @@ -563,6 +605,10 @@ static int dispatch_rw_block_io(struct xen_blkif *blkif, blkif->st_f_req++; operation = WRITE_FLUSH; break; + case BLKIF_OP_DISCARD: + blkif->st_ds_req++; + operation = REQ_DISCARD; + break; case BLKIF_OP_WRITE_BARRIER: default: operation = 0; /* make gcc happy */ @@ -572,7 +618,8 @@ static int dispatch_rw_block_io(struct xen_blkif *blkif, /* Check that the number of segments is sane. */ nseg = req->nr_segments; - if (unlikely(nseg == 0 && operation != WRITE_FLUSH) || + if (unlikely(nseg == 0 && operation != WRITE_FLUSH && + operation != REQ_DISCARD) || unlikely(nseg > BLKIF_MAX_SEGMENTS_PER_REQUEST)) { pr_debug(DRV_PFX "Bad number of segments in request (%d)\n", nseg); @@ -627,10 +674,14 @@ static int dispatch_rw_block_io(struct xen_blkif *blkif, * the hypercall to unmap the grants - that is all done in * xen_blkbk_unmap. */ - if (xen_blkbk_map(req, pending_req, seg)) + if (operation != BLKIF_OP_DISCARD && + xen_blkbk_map(req, pending_req, seg)) goto fail_flush; - /* This corresponding xen_blkif_put is done in __end_block_io_op */ + /* + * This corresponding xen_blkif_put is done in __end_block_io_op, or + * below (in "!bio") if we are handling a BLKIF_OP_DISCARD. + */ xen_blkif_get(blkif); for (i = 0; i < nseg; i++) { @@ -654,18 +705,25 @@ static int dispatch_rw_block_io(struct xen_blkif *blkif, preq.sector_number += seg[i].nsec; } - /* This will be hit if the operation was a flush. */ + /* This will be hit if the operation was a flush or discard. */ if (!bio) { - BUG_ON(operation != WRITE_FLUSH); + BUG_ON(operation != WRITE_FLUSH && operation != REQ_DISCARD); - bio = bio_alloc(GFP_KERNEL, 0); - if (unlikely(bio == NULL)) - goto fail_put_bio; + if (operation == WRITE_FLUSH) { + bio = bio_alloc(GFP_KERNEL, 0); + if (unlikely(bio == NULL)) + goto fail_put_bio; - biolist[nbio++] = bio; - bio->bi_bdev = preq.bdev; - bio->bi_private = pending_req; - bio->bi_end_io = end_block_io_op; + biolist[nbio++] = bio; + bio->bi_bdev = preq.bdev; + bio->bi_private = pending_req; + bio->bi_end_io = end_block_io_op; + } else if (operation == REQ_DISCARD) { + xen_blk_discard(blkif, req); + xen_blkif_put(blkif); + free_req(pending_req); + return 0; + } } /* diff --git a/drivers/block/xen-blkback/common.h b/drivers/block/xen-blkback/common.h index 9e40b28..bfb532e 100644 --- a/drivers/block/xen-blkback/common.h +++ b/drivers/block/xen-blkback/common.h @@ -63,13 +63,26 @@ struct blkif_common_response { /* i386 protocol version */ #pragma pack(push, 4) + +struct blkif_x86_32_request_rw { + blkif_sector_t sector_number;/* start sector idx on disk (r/w only) */ + struct blkif_request_segment seg[BLKIF_MAX_SEGMENTS_PER_REQUEST]; +}; + +struct blkif_x86_32_request_discard { + blkif_sector_t sector_number;/* start sector idx on disk (r/w only) */ + uint64_t nr_sectors; +}; + struct blkif_x86_32_request { uint8_t operation; /* BLKIF_OP_??? */ uint8_t nr_segments; /* number of segments */ blkif_vdev_t handle; /* only for read/write requests */ uint64_t id; /* private guest value, echoed in resp */ - blkif_sector_t sector_number;/* start sector idx on disk (r/w only) */ - struct blkif_request_segment seg[BLKIF_MAX_SEGMENTS_PER_REQUEST]; + union { + struct blkif_x86_32_request_rw rw; + struct blkif_x86_32_request_discard discard; + } u; }; struct blkif_x86_32_response { uint64_t id; /* copied from request */ @@ -79,13 +92,26 @@ struct blkif_x86_32_response { #pragma pack(pop) /* x86_64 protocol version */ + +struct blkif_x86_64_request_rw { + blkif_sector_t sector_number;/* start sector idx on disk (r/w only) */ + struct blkif_request_segment seg[BLKIF_MAX_SEGMENTS_PER_REQUEST]; +}; + +struct blkif_x86_64_request_discard { + blkif_sector_t sector_number;/* start sector idx on disk (r/w only) */ + uint64_t nr_sectors; +}; + struct blkif_x86_64_request { uint8_t operation; /* BLKIF_OP_??? */ uint8_t nr_segments; /* number of segments */ blkif_vdev_t handle; /* only for read/write requests */ uint64_t __attribute__((__aligned__(8))) id; - blkif_sector_t sector_number;/* start sector idx on disk (r/w only) */ - struct blkif_request_segment seg[BLKIF_MAX_SEGMENTS_PER_REQUEST]; + union { + struct blkif_x86_64_request_rw rw; + struct blkif_x86_64_request_discard discard; + } u; }; struct blkif_x86_64_response { uint64_t __attribute__((__aligned__(8))) id; @@ -113,6 +139,11 @@ enum blkif_protocol { BLKIF_PROTOCOL_X86_64 = 3, }; +enum blkif_backend_type { + BLKIF_BACKEND_PHY = 1, + BLKIF_BACKEND_FILE = 2, +}; + struct xen_vbd { /* What the domain refers to this vbd as. */ blkif_vdev_t handle; @@ -138,6 +169,7 @@ struct xen_blkif { unsigned int irq; /* Comms information. */ enum blkif_protocol blk_protocol; + enum blkif_backend_type blk_backend_type; union blkif_back_rings blk_rings; struct vm_struct *blk_ring_area; /* The VBD attached to this interface. */ @@ -159,6 +191,7 @@ struct xen_blkif { int st_wr_req; int st_oo_req; int st_f_req; + int st_ds_req; int st_rd_sect; int st_wr_sect; @@ -182,7 +215,7 @@ struct xen_blkif { struct phys_req { unsigned short dev; - unsigned short nr_sects; + blkif_sector_t nr_sects; struct block_device *bdev; blkif_sector_t sector_number; }; @@ -206,12 +239,25 @@ static inline void blkif_get_x86_32_req(struct blkif_request *dst, dst->nr_segments = src->nr_segments; dst->handle = src->handle; dst->id = src->id; - dst->u.rw.sector_number = src->sector_number; - barrier(); - if (n > dst->nr_segments) - n = dst->nr_segments; - for (i = 0; i < n; i++) - dst->u.rw.seg[i] = src->seg[i]; + switch (src->operation) { + case BLKIF_OP_READ: + case BLKIF_OP_WRITE: + case BLKIF_OP_WRITE_BARRIER: + case BLKIF_OP_FLUSH_DISKCACHE: + dst->u.rw.sector_number = src->u.rw.sector_number; + barrier(); + if (n > dst->nr_segments) + n = dst->nr_segments; + for (i = 0; i < n; i++) + dst->u.rw.seg[i] = src->u.rw.seg[i]; + break; + case BLKIF_OP_DISCARD: + dst->u.discard.sector_number = src->u.discard.sector_number; + dst->u.discard.nr_sectors = src->u.discard.nr_sectors; + break; + default: + break; + } } static inline void blkif_get_x86_64_req(struct blkif_request *dst, @@ -222,12 +268,25 @@ static inline void blkif_get_x86_64_req(struct blkif_request *dst, dst->nr_segments = src->nr_segments; dst->handle = src->handle; dst->id = src->id; - dst->u.rw.sector_number = src->sector_number; - barrier(); - if (n > dst->nr_segments) - n = dst->nr_segments; - for (i = 0; i < n; i++) - dst->u.rw.seg[i] = src->seg[i]; + switch (src->operation) { + case BLKIF_OP_READ: + case BLKIF_OP_WRITE: + case BLKIF_OP_WRITE_BARRIER: + case BLKIF_OP_FLUSH_DISKCACHE: + dst->u.rw.sector_number = src->u.rw.sector_number; + barrier(); + if (n > dst->nr_segments) + n = dst->nr_segments; + for (i = 0; i < n; i++) + dst->u.rw.seg[i] = src->u.rw.seg[i]; + break; + case BLKIF_OP_DISCARD: + dst->u.discard.sector_number = src->u.discard.sector_number; + dst->u.discard.nr_sectors = src->u.discard.nr_sectors; + break; + default: + break; + } } #endif /* __XEN_BLKIF__BACKEND__COMMON_H__ */ diff --git a/drivers/block/xen-blkback/xenbus.c b/drivers/block/xen-blkback/xenbus.c index 3f129b4..2b3aef0 100644 --- a/drivers/block/xen-blkback/xenbus.c +++ b/drivers/block/xen-blkback/xenbus.c @@ -272,6 +272,7 @@ VBD_SHOW(oo_req, "%d\n", be->blkif->st_oo_req); VBD_SHOW(rd_req, "%d\n", be->blkif->st_rd_req); VBD_SHOW(wr_req, "%d\n", be->blkif->st_wr_req); VBD_SHOW(f_req, "%d\n", be->blkif->st_f_req); +VBD_SHOW(ds_req, "%d\n", be->blkif->st_ds_req); VBD_SHOW(rd_sect, "%d\n", be->blkif->st_rd_sect); VBD_SHOW(wr_sect, "%d\n", be->blkif->st_wr_sect); @@ -280,6 +281,7 @@ static struct attribute *xen_vbdstat_attrs[] = { &dev_attr_rd_req.attr, &dev_attr_wr_req.attr, &dev_attr_f_req.attr, + &dev_attr_ds_req.attr, &dev_attr_rd_sect.attr, &dev_attr_wr_sect.attr, NULL @@ -419,6 +421,60 @@ int xen_blkbk_flush_diskcache(struct xenbus_transaction xbt, return err; } +int xen_blkbk_discard(struct xenbus_transaction xbt, struct backend_info *be) +{ + struct xenbus_device *dev = be->dev; + struct xen_blkif *blkif = be->blkif; + char *type; + int err; + int state = 0; + + type = xenbus_read(XBT_NIL, dev->nodename, "type", NULL); + if (!IS_ERR(type)) { + if (strncmp(type, "file", 4) == 0) { + state = 1; + blkif->blk_backend_type = BLKIF_BACKEND_FILE; + } + if (strncmp(type, "phy", 3) == 0) { + struct block_device *bdev = be->blkif->vbd.bdev; + struct request_queue *q = bdev_get_queue(bdev); + if (blk_queue_discard(q)) { + err = xenbus_printf(xbt, dev->nodename, + "discard-granularity", "%u", + q->limits.discard_granularity); + if (err) { + xenbus_dev_fatal(dev, err, + "writing discard-granularity"); + goto kfree; + } + err = xenbus_printf(xbt, dev->nodename, + "discard-alignment", "%u", + q->limits.discard_alignment); + if (err) { + xenbus_dev_fatal(dev, err, + "writing discard-alignment"); + goto kfree; + } + state = 1; + blkif->blk_backend_type = BLKIF_BACKEND_PHY; + } + } + } else { + err = PTR_ERR(type); + xenbus_dev_fatal(dev, err, "reading type"); + goto out; + } + + err = xenbus_printf(xbt, dev->nodename, "feature-discard", + "%d", state); + if (err) + xenbus_dev_fatal(dev, err, "writing feature-discard"); +kfree: + kfree(type); +out: + return err; +} + /* * Entry point to this code when a new device is created. Allocate the basic * structures, and watch the store waiting for the hotplug scripts to tell us @@ -650,6 +706,8 @@ again: if (err) goto abort; + err = xen_blkbk_discard(xbt, be); + err = xenbus_printf(xbt, dev->nodename, "sectors", "%llu", (unsigned long long)vbd_sz(&be->blkif->vbd)); if (err) { -- cgit v0.10.2 From ed30bf317c5ceb25166cdbce3e0b35e33c82b509 Mon Sep 17 00:00:00 2001 From: Li Dongyang Date: Thu, 1 Sep 2011 18:39:09 +0800 Subject: xen-blkfront: Handle discard requests. If the backend advertises 'feature-discard', then interrogate the backend for alignment and granularity. Setup the request queue with the appropiate values and send the discard operation as required. Signed-off-by: Li Dongyang [v1: Amended commit description] Signed-off-by: Konrad Rzeszutek Wilk diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c index b536a9c..52076b0 100644 --- a/drivers/block/xen-blkfront.c +++ b/drivers/block/xen-blkfront.c @@ -98,6 +98,9 @@ struct blkfront_info unsigned long shadow_free; unsigned int feature_flush; unsigned int flush_op; + unsigned int feature_discard; + unsigned int discard_granularity; + unsigned int discard_alignment; int is_ready; }; @@ -302,29 +305,36 @@ static int blkif_queue_request(struct request *req) ring_req->operation = info->flush_op; } - ring_req->nr_segments = blk_rq_map_sg(req->q, req, info->sg); - BUG_ON(ring_req->nr_segments > BLKIF_MAX_SEGMENTS_PER_REQUEST); + if (unlikely(req->cmd_flags & REQ_DISCARD)) { + /* id, sector_number and handle are set above. */ + ring_req->operation = BLKIF_OP_DISCARD; + ring_req->nr_segments = 0; + ring_req->u.discard.nr_sectors = blk_rq_sectors(req); + } else { + ring_req->nr_segments = blk_rq_map_sg(req->q, req, info->sg); + BUG_ON(ring_req->nr_segments > BLKIF_MAX_SEGMENTS_PER_REQUEST); - for_each_sg(info->sg, sg, ring_req->nr_segments, i) { - buffer_mfn = pfn_to_mfn(page_to_pfn(sg_page(sg))); - fsect = sg->offset >> 9; - lsect = fsect + (sg->length >> 9) - 1; - /* install a grant reference. */ - ref = gnttab_claim_grant_reference(&gref_head); - BUG_ON(ref == -ENOSPC); + for_each_sg(info->sg, sg, ring_req->nr_segments, i) { + buffer_mfn = pfn_to_mfn(page_to_pfn(sg_page(sg))); + fsect = sg->offset >> 9; + lsect = fsect + (sg->length >> 9) - 1; + /* install a grant reference. */ + ref = gnttab_claim_grant_reference(&gref_head); + BUG_ON(ref == -ENOSPC); - gnttab_grant_foreign_access_ref( - ref, - info->xbdev->otherend_id, - buffer_mfn, - rq_data_dir(req) ); - - info->shadow[id].frame[i] = mfn_to_pfn(buffer_mfn); - ring_req->u.rw.seg[i] = - (struct blkif_request_segment) { - .gref = ref, - .first_sect = fsect, - .last_sect = lsect }; + gnttab_grant_foreign_access_ref( + ref, + info->xbdev->otherend_id, + buffer_mfn, + rq_data_dir(req)); + + info->shadow[id].frame[i] = mfn_to_pfn(buffer_mfn); + ring_req->u.rw.seg[i] = + (struct blkif_request_segment) { + .gref = ref, + .first_sect = fsect, + .last_sect = lsect }; + } } info->ring.req_prod_pvt++; @@ -399,6 +409,7 @@ wait: static int xlvbd_init_blk_queue(struct gendisk *gd, u16 sector_size) { struct request_queue *rq; + struct blkfront_info *info = gd->private_data; rq = blk_init_queue(do_blkif_request, &blkif_io_lock); if (rq == NULL) @@ -406,6 +417,13 @@ static int xlvbd_init_blk_queue(struct gendisk *gd, u16 sector_size) queue_flag_set_unlocked(QUEUE_FLAG_VIRT, rq); + if (info->feature_discard) { + queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, rq); + blk_queue_max_discard_sectors(rq, get_capacity(gd)); + rq->limits.discard_granularity = info->discard_granularity; + rq->limits.discard_alignment = info->discard_alignment; + } + /* Hard sector size and max sectors impersonate the equiv. hardware. */ blk_queue_logical_block_size(rq, sector_size); blk_queue_max_hw_sectors(rq, 512); @@ -722,6 +740,19 @@ static irqreturn_t blkif_interrupt(int irq, void *dev_id) error = (bret->status == BLKIF_RSP_OKAY) ? 0 : -EIO; switch (bret->operation) { + case BLKIF_OP_DISCARD: + if (unlikely(bret->status == BLKIF_RSP_EOPNOTSUPP)) { + struct request_queue *rq = info->rq; + printk(KERN_WARNING "blkfront: %s: discard op failed\n", + info->gd->disk_name); + error = -EOPNOTSUPP; + info->feature_discard = 0; + spin_lock(rq->queue_lock); + queue_flag_clear(QUEUE_FLAG_DISCARD, rq); + spin_unlock(rq->queue_lock); + } + __blk_end_request_all(req, error); + break; case BLKIF_OP_FLUSH_DISKCACHE: case BLKIF_OP_WRITE_BARRIER: if (unlikely(bret->status == BLKIF_RSP_EOPNOTSUPP)) { @@ -1098,6 +1129,33 @@ blkfront_closing(struct blkfront_info *info) bdput(bdev); } +static void blkfront_setup_discard(struct blkfront_info *info) +{ + int err; + char *type; + unsigned int discard_granularity; + unsigned int discard_alignment; + + type = xenbus_read(XBT_NIL, info->xbdev->otherend, "type", NULL); + if (IS_ERR(type)) + return; + + if (strncmp(type, "phy", 3) == 0) { + err = xenbus_gather(XBT_NIL, info->xbdev->otherend, + "discard-granularity", "%u", &discard_granularity, + "discard-alignment", "%u", &discard_alignment, + NULL); + if (!err) { + info->feature_discard = 1; + info->discard_granularity = discard_granularity; + info->discard_alignment = discard_alignment; + } + } else if (strncmp(type, "file", 4) == 0) + info->feature_discard = 1; + + kfree(type); +} + /* * Invoked when the backend is finally 'ready' (and has told produced * the details about the physical device - #sectors, size, etc). @@ -1108,7 +1166,7 @@ static void blkfront_connect(struct blkfront_info *info) unsigned long sector_size; unsigned int binfo; int err; - int barrier, flush; + int barrier, flush, discard; switch (info->connected) { case BLKIF_STATE_CONNECTED: @@ -1178,7 +1236,14 @@ static void blkfront_connect(struct blkfront_info *info) info->feature_flush = REQ_FLUSH; info->flush_op = BLKIF_OP_FLUSH_DISKCACHE; } - + + err = xenbus_gather(XBT_NIL, info->xbdev->otherend, + "feature-discard", "%d", &discard, + NULL); + + if (!err && discard) + blkfront_setup_discard(info); + err = xlvbd_alloc_gendisk(sectors, info, binfo, sector_size); if (err) { xenbus_dev_fatal(info->xbdev, err, "xlvbd_add at %s", -- cgit v0.10.2 From 69ef68cef961a934c35308843a04b26d91cad4a8 Mon Sep 17 00:00:00 2001 From: Li Dongyang Date: Wed, 14 Sep 2011 14:02:40 +0800 Subject: xen-blkfront: fix a deadlock while handling discard response When we get -EOPNOTSUPP response for a discard request, we will clear the discard flag on the request queue so we won't attempt to send discard requests to backend again, and this should be protected under rq->queue_lock. However, when we setup the request queue, we pass blkif_io_lock to blk_init_queue so rq->queue_lock is blkif_io_lock indeed, and this lock is already taken when we are in blkif_interrpt, so remove the spin_lock/spin_unlock when we clear the discard flag or we will end up with deadlock here Signed-off-by: Li Dongyang [v1: Updated description a bit and removed comment from source] Signed-off-by: Konrad Rzeszutek Wilk diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c index 52076b0..7d14877 100644 --- a/drivers/block/xen-blkfront.c +++ b/drivers/block/xen-blkfront.c @@ -747,9 +747,7 @@ static irqreturn_t blkif_interrupt(int irq, void *dev_id) info->gd->disk_name); error = -EOPNOTSUPP; info->feature_discard = 0; - spin_lock(rq->queue_lock); queue_flag_clear(QUEUE_FLAG_DISCARD, rq); - spin_unlock(rq->queue_lock); } __blk_end_request_all(req, error); break; -- cgit v0.10.2 From c555aab97de139ac8762c922248bb68f43a8c488 Mon Sep 17 00:00:00 2001 From: Joe Jin Date: Mon, 15 Aug 2011 12:57:07 +0800 Subject: xen-blkback: fixed indentation and comments This patch fixes belows: 1. Fix code style issue. 2. Fix incorrect functions name in comments. Signed-off-by: Joe Jin Cc: Jens Axboe Cc: Ian Campbell Signed-off-by: Konrad Rzeszutek Wilk diff --git a/drivers/block/xen-blkback/common.h b/drivers/block/xen-blkback/common.h index bfb532e..1b1bc44 100644 --- a/drivers/block/xen-blkback/common.h +++ b/drivers/block/xen-blkback/common.h @@ -46,7 +46,7 @@ #define DRV_PFX "xen-blkback:" #define DPRINTK(fmt, args...) \ - pr_debug(DRV_PFX "(%s:%d) " fmt ".\n", \ + pr_debug(DRV_PFX "(%s:%d) " fmt ".\n", \ __func__, __LINE__, ##args) diff --git a/drivers/block/xen-blkback/xenbus.c b/drivers/block/xen-blkback/xenbus.c index 2b3aef0..1c44b32 100644 --- a/drivers/block/xen-blkback/xenbus.c +++ b/drivers/block/xen-blkback/xenbus.c @@ -646,7 +646,7 @@ static void frontend_changed(struct xenbus_device *dev, /* * Enforce precondition before potential leak point. - * blkif_disconnect() is idempotent. + * xen_blkif_disconnect() is idempotent. */ xen_blkif_disconnect(be->blkif); @@ -667,7 +667,7 @@ static void frontend_changed(struct xenbus_device *dev, break; /* fall through if not online */ case XenbusStateUnknown: - /* implies blkif_disconnect() via blkback_remove() */ + /* implies xen_blkif_disconnect() via xen_blkbk_remove() */ device_unregister(&dev->dev); break; -- cgit v0.10.2 From 8e6dc6fe51957116d363204a725c1262b4b78e19 Mon Sep 17 00:00:00 2001 From: Jan Beulich Date: Fri, 16 Sep 2011 08:38:09 +0100 Subject: xen-blkback: use kzalloc() in favor of kmalloc()+memset() This fixes the problem of three of those four memset()-s having improper size arguments passed: Sizeof a pointer-typed expression returns the size of the pointer, not that of the pointed to data. It also reverts using kmalloc() instead of kzalloc() for the allocation of the pending grant handles array, as that array gets fully initialized in a subsequent loop. Reported-by: Julia Lawall Signed-off-by: Jan Beulich Signed-off-by: Konrad Rzeszutek Wilk diff --git a/drivers/block/xen-blkback/blkback.c b/drivers/block/xen-blkback/blkback.c index 9713d5a..e0dab61 100644 --- a/drivers/block/xen-blkback/blkback.c +++ b/drivers/block/xen-blkback/blkback.c @@ -823,9 +823,9 @@ static int __init xen_blkif_init(void) mmap_pages = xen_blkif_reqs * BLKIF_MAX_SEGMENTS_PER_REQUEST; - blkbk->pending_reqs = kmalloc(sizeof(blkbk->pending_reqs[0]) * + blkbk->pending_reqs = kzalloc(sizeof(blkbk->pending_reqs[0]) * xen_blkif_reqs, GFP_KERNEL); - blkbk->pending_grant_handles = kzalloc(sizeof(blkbk->pending_grant_handles[0]) * + blkbk->pending_grant_handles = kmalloc(sizeof(blkbk->pending_grant_handles[0]) * mmap_pages, GFP_KERNEL); blkbk->pending_pages = kzalloc(sizeof(blkbk->pending_pages[0]) * mmap_pages, GFP_KERNEL); @@ -848,8 +848,6 @@ static int __init xen_blkif_init(void) if (rc) goto failed_init; - memset(blkbk->pending_reqs, 0, sizeof(blkbk->pending_reqs)); - INIT_LIST_HEAD(&blkbk->pending_free); spin_lock_init(&blkbk->pending_free_lock); init_waitqueue_head(&blkbk->pending_free_wq); -- cgit v0.10.2 From d11e6158307bed3f178399a4e6216eec67d16200 Mon Sep 17 00:00:00 2001 From: Konrad Rzeszutek Wilk Date: Fri, 16 Sep 2011 15:15:14 -0400 Subject: xen-blkfront: If no barrier or flush is supported, use invalid operation. Guard against issuing BLKIF_OP_WRITE_BARRIER or BLKIF_OP_FLUSH_CACHE by checking whether we successfully negotiated with the backend. The negotiation with the backend also sets the q->flush_flags which fortunately for us is also used when submitting an bio to us. If we don't support barriers or flushes it would be set to zero so we should never end up having to deal with REQ_FLUSH | REQ_FUA. However, other third party implementations of __make_request that might be stacked on top of us might not be so smart, so lets fix this up. Acked-by: Jan Beulich Signed-off-by: Konrad Rzeszutek Wilk diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c index 7d14877..8bf0cb7 100644 --- a/drivers/block/xen-blkfront.c +++ b/drivers/block/xen-blkfront.c @@ -380,7 +380,9 @@ static void do_blkif_request(struct request_queue *rq) blk_start_request(req); - if (req->cmd_type != REQ_TYPE_FS) { + if ((req->cmd_type != REQ_TYPE_FS) || + ((req->cmd_flags & (REQ_FLUSH | REQ_FUA)) && + !info->flush_op)) { __blk_end_request_all(req, -EIO); continue; } -- cgit v0.10.2 From 469738e675524b6aa029ecd46bdda3f878b12eff Mon Sep 17 00:00:00 2001 From: Laszlo Ersek Date: Fri, 7 Oct 2011 21:34:38 +0200 Subject: xen-blkfront: plug device number leak in xlblk_init() error path ... though after a failed xenbus_register_frontend() all may be lost. Acked-by: Ian Campbell Signed-off-by: Laszlo Ersek Signed-off-by: Konrad Rzeszutek Wilk diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c index 8bf0cb7..773da7d 100644 --- a/drivers/block/xen-blkfront.c +++ b/drivers/block/xen-blkfront.c @@ -1450,6 +1450,8 @@ static struct xenbus_driver blkfront = { static int __init xlblk_init(void) { + int ret; + if (!xen_domain()) return -ENODEV; @@ -1459,7 +1461,13 @@ static int __init xlblk_init(void) return -ENODEV; } - return xenbus_register_frontend(&blkfront); + ret = xenbus_register_frontend(&blkfront); + if (ret) { + unregister_blkdev(XENVBD_MAJOR, DEV_NAME); + return ret; + } + + return 0; } module_init(xlblk_init); -- cgit v0.10.2 From 29bde093787f3bdf7b9b4270ada6be7c8076e36b Mon Sep 17 00:00:00 2001 From: Konrad Rzeszutek Wilk Date: Mon, 10 Oct 2011 00:42:22 -0400 Subject: xen/blkback: Support 'feature-barrier' aka old-style BARRIER requests. We emulate the barrier requests by draining the outstanding bio's and then sending the WRITE_FLUSH command. To drain the I/Os we use the refcnt that is used during disconnect to wait for all the I/Os before disconnecting from the frontend. We latch on its value and if it reaches either the threshold for disconnect or when there are no more outstanding I/Os, then we have drained all I/Os. Suggested-by: Christopher Hellwig Signed-off-by: Konrad Rzeszutek Wilk diff --git a/drivers/block/xen-blkback/blkback.c b/drivers/block/xen-blkback/blkback.c index e0dab61..184b133 100644 --- a/drivers/block/xen-blkback/blkback.c +++ b/drivers/block/xen-blkback/blkback.c @@ -452,6 +452,23 @@ static void xen_blk_discard(struct xen_blkif *blkif, struct blkif_request *req) make_response(blkif, req->id, req->operation, status); } +static void xen_blk_drain_io(struct xen_blkif *blkif) +{ + atomic_set(&blkif->drain, 1); + do { + wait_for_completion_interruptible_timeout( + &blkif->drain_complete, HZ); + + if (!atomic_read(&blkif->drain)) + break; + /* The initial value is one, and one refcnt taken at the + * start of the xen_blkif_schedule thread. */ + if (atomic_read(&blkif->refcnt) <= 2) + break; + } while (!kthread_should_stop()); + atomic_set(&blkif->drain, 0); +} + /* * Completion callback on the bio's. Called as bh->b_end_io() */ @@ -464,6 +481,11 @@ static void __end_block_io_op(struct pending_req *pending_req, int error) pr_debug(DRV_PFX "flush diskcache op failed, not supported\n"); xen_blkbk_flush_diskcache(XBT_NIL, pending_req->blkif->be, 0); pending_req->status = BLKIF_RSP_EOPNOTSUPP; + } else if ((pending_req->operation == BLKIF_OP_WRITE_BARRIER) && + (error == -EOPNOTSUPP)) { + pr_debug(DRV_PFX "write barrier op failed, not supported\n"); + xen_blkbk_barrier(XBT_NIL, pending_req->blkif->be, 0); + pending_req->status = BLKIF_RSP_EOPNOTSUPP; } else if (error) { pr_debug(DRV_PFX "Buffer not up-to-date at end of operation," " error=%d\n", error); @@ -481,6 +503,10 @@ static void __end_block_io_op(struct pending_req *pending_req, int error) pending_req->operation, pending_req->status); xen_blkif_put(pending_req->blkif); free_req(pending_req); + if (atomic_read(&pending_req->blkif->refcnt) <= 2) { + if (atomic_read(&pending_req->blkif->drain)) + complete(&pending_req->blkif->drain_complete); + } } } @@ -574,7 +600,6 @@ do_block_io_op(struct xen_blkif *blkif) return more_to_do; } - /* * Transmutation of the 'struct blkif_request' to a proper 'struct bio' * and call the 'submit_bio' to pass it to the underlying storage. @@ -591,6 +616,7 @@ static int dispatch_rw_block_io(struct xen_blkif *blkif, int i, nbio = 0; int operation; struct blk_plug plug; + bool drain = false; switch (req->operation) { case BLKIF_OP_READ: @@ -601,6 +627,8 @@ static int dispatch_rw_block_io(struct xen_blkif *blkif, blkif->st_wr_req++; operation = WRITE_ODIRECT; break; + case BLKIF_OP_WRITE_BARRIER: + drain = true; case BLKIF_OP_FLUSH_DISKCACHE: blkif->st_f_req++; operation = WRITE_FLUSH; @@ -609,7 +637,6 @@ static int dispatch_rw_block_io(struct xen_blkif *blkif, blkif->st_ds_req++; operation = REQ_DISCARD; break; - case BLKIF_OP_WRITE_BARRIER: default: operation = 0; /* make gcc happy */ goto fail_response; @@ -668,6 +695,12 @@ static int dispatch_rw_block_io(struct xen_blkif *blkif, } } + /* Wait on all outstanding I/O's and once that has been completed + * issue the WRITE_FLUSH. + */ + if (drain) + xen_blk_drain_io(pending_req->blkif); + /* * If we have failed at this point, we need to undo the M2P override, * set gnttab_set_unmap_op on all of the grant references and perform diff --git a/drivers/block/xen-blkback/common.h b/drivers/block/xen-blkback/common.h index 1b1bc44..e638457 100644 --- a/drivers/block/xen-blkback/common.h +++ b/drivers/block/xen-blkback/common.h @@ -181,6 +181,9 @@ struct xen_blkif { atomic_t refcnt; wait_queue_head_t wq; + /* for barrier (drain) requests */ + struct completion drain_complete; + atomic_t drain; /* One thread per one blkif. */ struct task_struct *xenblkd; unsigned int waiting_reqs; @@ -229,6 +232,8 @@ int xen_blkif_schedule(void *arg); int xen_blkbk_flush_diskcache(struct xenbus_transaction xbt, struct backend_info *be, int state); +int xen_blkbk_barrier(struct xenbus_transaction xbt, + struct backend_info *be, int state); struct xenbus_device *xen_blkbk_xenbus(struct backend_info *be); static inline void blkif_get_x86_32_req(struct blkif_request *dst, diff --git a/drivers/block/xen-blkback/xenbus.c b/drivers/block/xen-blkback/xenbus.c index 1c44b32..a6d4303 100644 --- a/drivers/block/xen-blkback/xenbus.c +++ b/drivers/block/xen-blkback/xenbus.c @@ -114,6 +114,8 @@ static struct xen_blkif *xen_blkif_alloc(domid_t domid) spin_lock_init(&blkif->blk_ring_lock); atomic_set(&blkif->refcnt, 1); init_waitqueue_head(&blkif->wq); + init_completion(&blkif->drain_complete); + atomic_set(&blkif->drain, 0); blkif->st_print = jiffies; init_waitqueue_head(&blkif->waiting_to_free); @@ -474,6 +476,19 @@ kfree: out: return err; } +int xen_blkbk_barrier(struct xenbus_transaction xbt, + struct backend_info *be, int state) +{ + struct xenbus_device *dev = be->dev; + int err; + + err = xenbus_printf(xbt, dev->nodename, "feature-barrier", + "%d", state); + if (err) + xenbus_dev_fatal(dev, err, "writing feature-barrier"); + + return err; +} /* * Entry point to this code when a new device is created. Allocate the basic @@ -708,6 +723,9 @@ again: err = xen_blkbk_discard(xbt, be); + /* If we can't advertise it is OK. */ + err = xen_blkbk_barrier(xbt, be, be->blkif->vbd.flush_support); + err = xenbus_printf(xbt, dev->nodename, "sectors", "%llu", (unsigned long long)vbd_sz(&be->blkif->vbd)); if (err) { -- cgit v0.10.2 From 5c62cb48602dba95159c81ffeca179d3852e25be Mon Sep 17 00:00:00 2001 From: Konrad Rzeszutek Wilk Date: Mon, 10 Oct 2011 12:33:21 -0400 Subject: xen/blkback: Report VBD_WSECT (wr_sect) properly. We did not increment the amount of sectors written to disk b/c we tested for the == WRITE which is incorrect - as the operations are more of WRITE_FLUSH, WRITE_ODIRECT. This patch fixes it by doing a & WRITE check. CC: stable@kernel.org Reported-by: Andy Burns Suggested-by: Ian Campbell Signed-off-by: Konrad Rzeszutek Wilk diff --git a/drivers/block/xen-blkback/blkback.c b/drivers/block/xen-blkback/blkback.c index 184b133..c15c559 100644 --- a/drivers/block/xen-blkback/blkback.c +++ b/drivers/block/xen-blkback/blkback.c @@ -776,7 +776,7 @@ static int dispatch_rw_block_io(struct xen_blkif *blkif, if (operation == READ) blkif->st_rd_sect += preq.nr_sects; - else if (operation == WRITE || operation == WRITE_FLUSH) + else if (operation & WRITE) blkif->st_wr_sect += preq.nr_sects; return 0; -- cgit v0.10.2 From 64391b2536ca92f9c589b2bfeaca3954896fe057 Mon Sep 17 00:00:00 2001 From: Konrad Rzeszutek Wilk Date: Mon, 10 Oct 2011 00:47:49 -0400 Subject: xen/blkback: Fix the inhibition to map pages when discarding sector ranges. The 'operation' parameters are the ones provided to the bio layer while the req->operation are the ones passed in between the backend and frontend. We used the wrong 'operation' value to squash the call to map pages when processing the discard operation resulting in an hypercall that did nothing. Lets guard against going in the mapping function by checking for the proper operation type. CC: Li Dongyang Signed-off-by: Konrad Rzeszutek Wilk diff --git a/drivers/block/xen-blkback/blkback.c b/drivers/block/xen-blkback/blkback.c index c15c559..53c81de 100644 --- a/drivers/block/xen-blkback/blkback.c +++ b/drivers/block/xen-blkback/blkback.c @@ -707,8 +707,7 @@ static int dispatch_rw_block_io(struct xen_blkif *blkif, * the hypercall to unmap the grants - that is all done in * xen_blkbk_unmap. */ - if (operation != BLKIF_OP_DISCARD && - xen_blkbk_map(req, pending_req, seg)) + if (operation == REQ_DISCARD && xen_blkbk_map(req, pending_req, seg)) goto fail_flush; /* -- cgit v0.10.2 From dda1852802a6cc6fdecb9021e491b2de680c76b9 Mon Sep 17 00:00:00 2001 From: Konrad Rzeszutek Wilk Date: Fri, 14 Oct 2011 12:13:05 -0400 Subject: xen/blkback: Check for proper operation. The patch titled: "xen/blkback: Fix the inhibition to map pages when discarding sector ranges." had the right idea except that it used the wrong comparison operator. It had == instead of !=. This fixes the bug where all (except discard) operations would have been ignored. Signed-off-by: Konrad Rzeszutek Wilk diff --git a/drivers/block/xen-blkback/blkback.c b/drivers/block/xen-blkback/blkback.c index 53c81de..a1ee265 100644 --- a/drivers/block/xen-blkback/blkback.c +++ b/drivers/block/xen-blkback/blkback.c @@ -707,7 +707,7 @@ static int dispatch_rw_block_io(struct xen_blkif *blkif, * the hypercall to unmap the grants - that is all done in * xen_blkbk_unmap. */ - if (operation == REQ_DISCARD && xen_blkbk_map(req, pending_req, seg)) + if (operation != REQ_DISCARD && xen_blkbk_map(req, pending_req, seg)) goto fail_flush; /* -- cgit v0.10.2 From 6927d92091df2848fc0e6a693a017d4b2df549c2 Mon Sep 17 00:00:00 2001 From: Konrad Rzeszutek Wilk Date: Mon, 17 Oct 2011 14:27:48 -0400 Subject: xen/blkback: Fix two races in the handling of barrier requests. There are two windows of opportunity to cause a race when processing a barrier request. This patch fixes this. Signed-off-by: Konrad Rzeszutek Wilk diff --git a/drivers/block/xen-blkback/blkback.c b/drivers/block/xen-blkback/blkback.c index a1ee265..79efec2 100644 --- a/drivers/block/xen-blkback/blkback.c +++ b/drivers/block/xen-blkback/blkback.c @@ -456,15 +456,15 @@ static void xen_blk_drain_io(struct xen_blkif *blkif) { atomic_set(&blkif->drain, 1); do { + /* The initial value is one, and one refcnt taken at the + * start of the xen_blkif_schedule thread. */ + if (atomic_read(&blkif->refcnt) <= 2) + break; wait_for_completion_interruptible_timeout( &blkif->drain_complete, HZ); if (!atomic_read(&blkif->drain)) break; - /* The initial value is one, and one refcnt taken at the - * start of the xen_blkif_schedule thread. */ - if (atomic_read(&blkif->refcnt) <= 2) - break; } while (!kthread_should_stop()); atomic_set(&blkif->drain, 0); } @@ -502,11 +502,11 @@ static void __end_block_io_op(struct pending_req *pending_req, int error) make_response(pending_req->blkif, pending_req->id, pending_req->operation, pending_req->status); xen_blkif_put(pending_req->blkif); - free_req(pending_req); if (atomic_read(&pending_req->blkif->refcnt) <= 2) { if (atomic_read(&pending_req->blkif->drain)) complete(&pending_req->blkif->drain_complete); } + free_req(pending_req); } } -- cgit v0.10.2 From ab5dbebe33e0c353e8545f09c34553ac3351dad6 Mon Sep 17 00:00:00 2001 From: Mike Miller Date: Thu, 20 Oct 2011 22:19:17 +0200 Subject: cciss: add small delay when using PCI Power Management to reset for kump The P600 requires a small delay when changing states. Otherwise we may think the board did not reset and we bail. This for kdump only and is particular to the P600. Cc: stable@kernel.org Signed-off-by: Jens Axboe diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index 6da7ede..486f94e 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -4557,6 +4557,13 @@ static int cciss_controller_hard_reset(struct pci_dev *pdev, pmcsr &= ~PCI_PM_CTRL_STATE_MASK; pmcsr |= PCI_D0; pci_write_config_word(pdev, pos + PCI_PM_CTRL, pmcsr); + + /* + * The P600 requires a small delay when changing states. + * Otherwise we may think the board did not reset and we bail. + * This for kdump only and is particular to the P600. + */ + msleep(500); } return 0; } -- cgit v0.10.2 From c4853efec665134b2e6fc9c13447323240980351 Mon Sep 17 00:00:00 2001 From: Mike Miller Date: Fri, 21 Oct 2011 08:19:43 +0200 Subject: hpsa: add small delay when using PCI Power Management to reset for kump The P600 requires a small delay when changing states. Otherwise we may think the board did not reset and we bail. This for kdump only and is particular to the P600. Signed-off-by: Mike Miller Cc: stable@kernel.org Signed-off-by: Jens Axboe diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index ec61bdb..3819298 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c @@ -3283,6 +3283,13 @@ static int hpsa_controller_hard_reset(struct pci_dev *pdev, pmcsr &= ~PCI_PM_CTRL_STATE_MASK; pmcsr |= PCI_D0; pci_write_config_word(pdev, pos + PCI_PM_CTRL, pmcsr); + + /* + * The P600 requires a small delay when changing states. + * Otherwise we may think the board did not reset and we bail. + * This for kdump only and is particular to the P600. + */ + msleep(500); } return 0; } -- cgit v0.10.2 From a0eda62552eba4e1f92d5354bb65c68fb6b45f87 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Mon, 31 Oct 2011 08:05:36 +0100 Subject: virtio-blk: use ida to allocate disk index Based on a patch by Mark Wu Current index allocation in virtio-blk is based on a monotonically increasing variable "index". This means we'll run out of numbers after a while. It also could cause confusion about the disk name in the case of hot-plugging disks. Change virtio-blk to use ida to allocate index, instead. Signed-off-by: Michael S. Tsirkin Signed-off-by: Jens Axboe diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c index 079c088..e7a5750 100644 --- a/drivers/block/virtio_blk.c +++ b/drivers/block/virtio_blk.c @@ -8,10 +8,13 @@ #include #include #include +#include #define PART_BITS 4 -static int major, index; +static int major; +static DEFINE_IDA(vd_index_ida); + struct workqueue_struct *virtblk_wq; struct virtio_blk @@ -35,6 +38,9 @@ struct virtio_blk /* What host tells us, plus 2 for header & tailer. */ unsigned int sg_elems; + /* Ida index - used to track minor number allocations. */ + int index; + /* Scatterlist: can be too big for stack. */ struct scatterlist sg[/*sg_elems*/]; }; @@ -276,6 +282,11 @@ static int index_to_minor(int index) return index << PART_BITS; } +static int minor_to_index(int minor) +{ + return minor >> PART_BITS; +} + static ssize_t virtblk_serial_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -341,14 +352,17 @@ static int __devinit virtblk_probe(struct virtio_device *vdev) { struct virtio_blk *vblk; struct request_queue *q; - int err; + int err, index; u64 cap; u32 v, blk_size, sg_elems, opt_io_size; u16 min_io_size; u8 physical_block_exp, alignment_offset; - if (index_to_minor(index) >= 1 << MINORBITS) - return -ENOSPC; + err = ida_simple_get(&vd_index_ida, 0, minor_to_index(1 << MINORBITS), + GFP_KERNEL); + if (err < 0) + goto out; + index = err; /* We need to know how many segments before we allocate. */ err = virtio_config_val(vdev, VIRTIO_BLK_F_SEG_MAX, @@ -365,7 +379,7 @@ static int __devinit virtblk_probe(struct virtio_device *vdev) sizeof(vblk->sg[0]) * sg_elems, GFP_KERNEL); if (!vblk) { err = -ENOMEM; - goto out; + goto out_free_index; } INIT_LIST_HEAD(&vblk->reqs); @@ -421,7 +435,7 @@ static int __devinit virtblk_probe(struct virtio_device *vdev) vblk->disk->private_data = vblk; vblk->disk->fops = &virtblk_fops; vblk->disk->driverfs_dev = &vdev->dev; - index++; + vblk->index = index; /* configure queue flush support */ if (virtio_has_feature(vdev, VIRTIO_BLK_F_FLUSH)) @@ -516,6 +530,8 @@ out_free_vq: vdev->config->del_vqs(vdev); out_free_vblk: kfree(vblk); +out_free_index: + ida_simple_remove(&vd_index_ida, index); out: return err; } @@ -523,6 +539,7 @@ out: static void __devexit virtblk_remove(struct virtio_device *vdev) { struct virtio_blk *vblk = vdev->priv; + int index = vblk->index; flush_work(&vblk->config_work); @@ -538,6 +555,7 @@ static void __devexit virtblk_remove(struct virtio_device *vdev) mempool_destroy(vblk->pool); vdev->config->del_vqs(vdev); kfree(vblk); + ida_simple_remove(&vd_index_ida, index); } static const struct virtio_device_id id_table[] = { -- cgit v0.10.2