diff options
Diffstat (limited to 'drivers/block')
-rw-r--r-- | drivers/block/DAC960.c | 2 | ||||
-rw-r--r-- | drivers/block/Kconfig | 5 | ||||
-rw-r--r-- | drivers/block/cciss.c | 9 | ||||
-rw-r--r-- | drivers/block/cpqarray.c | 2 | ||||
-rw-r--r-- | drivers/block/floppy.c | 2 | ||||
-rw-r--r-- | drivers/block/loop.c | 23 | ||||
-rw-r--r-- | drivers/block/nbd.c | 124 | ||||
-rw-r--r-- | drivers/block/paride/Kconfig | 5 | ||||
-rw-r--r-- | drivers/block/rd.c | 4 | ||||
-rw-r--r-- | drivers/block/sx8.c | 2 | ||||
-rw-r--r-- | drivers/block/ub.c | 439 | ||||
-rw-r--r-- | drivers/block/viodasd.c | 2 |
12 files changed, 399 insertions, 220 deletions
diff --git a/drivers/block/DAC960.c b/drivers/block/DAC960.c index 70eaa5c..21097a3 100644 --- a/drivers/block/DAC960.c +++ b/drivers/block/DAC960.c @@ -3471,7 +3471,7 @@ static inline boolean DAC960_ProcessCompletedRequest(DAC960_Command_T *Command, if (!end_that_request_first(Request, UpToDate, Command->BlockCount)) { - end_that_request_last(Request); + end_that_request_last(Request, UpToDate); if (Command->Completion) { complete(Command->Completion); diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig index 7b1cd93..139cbba 100644 --- a/drivers/block/Kconfig +++ b/drivers/block/Kconfig @@ -117,7 +117,7 @@ config BLK_DEV_XD config PARIDE tristate "Parallel port IDE device support" - depends on PARPORT + depends on PARPORT_PC ---help--- There are many external CD-ROM and disk devices that connect through your computer's parallel port. Most of them are actually IDE devices @@ -358,7 +358,8 @@ config BLK_DEV_UB This driver supports certain USB attached storage devices such as flash keys. - Warning: Enabling this cripples the usb-storage driver. + If you enable this driver, it is recommended to avoid conflicts + with usb-storage by enabling USB_LIBUSUAL. If unsure, say N. diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index a9e33db..d2815b7 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -1146,7 +1146,6 @@ static int revalidate_allvol(ctlr_info_t *host) del_gendisk(disk); if (q) blk_cleanup_queue(q); - put_disk(disk); } } @@ -1465,9 +1464,10 @@ static int deregister_disk(struct gendisk *disk, drive_info_struct *drv, request_queue_t *q = disk->queue; if (disk->flags & GENHD_FL_UP) del_gendisk(disk); - if (q) + if (q) { blk_cleanup_queue(q); - put_disk(disk); + drv->queue = NULL; + } } } @@ -2310,7 +2310,7 @@ static inline void complete_command( ctlr_info_t *h, CommandList_struct *cmd, printk("Done with %p\n", cmd->rq); #endif /* CCISS_DEBUG */ - end_that_request_last(cmd->rq); + end_that_request_last(cmd->rq, status ? 1 : -EIO); cmd_free(h,cmd,1); } @@ -3243,7 +3243,6 @@ static void __devexit cciss_remove_one (struct pci_dev *pdev) del_gendisk(disk); if (q) blk_cleanup_queue(q); - put_disk(disk); } } diff --git a/drivers/block/cpqarray.c b/drivers/block/cpqarray.c index cf1822a..9bddb68 100644 --- a/drivers/block/cpqarray.c +++ b/drivers/block/cpqarray.c @@ -1036,7 +1036,7 @@ static inline void complete_command(cmdlist_t *cmd, int timeout) complete_buffers(cmd->rq->bio, ok); DBGPX(printk("Done with %p\n", cmd->rq);); - end_that_request_last(cmd->rq); + end_that_request_last(cmd->rq, ok ? 1 : -EIO); } /* diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c index f7e765a..a5b857c 100644 --- a/drivers/block/floppy.c +++ b/drivers/block/floppy.c @@ -2301,7 +2301,7 @@ static void floppy_end_request(struct request *req, int uptodate) add_disk_randomness(req->rq_disk); floppy_off((long)req->rq_disk->private_data); blkdev_dequeue_request(req); - end_that_request_last(req); + end_that_request_last(req, uptodate); /* We're done with the request */ current_req = NULL; diff --git a/drivers/block/loop.c b/drivers/block/loop.c index 96c664a..a452b13 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -213,7 +213,7 @@ static int do_lo_send_aops(struct loop_device *lo, struct bio_vec *bvec, struct address_space_operations *aops = mapping->a_ops; pgoff_t index; unsigned offset, bv_offs; - int len, ret = 0; + int len, ret; down(&mapping->host->i_sem); index = pos >> PAGE_CACHE_SHIFT; @@ -232,9 +232,15 @@ static int do_lo_send_aops(struct loop_device *lo, struct bio_vec *bvec, page = grab_cache_page(mapping, index); if (unlikely(!page)) goto fail; - if (unlikely(aops->prepare_write(file, page, offset, - offset + size))) + ret = aops->prepare_write(file, page, offset, + offset + size); + if (unlikely(ret)) { + if (ret == AOP_TRUNCATED_PAGE) { + page_cache_release(page); + continue; + } goto unlock; + } transfer_result = lo_do_transfer(lo, WRITE, page, offset, bvec->bv_page, bv_offs, size, IV); if (unlikely(transfer_result)) { @@ -251,9 +257,15 @@ static int do_lo_send_aops(struct loop_device *lo, struct bio_vec *bvec, kunmap_atomic(kaddr, KM_USER0); } flush_dcache_page(page); - if (unlikely(aops->commit_write(file, page, offset, - offset + size))) + ret = aops->commit_write(file, page, offset, + offset + size); + if (unlikely(ret)) { + if (ret == AOP_TRUNCATED_PAGE) { + page_cache_release(page); + continue; + } goto unlock; + } if (unlikely(transfer_result)) goto unlock; bv_offs += size; @@ -264,6 +276,7 @@ static int do_lo_send_aops(struct loop_device *lo, struct bio_vec *bvec, unlock_page(page); page_cache_release(page); } + ret = 0; out: up(&mapping->host->i_sem); return ret; diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index 9e268dd..33d6f23 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -54,11 +54,15 @@ #include <linux/errno.h> #include <linux/file.h> #include <linux/ioctl.h> +#include <linux/compiler.h> +#include <linux/err.h> +#include <linux/kernel.h> #include <net/sock.h> #include <linux/devfs_fs_kernel.h> #include <asm/uaccess.h> +#include <asm/system.h> #include <asm/types.h> #include <linux/nbd.h> @@ -136,7 +140,7 @@ static void nbd_end_request(struct request *req) spin_lock_irqsave(q->queue_lock, flags); if (!end_that_request_first(req, uptodate, req->nr_sectors)) { - end_that_request_last(req); + end_that_request_last(req, uptodate); } spin_unlock_irqrestore(q->queue_lock, flags); } @@ -230,14 +234,6 @@ static int nbd_send_req(struct nbd_device *lo, struct request *req) request.len = htonl(size); memcpy(request.handle, &req, sizeof(req)); - down(&lo->tx_lock); - - if (!sock || !lo->sock) { - printk(KERN_ERR "%s: Attempted send on closed socket\n", - lo->disk->disk_name); - goto error_out; - } - dprintk(DBG_TX, "%s: request %p: sending control (%s@%llu,%luB)\n", lo->disk->disk_name, req, nbdcmd_to_ascii(nbd_cmd(req)), @@ -276,11 +272,9 @@ static int nbd_send_req(struct nbd_device *lo, struct request *req) } } } - up(&lo->tx_lock); return 0; error_out: - up(&lo->tx_lock); return 1; } @@ -289,9 +283,14 @@ static struct request *nbd_find_request(struct nbd_device *lo, char *handle) struct request *req; struct list_head *tmp; struct request *xreq; + int err; memcpy(&xreq, handle, sizeof(xreq)); + err = wait_event_interruptible(lo->active_wq, lo->active_req != xreq); + if (unlikely(err)) + goto out; + spin_lock(&lo->queue_lock); list_for_each(tmp, &lo->queue_head) { req = list_entry(tmp, struct request, queuelist); @@ -302,7 +301,11 @@ static struct request *nbd_find_request(struct nbd_device *lo, char *handle) return req; } spin_unlock(&lo->queue_lock); - return NULL; + + err = -ENOENT; + +out: + return ERR_PTR(err); } static inline int sock_recv_bvec(struct socket *sock, struct bio_vec *bvec) @@ -331,7 +334,11 @@ static struct request *nbd_read_stat(struct nbd_device *lo) goto harderror; } req = nbd_find_request(lo, reply.handle); - if (req == NULL) { + if (unlikely(IS_ERR(req))) { + result = PTR_ERR(req); + if (result != -ENOENT) + goto harderror; + printk(KERN_ERR "%s: Unexpected reply (%p)\n", lo->disk->disk_name, reply.handle); result = -EBADR; @@ -395,19 +402,24 @@ static void nbd_clear_que(struct nbd_device *lo) BUG_ON(lo->magic != LO_MAGIC); - do { - req = NULL; - spin_lock(&lo->queue_lock); - if (!list_empty(&lo->queue_head)) { - req = list_entry(lo->queue_head.next, struct request, queuelist); - list_del_init(&req->queuelist); - } - spin_unlock(&lo->queue_lock); - if (req) { - req->errors++; - nbd_end_request(req); - } - } while (req); + /* + * Because we have set lo->sock to NULL under the tx_lock, all + * modifications to the list must have completed by now. For + * the same reason, the active_req must be NULL. + * + * As a consequence, we don't need to take the spin lock while + * purging the list here. + */ + BUG_ON(lo->sock); + BUG_ON(lo->active_req); + + while (!list_empty(&lo->queue_head)) { + req = list_entry(lo->queue_head.next, struct request, + queuelist); + list_del_init(&req->queuelist); + req->errors++; + nbd_end_request(req); + } } /* @@ -435,11 +447,6 @@ static void do_nbd_request(request_queue_t * q) BUG_ON(lo->magic != LO_MAGIC); - if (!lo->file) { - printk(KERN_ERR "%s: Request when not-ready\n", - lo->disk->disk_name); - goto error_out; - } nbd_cmd(req) = NBD_CMD_READ; if (rq_data_dir(req) == WRITE) { nbd_cmd(req) = NBD_CMD_WRITE; @@ -453,32 +460,34 @@ static void do_nbd_request(request_queue_t * q) req->errors = 0; spin_unlock_irq(q->queue_lock); - spin_lock(&lo->queue_lock); - - if (!lo->file) { - spin_unlock(&lo->queue_lock); - printk(KERN_ERR "%s: failed between accept and semaphore, file lost\n", - lo->disk->disk_name); + down(&lo->tx_lock); + if (unlikely(!lo->sock)) { + up(&lo->tx_lock); + printk(KERN_ERR "%s: Attempted send on closed socket\n", + lo->disk->disk_name); req->errors++; nbd_end_request(req); spin_lock_irq(q->queue_lock); continue; } - list_add(&req->queuelist, &lo->queue_head); - spin_unlock(&lo->queue_lock); + lo->active_req = req; if (nbd_send_req(lo, req) != 0) { printk(KERN_ERR "%s: Request send failed\n", lo->disk->disk_name); - if (nbd_find_request(lo, (char *)&req) != NULL) { - /* we still own req */ - req->errors++; - nbd_end_request(req); - } else /* we're racing with nbd_clear_que */ - printk(KERN_DEBUG "nbd: can't find req\n"); + req->errors++; + nbd_end_request(req); + } else { + spin_lock(&lo->queue_lock); + list_add(&req->queuelist, &lo->queue_head); + spin_unlock(&lo->queue_lock); } + lo->active_req = NULL; + up(&lo->tx_lock); + wake_up_all(&lo->active_wq); + spin_lock_irq(q->queue_lock); continue; @@ -529,17 +538,10 @@ static int nbd_ioctl(struct inode *inode, struct file *file, down(&lo->tx_lock); lo->sock = NULL; up(&lo->tx_lock); - spin_lock(&lo->queue_lock); file = lo->file; lo->file = NULL; - spin_unlock(&lo->queue_lock); nbd_clear_que(lo); - spin_lock(&lo->queue_lock); - if (!list_empty(&lo->queue_head)) { - printk(KERN_ERR "nbd: disconnect: some requests are in progress -> please try again.\n"); - error = -EBUSY; - } - spin_unlock(&lo->queue_lock); + BUG_ON(!list_empty(&lo->queue_head)); if (file) fput(file); return error; @@ -598,24 +600,19 @@ static int nbd_ioctl(struct inode *inode, struct file *file, lo->sock = NULL; } up(&lo->tx_lock); - spin_lock(&lo->queue_lock); file = lo->file; lo->file = NULL; - spin_unlock(&lo->queue_lock); nbd_clear_que(lo); printk(KERN_WARNING "%s: queue cleared\n", lo->disk->disk_name); if (file) fput(file); return lo->harderror; case NBD_CLEAR_QUE: - down(&lo->tx_lock); - if (lo->sock) { - up(&lo->tx_lock); - return 0; /* probably should be error, but that would - * break "nbd-client -d", so just return 0 */ - } - up(&lo->tx_lock); - nbd_clear_que(lo); + /* + * This is for compatibility only. The queue is always cleared + * by NBD_DO_IT or NBD_CLEAR_SOCK. + */ + BUG_ON(!lo->sock && !list_empty(&lo->queue_head)); return 0; case NBD_PRINT_DEBUG: printk(KERN_INFO "%s: next = %p, prev = %p, head = %p\n", @@ -688,6 +685,7 @@ static int __init nbd_init(void) spin_lock_init(&nbd_dev[i].queue_lock); INIT_LIST_HEAD(&nbd_dev[i].queue_head); init_MUTEX(&nbd_dev[i].tx_lock); + init_waitqueue_head(&nbd_dev[i].active_wq); nbd_dev[i].blksize = 1024; nbd_dev[i].bytesize = 0x7ffffc00ULL << 10; /* 2TB */ disk->major = NBD_MAJOR; diff --git a/drivers/block/paride/Kconfig b/drivers/block/paride/Kconfig index 17ff405..c0d2854 100644 --- a/drivers/block/paride/Kconfig +++ b/drivers/block/paride/Kconfig @@ -4,11 +4,12 @@ # PARIDE doesn't need PARPORT, but if PARPORT is configured as a module, # PARIDE must also be a module. The bogus CONFIG_PARIDE_PARPORT option # controls the choices given to the user ... +# PARIDE only supports PC style parports. Tough for USB or other parports... config PARIDE_PARPORT tristate depends on PARIDE!=n - default m if PARPORT=m - default y if PARPORT!=m + default m if PARPORT_PC=m + default y if PARPORT_PC!=m comment "Parallel IDE high-level drivers" depends on PARIDE diff --git a/drivers/block/rd.c b/drivers/block/rd.c index 68c60a5..ffd6abd 100644 --- a/drivers/block/rd.c +++ b/drivers/block/rd.c @@ -154,7 +154,7 @@ static int ramdisk_commit_write(struct file *file, struct page *page, /* * ->writepage to the the blockdev's mapping has to redirty the page so that the - * VM doesn't go and steal it. We return WRITEPAGE_ACTIVATE so that the VM + * VM doesn't go and steal it. We return AOP_WRITEPAGE_ACTIVATE so that the VM * won't try to (pointlessly) write the page again for a while. * * Really, these pages should not be on the LRU at all. @@ -165,7 +165,7 @@ static int ramdisk_writepage(struct page *page, struct writeback_control *wbc) make_page_uptodate(page); SetPageDirty(page); if (wbc->for_reclaim) - return WRITEPAGE_ACTIVATE; + return AOP_WRITEPAGE_ACTIVATE; unlock_page(page); return 0; } diff --git a/drivers/block/sx8.c b/drivers/block/sx8.c index 1ded3b4..9251f41 100644 --- a/drivers/block/sx8.c +++ b/drivers/block/sx8.c @@ -770,7 +770,7 @@ static inline void carm_end_request_queued(struct carm_host *host, rc = end_that_request_first(req, uptodate, req->hard_nr_sectors); assert(rc == 0); - end_that_request_last(req); + end_that_request_last(req, uptodate); rc = carm_put_request(host, crq); assert(rc == 0); diff --git a/drivers/block/ub.c b/drivers/block/ub.c index bfb23d5..a05fe58 100644 --- a/drivers/block/ub.c +++ b/drivers/block/ub.c @@ -9,7 +9,6 @@ * * TODO (sorted by decreasing priority) * -- Kill first_open (Al Viro fixed the block layer now) - * -- Do resets with usb_device_reset (needs a thread context, use khubd) * -- set readonly flag for CDs, set removable flag for CF readers * -- do inquiry and verify we got a disk and not a tape (for LUN mismatch) * -- special case some senses, e.g. 3a/0 -> no media present, reduce retries @@ -29,6 +28,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/usb.h> +#include <linux/usb_usual.h> #include <linux/blkdev.h> #include <linux/devfs_fs_kernel.h> #include <linux/timer.h> @@ -107,16 +107,6 @@ */ /* - * Definitions which have to be scattered once we understand the layout better. - */ - -/* Transport (despite PR in the name) */ -#define US_PR_BULK 0x50 /* bulk only */ - -/* Protocol */ -#define US_SC_SCSI 0x06 /* Transparent */ - -/* * This many LUNs per USB device. * Every one of them takes a host, see UB_MAX_HOSTS. */ @@ -125,7 +115,7 @@ /* */ -#define UB_MINORS_PER_MAJOR 8 +#define UB_PARTS_PER_LUN 8 #define UB_MAX_CDB_SIZE 16 /* Corresponds to Bulk */ @@ -245,6 +235,13 @@ struct ub_scsi_cmd { void *back; }; +struct ub_request { + struct request *rq; + unsigned int current_try; + unsigned int nsg; /* sgv[nsg] */ + struct scatterlist sgv[UB_MAX_REQ_SG]; +}; + /* */ struct ub_capacity { @@ -340,6 +337,8 @@ struct ub_lun { int readonly; int first_open; /* Kludge. See ub_bd_open. */ + struct ub_request urq; + /* Use Ingo's mempool if or when we have more than one command. */ /* * Currently we never need more than one command for the whole device. @@ -360,6 +359,7 @@ struct ub_dev { atomic_t poison; /* The USB device is disconnected */ int openc; /* protected by ub_lock! */ /* kref is too implicit for our taste */ + int reset; /* Reset is running */ unsigned int tagcnt; char name[12]; struct usb_device *dev; @@ -387,6 +387,9 @@ struct ub_dev { struct bulk_cs_wrap work_bcs; struct usb_ctrlrequest work_cr; + struct work_struct reset_work; + wait_queue_head_t reset_wait; + int sg_stat[6]; struct ub_scsi_trace tr; }; @@ -395,12 +398,14 @@ struct ub_dev { */ static void ub_cleanup(struct ub_dev *sc); static int ub_request_fn_1(struct ub_lun *lun, struct request *rq); -static int ub_cmd_build_block(struct ub_dev *sc, struct ub_lun *lun, - struct ub_scsi_cmd *cmd, struct request *rq); -static int ub_cmd_build_packet(struct ub_dev *sc, struct ub_lun *lun, - struct ub_scsi_cmd *cmd, struct request *rq); +static void ub_cmd_build_block(struct ub_dev *sc, struct ub_lun *lun, + struct ub_scsi_cmd *cmd, struct ub_request *urq); +static void ub_cmd_build_packet(struct ub_dev *sc, struct ub_lun *lun, + struct ub_scsi_cmd *cmd, struct ub_request *urq); static void ub_rw_cmd_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd); static void ub_end_rq(struct request *rq, int uptodate); +static int ub_rw_cmd_retry(struct ub_dev *sc, struct ub_lun *lun, + struct ub_request *urq, struct ub_scsi_cmd *cmd); static int ub_submit_scsi(struct ub_dev *sc, struct ub_scsi_cmd *cmd); static void ub_urb_complete(struct urb *urb, struct pt_regs *pt); static void ub_scsi_action(unsigned long _dev); @@ -415,6 +420,8 @@ static void ub_state_sense(struct ub_dev *sc, struct ub_scsi_cmd *cmd); static int ub_submit_clear_stall(struct ub_dev *sc, struct ub_scsi_cmd *cmd, int stalled_pipe); static void ub_top_sense_done(struct ub_dev *sc, struct ub_scsi_cmd *scmd); +static void ub_reset_enter(struct ub_dev *sc); +static void ub_reset_task(void *arg); static int ub_sync_tur(struct ub_dev *sc, struct ub_lun *lun); static int ub_sync_read_cap(struct ub_dev *sc, struct ub_lun *lun, struct ub_capacity *ret); @@ -422,13 +429,18 @@ static int ub_probe_lun(struct ub_dev *sc, int lnum); /* */ +#ifdef CONFIG_USB_LIBUSUAL + +#define ub_usb_ids storage_usb_ids +#else + static struct usb_device_id ub_usb_ids[] = { - // { USB_DEVICE_VER(0x0781, 0x0002, 0x0009, 0x0009) }, /* SDDR-31 */ { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_SCSI, US_PR_BULK) }, { } }; MODULE_DEVICE_TABLE(usb, ub_usb_ids); +#endif /* CONFIG_USB_LIBUSUAL */ /* * Find me a way to identify "next free minor" for add_disk(), @@ -522,6 +534,9 @@ static ssize_t ub_diag_show(struct device *dev, struct device_attribute *attr, spin_lock_irqsave(&sc->lock, flags); cnt += sprintf(page + cnt, + "poison %d reset %d\n", + atomic_read(&sc->poison), sc->reset); + cnt += sprintf(page + cnt, "qlen %d qmax %d\n", sc->cmd_queue.qlen, sc->cmd_queue.qmax); cnt += sprintf(page + cnt, @@ -770,7 +785,8 @@ static int ub_request_fn_1(struct ub_lun *lun, struct request *rq) { struct ub_dev *sc = lun->udev; struct ub_scsi_cmd *cmd; - int rc; + struct ub_request *urq; + int n_elem; if (atomic_read(&sc->poison) || lun->changed) { blkdev_dequeue_request(rq); @@ -778,65 +794,70 @@ static int ub_request_fn_1(struct ub_lun *lun, struct request *rq) return 0; } + if (lun->urq.rq != NULL) + return -1; if ((cmd = ub_get_cmd(lun)) == NULL) return -1; memset(cmd, 0, sizeof(struct ub_scsi_cmd)); blkdev_dequeue_request(rq); + + urq = &lun->urq; + memset(urq, 0, sizeof(struct ub_request)); + urq->rq = rq; + + /* + * get scatterlist from block layer + */ + n_elem = blk_rq_map_sg(lun->disk->queue, rq, &urq->sgv[0]); + if (n_elem < 0) { + printk(KERN_INFO "%s: failed request map (%d)\n", + lun->name, n_elem); /* P3 */ + goto drop; + } + if (n_elem > UB_MAX_REQ_SG) { /* Paranoia */ + printk(KERN_WARNING "%s: request with %d segments\n", + lun->name, n_elem); + goto drop; + } + urq->nsg = n_elem; + sc->sg_stat[n_elem < 5 ? n_elem : 5]++; + if (blk_pc_request(rq)) { - rc = ub_cmd_build_packet(sc, lun, cmd, rq); + ub_cmd_build_packet(sc, lun, cmd, urq); } else { - rc = ub_cmd_build_block(sc, lun, cmd, rq); - } - if (rc != 0) { - ub_put_cmd(lun, cmd); - ub_end_rq(rq, 0); - return 0; + ub_cmd_build_block(sc, lun, cmd, urq); } cmd->state = UB_CMDST_INIT; cmd->lun = lun; cmd->done = ub_rw_cmd_done; - cmd->back = rq; + cmd->back = urq; cmd->tag = sc->tagcnt++; - if (ub_submit_scsi(sc, cmd) != 0) { - ub_put_cmd(lun, cmd); - ub_end_rq(rq, 0); - return 0; - } + if (ub_submit_scsi(sc, cmd) != 0) + goto drop; return 0; + +drop: + ub_put_cmd(lun, cmd); + ub_end_rq(rq, 0); + return 0; } -static int ub_cmd_build_block(struct ub_dev *sc, struct ub_lun *lun, - struct ub_scsi_cmd *cmd, struct request *rq) +static void ub_cmd_build_block(struct ub_dev *sc, struct ub_lun *lun, + struct ub_scsi_cmd *cmd, struct ub_request *urq) { - int ub_dir; - int n_elem; + struct request *rq = urq->rq; unsigned int block, nblks; if (rq_data_dir(rq) == WRITE) - ub_dir = UB_DIR_WRITE; + cmd->dir = UB_DIR_WRITE; else - ub_dir = UB_DIR_READ; - cmd->dir = ub_dir; + cmd->dir = UB_DIR_READ; - /* - * get scatterlist from block layer - */ - n_elem = blk_rq_map_sg(lun->disk->queue, rq, &cmd->sgv[0]); - if (n_elem <= 0) { - printk(KERN_INFO "%s: failed request map (%d)\n", - sc->name, n_elem); /* P3 */ - return -1; /* request with no s/g entries? */ - } - if (n_elem > UB_MAX_REQ_SG) { /* Paranoia */ - printk(KERN_WARNING "%s: request with %d segments\n", - sc->name, n_elem); - return -1; - } - cmd->nsg = n_elem; - sc->sg_stat[n_elem < 5 ? n_elem : 5]++; + cmd->nsg = urq->nsg; + memcpy(cmd->sgv, urq->sgv, sizeof(struct scatterlist) * cmd->nsg); /* * build the command @@ -847,7 +868,7 @@ static int ub_cmd_build_block(struct ub_dev *sc, struct ub_lun *lun, block = rq->sector >> lun->capacity.bshift; nblks = rq->nr_sectors >> lun->capacity.bshift; - cmd->cdb[0] = (ub_dir == UB_DIR_READ)? READ_10: WRITE_10; + cmd->cdb[0] = (cmd->dir == UB_DIR_READ)? READ_10: WRITE_10; /* 10-byte uses 4 bytes of LBA: 2147483648KB, 2097152MB, 2048GB */ cmd->cdb[2] = block >> 24; cmd->cdb[3] = block >> 16; @@ -858,14 +879,12 @@ static int ub_cmd_build_block(struct ub_dev *sc, struct ub_lun *lun, cmd->cdb_len = 10; cmd->len = rq->nr_sectors * 512; - - return 0; } -static int ub_cmd_build_packet(struct ub_dev *sc, struct ub_lun *lun, - struct ub_scsi_cmd *cmd, struct request *rq) +static void ub_cmd_build_packet(struct ub_dev *sc, struct ub_lun *lun, + struct ub_scsi_cmd *cmd, struct ub_request *urq) { - int n_elem; + struct request *rq = urq->rq; if (rq->data_len == 0) { cmd->dir = UB_DIR_NONE; @@ -874,40 +893,26 @@ static int ub_cmd_build_packet(struct ub_dev *sc, struct ub_lun *lun, cmd->dir = UB_DIR_WRITE; else cmd->dir = UB_DIR_READ; - } - /* - * get scatterlist from block layer - */ - n_elem = blk_rq_map_sg(lun->disk->queue, rq, &cmd->sgv[0]); - if (n_elem < 0) { - printk(KERN_INFO "%s: failed request map (%d)\n", - sc->name, n_elem); /* P3 */ - return -1; - } - if (n_elem > UB_MAX_REQ_SG) { /* Paranoia */ - printk(KERN_WARNING "%s: request with %d segments\n", - sc->name, n_elem); - return -1; - } - cmd->nsg = n_elem; - sc->sg_stat[n_elem < 5 ? n_elem : 5]++; + cmd->nsg = urq->nsg; + memcpy(cmd->sgv, urq->sgv, sizeof(struct scatterlist) * cmd->nsg); memcpy(&cmd->cdb, rq->cmd, rq->cmd_len); cmd->cdb_len = rq->cmd_len; cmd->len = rq->data_len; - - return 0; } static void ub_rw_cmd_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd) { - struct request *rq = cmd->back; struct ub_lun *lun = cmd->lun; + struct ub_request *urq = cmd->back; + struct request *rq; int uptodate; + rq = urq->rq; + if (cmd->error == 0) { uptodate = 1; @@ -928,9 +933,16 @@ static void ub_rw_cmd_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd) rq->errors = SAM_STAT_CHECK_CONDITION; else rq->errors = DID_ERROR << 16; + } else { + if (cmd->error == -EIO) { + if (ub_rw_cmd_retry(sc, lun, urq, cmd) == 0) + return; + } } } + urq->rq = NULL; + ub_put_cmd(lun, cmd); ub_end_rq(rq, uptodate); blk_start_queue(lun->disk->queue); @@ -938,11 +950,43 @@ static void ub_rw_cmd_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd) static void ub_end_rq(struct request *rq, int uptodate) { - int rc; + end_that_request_first(rq, uptodate, rq->hard_nr_sectors); + end_that_request_last(rq, uptodate); +} + +static int ub_rw_cmd_retry(struct ub_dev *sc, struct ub_lun *lun, + struct ub_request *urq, struct ub_scsi_cmd *cmd) +{ + + if (atomic_read(&sc->poison)) + return -ENXIO; + + ub_reset_enter(sc); - rc = end_that_request_first(rq, uptodate, rq->hard_nr_sectors); - // assert(rc == 0); - end_that_request_last(rq); + if (urq->current_try >= 3) + return -EIO; + urq->current_try++; + /* P3 */ printk("%s: dir %c len/act %d/%d " + "[sense %x %02x %02x] retry %d\n", + sc->name, UB_DIR_CHAR(cmd->dir), cmd->len, cmd->act_len, + cmd->key, cmd->asc, cmd->ascq, urq->current_try); + + memset(cmd, 0, sizeof(struct ub_scsi_cmd)); + ub_cmd_build_block(sc, lun, cmd, urq); + + cmd->state = UB_CMDST_INIT; + cmd->lun = lun; + cmd->done = ub_rw_cmd_done; + cmd->back = urq; + + cmd->tag = sc->tagcnt++; + +#if 0 /* Wasteful */ + return ub_submit_scsi(sc, cmd); +#else + ub_cmdq_add(sc, cmd); + return 0; +#endif } /* @@ -1075,7 +1119,7 @@ static void ub_scsi_dispatch(struct ub_dev *sc) struct ub_scsi_cmd *cmd; int rc; - while ((cmd = ub_cmdq_peek(sc)) != NULL) { + while (!sc->reset && (cmd = ub_cmdq_peek(sc)) != NULL) { if (cmd->state == UB_CMDST_DONE) { ub_cmdq_pop(sc); (*cmd->done)(sc, cmd); @@ -1098,11 +1142,12 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd) { struct urb *urb = &sc->work_urb; struct bulk_cs_wrap *bcs; + int len; int rc; if (atomic_read(&sc->poison)) { - /* A little too simplistic, I feel... */ - goto Bad_End; + ub_state_done(sc, cmd, -ENODEV); + return; } if (cmd->state == UB_CMDST_CLEAR) { @@ -1110,7 +1155,6 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd) /* * STALL while clearning STALL. * The control pipe clears itself - nothing to do. - * XXX Might try to reset the device here and retry. */ printk(KERN_NOTICE "%s: stall on control pipe\n", sc->name); @@ -1129,11 +1173,6 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd) } else if (cmd->state == UB_CMDST_CLR2STS) { if (urb->status == -EPIPE) { - /* - * STALL while clearning STALL. - * The control pipe clears itself - nothing to do. - * XXX Might try to reset the device here and retry. - */ printk(KERN_NOTICE "%s: stall on control pipe\n", sc->name); goto Bad_End; @@ -1151,11 +1190,6 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd) } else if (cmd->state == UB_CMDST_CLRRS) { if (urb->status == -EPIPE) { - /* - * STALL while clearning STALL. - * The control pipe clears itself - nothing to do. - * XXX Might try to reset the device here and retry. - */ printk(KERN_NOTICE "%s: stall on control pipe\n", sc->name); goto Bad_End; @@ -1172,7 +1206,12 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd) ub_state_stat_counted(sc, cmd); } else if (cmd->state == UB_CMDST_CMD) { - if (urb->status == -EPIPE) { + switch (urb->status) { + case 0: + break; + case -EOVERFLOW: + goto Bad_End; + case -EPIPE: rc = ub_submit_clear_stall(sc, cmd, sc->last_pipe); if (rc != 0) { printk(KERN_NOTICE "%s: " @@ -1182,17 +1221,20 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd) * This is typically ENOMEM or some other such shit. * Retrying is pointless. Just do Bad End on it... */ - goto Bad_End; + ub_state_done(sc, cmd, rc); + return; } cmd->state = UB_CMDST_CLEAR; ub_cmdtr_state(sc, cmd); return; - } - if (urb->status != 0) { + case -ESHUTDOWN: /* unplug */ + case -EILSEQ: /* unplug timeout on uhci */ + ub_state_done(sc, cmd, -ENODEV); + return; + default: goto Bad_End; } if (urb->actual_length != US_BULK_CB_WRAP_LEN) { - /* XXX Must do reset here to unconfuse the device */ goto Bad_End; } @@ -1211,11 +1253,8 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd) printk(KERN_NOTICE "%s: " "unable to submit clear (%d)\n", sc->name, rc); - /* - * This is typically ENOMEM or some other such shit. - * Retrying is pointless. Just do Bad End on it... - */ - goto Bad_End; + ub_state_done(sc, cmd, rc); + return; } cmd->state = UB_CMDST_CLR2STS; ub_cmdtr_state(sc, cmd); @@ -1224,14 +1263,50 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd) if (urb->status == -EOVERFLOW) { /* * A babble? Failure, but we must transfer CSW now. - * XXX This is going to end in perpetual babble. Reset. */ cmd->error = -EOVERFLOW; /* A cheap trick... */ ub_state_stat(sc, cmd); return; } - if (urb->status != 0) - goto Bad_End; + + if (cmd->dir == UB_DIR_WRITE) { + /* + * Do not continue writes in case of a failure. + * Doing so would cause sectors to be mixed up, + * which is worse than sectors lost. + * + * We must try to read the CSW, or many devices + * get confused. + */ + len = urb->actual_length; + if (urb->status != 0 || + len != cmd->sgv[cmd->current_sg].length) { + cmd->act_len += len; + ub_cmdtr_act_len(sc, cmd); + + cmd->error = -EIO; + ub_state_stat(sc, cmd); + return; + } + + } else { + /* + * If an error occurs on read, we record it, and + * continue to fetch data in order to avoid bubble. + * + * As a small shortcut, we stop if we detect that + * a CSW mixed into data. + */ + if (urb->status != 0) + cmd->error = -EIO; + + len = urb->actual_length; + if (urb->status != 0 || + len != cmd->sgv[cmd->current_sg].length) { + if ((len & 0x1FF) == US_BULK_CS_WRAP_LEN) + goto Bad_End; + } + } cmd->act_len += urb->actual_length; ub_cmdtr_act_len(sc, cmd); @@ -1249,11 +1324,8 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd) printk(KERN_NOTICE "%s: " "unable to submit clear (%d)\n", sc->name, rc); - /* - * This is typically ENOMEM or some other such shit. - * Retrying is pointless. Just do Bad End on it... - */ - goto Bad_End; + ub_state_done(sc, cmd, rc); + return; } /* @@ -1266,14 +1338,8 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd) ub_cmdtr_state(sc, cmd); return; } - if (urb->status == -EOVERFLOW) { - /* - * XXX We are screwed here. Retrying is pointless, - * because the pipelined data will not get in until - * we read with a big enough buffer. We must reset XXX. - */ - goto Bad_End; - } + + /* Catch everything, including -EOVERFLOW and other nasties. */ if (urb->status != 0) goto Bad_End; @@ -1319,15 +1385,15 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd) return; } - rc = le32_to_cpu(bcs->Residue); - if (rc != cmd->len - cmd->act_len) { + len = le32_to_cpu(bcs->Residue); + if (len != cmd->len - cmd->act_len) { /* * It is all right to transfer less, the caller has * to check. But it's not all right if the device * counts disagree with our counts. */ /* P3 */ printk("%s: resid %d len %d act %d\n", - sc->name, rc, cmd->len, cmd->act_len); + sc->name, len, cmd->len, cmd->act_len); goto Bad_End; } @@ -1338,13 +1404,13 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd) ub_state_sense(sc, cmd); return; case US_BULK_STAT_PHASE: - /* XXX We must reset the transport here */ /* P3 */ printk("%s: status PHASE\n", sc->name); goto Bad_End; default: printk(KERN_INFO "%s: unknown CSW status 0x%x\n", sc->name, bcs->Status); - goto Bad_End; + ub_state_done(sc, cmd, -EINVAL); + return; } /* Not zeroing error to preserve a babble indicator */ @@ -1364,7 +1430,8 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd) printk(KERN_WARNING "%s: " "wrong command state %d\n", sc->name, cmd->state); - goto Bad_End; + ub_state_done(sc, cmd, -EINVAL); + return; } return; @@ -1612,6 +1679,93 @@ static void ub_top_sense_done(struct ub_dev *sc, struct ub_scsi_cmd *scmd) } /* + * Reset management + */ + +static void ub_reset_enter(struct ub_dev *sc) +{ + + if (sc->reset) { + /* This happens often on multi-LUN devices. */ + return; + } + sc->reset = 1; + +#if 0 /* Not needed because the disconnect waits for us. */ + unsigned long flags; + spin_lock_irqsave(&ub_lock, flags); + sc->openc++; + spin_unlock_irqrestore(&ub_lock, flags); +#endif + +#if 0 /* We let them stop themselves. */ + struct list_head *p; + struct ub_lun *lun; + list_for_each(p, &sc->luns) { + lun = list_entry(p, struct ub_lun, link); + blk_stop_queue(lun->disk->queue); + } +#endif + + schedule_work(&sc->reset_work); +} + +static void ub_reset_task(void *arg) +{ + struct ub_dev *sc = arg; + unsigned long flags; + struct list_head *p; + struct ub_lun *lun; + int lkr, rc; + + if (!sc->reset) { + printk(KERN_WARNING "%s: Running reset unrequested\n", + sc->name); + return; + } + + if (atomic_read(&sc->poison)) { + printk(KERN_NOTICE "%s: Not resetting disconnected device\n", + sc->name); /* P3 This floods. Remove soon. XXX */ + } else if (sc->dev->actconfig->desc.bNumInterfaces != 1) { + printk(KERN_NOTICE "%s: Not resetting multi-interface device\n", + sc->name); /* P3 This floods. Remove soon. XXX */ + } else { + if ((lkr = usb_lock_device_for_reset(sc->dev, sc->intf)) < 0) { + printk(KERN_NOTICE + "%s: usb_lock_device_for_reset failed (%d)\n", + sc->name, lkr); + } else { + rc = usb_reset_device(sc->dev); + if (rc < 0) { + printk(KERN_NOTICE "%s: " + "usb_lock_device_for_reset failed (%d)\n", + sc->name, rc); + } + + if (lkr) + usb_unlock_device(sc->dev); + } + } + + /* + * In theory, no commands can be running while reset is active, + * so nobody can ask for another reset, and so we do not need any + * queues of resets or anything. We do need a spinlock though, + * to interact with block layer. + */ + spin_lock_irqsave(&sc->lock, flags); + sc->reset = 0; + tasklet_schedule(&sc->tasklet); + list_for_each(p, &sc->luns) { + lun = list_entry(p, struct ub_lun, link); + blk_start_queue(lun->disk->queue); + } + wake_up(&sc->reset_wait); + spin_unlock_irqrestore(&sc->lock, flags); +} + +/* * This is called from a process context. */ static void ub_revalidate(struct ub_dev *sc, struct ub_lun *lun) @@ -2146,7 +2300,7 @@ static int ub_get_pipes(struct ub_dev *sc, struct usb_device *dev, if (ep_in == NULL || ep_out == NULL) { printk(KERN_NOTICE "%s: failed endpoint check\n", sc->name); - return -EIO; + return -ENODEV; } /* Calculate and store the pipe values */ @@ -2172,6 +2326,9 @@ static int ub_probe(struct usb_interface *intf, int rc; int i; + if (usb_usual_check_type(dev_id, USB_US_TYPE_UB)) + return -ENXIO; + rc = -ENOMEM; if ((sc = kmalloc(sizeof(struct ub_dev), GFP_KERNEL)) == NULL) goto err_core; @@ -2181,6 +2338,8 @@ static int ub_probe(struct usb_interface *intf, usb_init_urb(&sc->work_urb); tasklet_init(&sc->tasklet, ub_scsi_action, (unsigned long)sc); atomic_set(&sc->poison, 0); + INIT_WORK(&sc->reset_work, ub_reset_task, sc); + init_waitqueue_head(&sc->reset_wait); init_timer(&sc->work_timer); sc->work_timer.data = (unsigned long) sc; @@ -2201,7 +2360,8 @@ static int ub_probe(struct usb_interface *intf, /* XXX Verify that we can handle the device (from descriptors) */ - ub_get_pipes(sc, sc->dev, intf); + if (ub_get_pipes(sc, sc->dev, intf) != 0) + goto err_dev_desc; if (device_create_file(&sc->intf->dev, &dev_attr_diag) != 0) goto err_diag; @@ -2272,6 +2432,7 @@ static int ub_probe(struct usb_interface *intf, /* device_remove_file(&sc->intf->dev, &dev_attr_diag); */ err_diag: +err_dev_desc: usb_set_intfdata(intf, NULL); // usb_put_intf(sc->intf); usb_put_dev(sc->dev); @@ -2309,14 +2470,14 @@ static int ub_probe_lun(struct ub_dev *sc, int lnum) ub_revalidate(sc, lun); rc = -ENOMEM; - if ((disk = alloc_disk(UB_MINORS_PER_MAJOR)) == NULL) + if ((disk = alloc_disk(UB_PARTS_PER_LUN)) == NULL) goto err_diskalloc; lun->disk = disk; sprintf(disk->disk_name, DRV_NAME "%c", lun->id + 'a'); sprintf(disk->devfs_name, DEVFS_NAME "/%c", lun->id + 'a'); disk->major = UB_MAJOR; - disk->first_minor = lun->id * UB_MINORS_PER_MAJOR; + disk->first_minor = lun->id * UB_PARTS_PER_LUN; disk->fops = &ub_bd_fops; disk->private_data = lun; disk->driverfs_dev = &sc->intf->dev; @@ -2380,6 +2541,11 @@ static void ub_disconnect(struct usb_interface *intf) atomic_set(&sc->poison, 1); /* + * Wait for reset to end, if any. + */ + wait_event(sc->reset_wait, !sc->reset); + + /* * Blow away queued commands. * * Actually, this never works, because before we get here @@ -2392,7 +2558,7 @@ static void ub_disconnect(struct usb_interface *intf) { struct ub_scsi_cmd *cmd; int cnt = 0; - while ((cmd = ub_cmdq_pop(sc)) != NULL) { + while ((cmd = ub_cmdq_peek(sc)) != NULL) { cmd->error = -ENOTCONN; cmd->state = UB_CMDST_DONE; ub_cmdtr_state(sc, cmd); @@ -2461,7 +2627,6 @@ static void ub_disconnect(struct usb_interface *intf) } static struct usb_driver ub_driver = { - .owner = THIS_MODULE, .name = "ub", .probe = ub_probe, .disconnect = ub_disconnect, @@ -2479,6 +2644,7 @@ static int __init ub_init(void) if ((rc = usb_register(&ub_driver)) != 0) goto err_register; + usb_usual_set_present(USB_US_TYPE_UB); return 0; err_register: @@ -2494,6 +2660,7 @@ static void __exit ub_exit(void) devfs_remove(DEVFS_NAME); unregister_blkdev(UB_MAJOR, DRV_NAME); + usb_usual_clear_present(USB_US_TYPE_UB); } module_init(ub_init); diff --git a/drivers/block/viodasd.c b/drivers/block/viodasd.c index 2d518aa..063f030 100644 --- a/drivers/block/viodasd.c +++ b/drivers/block/viodasd.c @@ -305,7 +305,7 @@ static void viodasd_end_request(struct request *req, int uptodate, if (end_that_request_first(req, uptodate, num_sectors)) return; add_disk_randomness(req->rq_disk); - end_that_request_last(req); + end_that_request_last(req, uptodate); } /* |