diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test9/Documentation/Configure.help linux/Documentation/Configure.help --- /opt/kernel/linux-2.4.0-test9/Documentation/Configure.help Sun Oct 15 12:51:07 2000 +++ linux/Documentation/Configure.help Sun Oct 15 13:00:31 2000 @@ -511,6 +511,31 @@ say M here and read Documentation/modules.txt. The module will be called ide-cd.o. + +Packet writing on CD/DVD media (EXPERIMENTAL) +CONFIG_CDROM_PKTCDVD + If you have a CDROM drive that supports packet writing, say Y to + include preliminary support. It should work with any MMC/Mt Fuji + complain ATAPI or SCSI drive, which is just about any newer CD + writer. + + Currently only writing to CD-RW discs is possible. + + If you want to compile the driver as a module ( = code which can be + inserted in and removed from the running kernel whenever you want), + say M here and read Documentation/modules.txt. The module will be + called packet.o. + +Free buffers +CONFIG_CDROM_PKTCDVD_BUFFERS + This controls the amount of free buffers that are allocated for + data gathering. More buffers speed up big writes at the cost of + latency and a bigger memory requirement (2KB per buffer). + + This option has no effect at all if the CD-RW is used with other + file systems (or without a file system). + + Include IDE/ATAPI TAPE support CONFIG_BLK_DEV_IDETAPE If you have an IDE tape drive using the ATAPI protocol, say Y. diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test9/arch/sparc64/kernel/ioctl32.c linux/arch/sparc64/kernel/ioctl32.c --- /opt/kernel/linux-2.4.0-test9/arch/sparc64/kernel/ioctl32.c Fri Aug 18 19:26:25 2000 +++ linux/arch/sparc64/kernel/ioctl32.c Sat Sep 9 14:55:59 2000 @@ -86,6 +86,7 @@ #include #include #include +#include /* Use this to get at 32-bit user passed pointers. See sys_sparc32.c for description about these. */ @@ -714,6 +715,37 @@ return ret; } +struct packet_stats32 { + u32 bh_s; + u32 bh_e; + u32 bh_w; + u32 bh_r; +}; + +static inline int pkt_getstats(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct packet_stats p; + struct packet_stats32 p32; + mm_segment_t old_fs = get_fs(); + int ret; + + ret = copy_from_user (&p32, (struct packet_stats32 *)arg, sizeof(struct packet_stats32)); + if (ret) + return -EFAULT; +#define P(x) (p.x = (unsigned long)p32.x) + P(bh_s); + P(bh_e); + P(bh_w); + P(bh_r); +#undef P + + set_fs (KERNEL_DS); + ret = sys_ioctl (fd, cmd, (long)&p); + set_fs (old_fs); + + return ret; +} + struct hd_geometry32 { unsigned char heads; unsigned char sectors; @@ -3574,6 +3606,12 @@ /* elevator */ COMPATIBLE_IOCTL(BLKELVGET) COMPATIBLE_IOCTL(BLKELVSET) +/* Big X, CDRW Packet Driver */ +#if defined(CONFIG_CDROM_PKTCDVD) +COMPATIBLE_IOCTL(PACKET_SETUP_DEV) +COMPATIBLE_IOCTL(PACKET_TEARDOWN_DEV) +HANDLE_IOCTL(PACKET_GET_STATS, pkt_getstats) +#endif /* CONFIG_CDROM_PKTCDVD */ /* And these ioctls need translation */ HANDLE_IOCTL(SIOCGIFNAME, dev_ifname32) HANDLE_IOCTL(SIOCGIFCONF, dev_ifconf) diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test9/drivers/block/Config.in linux/drivers/block/Config.in --- /opt/kernel/linux-2.4.0-test9/drivers/block/Config.in Sun Oct 15 12:51:09 2000 +++ linux/drivers/block/Config.in Tue Oct 10 20:22:22 2000 @@ -34,11 +34,21 @@ source drivers/block/paride/Config.in fi dep_tristate 'Compaq SMART2 support' CONFIG_BLK_CPQ_DA $CONFIG_PCI -dep_tristate 'Compaq CISS Array support' CONFIG_BLK_CPQ_CISS_DA $CONFIG_PCI +dep_tristate 'Compaq CISS Array support' CONFIG_BLK_CPQ_CISS_DA $CONFIG_PCI dep_tristate 'Mylex DAC960/DAC1100 PCI RAID Controller support' CONFIG_BLK_DEV_DAC960 $CONFIG_PCI +tristate 'Packet writing on CD/DVD media' CONFIG_CDROM_PKTCDVD +if [ "$CONFIG_CDROM_PKTCDVD" != "n" ]; then + int ' Free buffers for data gathering' CONFIG_CDROM_PKTCDVD_BUFFERS 256 +fi + tristate 'Loopback device support' CONFIG_BLK_DEV_LOOP dep_tristate 'Network block device support' CONFIG_BLK_DEV_NBD $CONFIG_NET + +tristate 'Logical volume manager (LVM) support' CONFIG_BLK_DEV_LVM N +if [ "$CONFIG_BLK_DEV_LVM" != "n" ]; then + bool ' LVM information in proc filesystem' CONFIG_LVM_PROC_FS Y +fi tristate 'RAM disk support' CONFIG_BLK_DEV_RAM if [ "$CONFIG_BLK_DEV_RAM" = "y" -o "$CONFIG_BLK_DEV_RAM" = "m" ]; then diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test9/drivers/block/DAC960.c linux/drivers/block/DAC960.c --- /opt/kernel/linux-2.4.0-test9/drivers/block/DAC960.c Mon Aug 21 18:23:54 2000 +++ linux/drivers/block/DAC960.c Fri Sep 8 01:04:37 2000 @@ -1825,7 +1825,6 @@ Request->nr_segments < Controller->DriverScatterGatherLimit) { Request->nr_segments++; - RequestQueue->elevator.nr_segments++; return true; } return false; @@ -1849,7 +1848,6 @@ Request->nr_segments < Controller->DriverScatterGatherLimit) { Request->nr_segments++; - RequestQueue->elevator.nr_segments++; return true; } return false; @@ -1879,7 +1877,6 @@ if (TotalSegments > MaxSegments || TotalSegments > Controller->DriverScatterGatherLimit) return false; - RequestQueue->elevator.nr_segments -= SameSegment; Request->nr_segments = TotalSegments; return true; } diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test9/drivers/block/Makefile linux/drivers/block/Makefile --- /opt/kernel/linux-2.4.0-test9/drivers/block/Makefile Sun Oct 15 12:51:09 2000 +++ linux/drivers/block/Makefile Tue Oct 10 20:36:04 2000 @@ -14,7 +14,7 @@ O_TARGET := block.o -export-objs := ll_rw_blk.o blkpg.o loop.o DAC960.o +export-objs := ll_rw_blk.o blkpg.o loop.o DAC960.o elevator.o obj-y := ll_rw_blk.o blkpg.o genhd.o elevator.o @@ -35,6 +35,8 @@ obj-$(CONFIG_BLK_DEV_DAC960) += DAC960.o obj-$(CONFIG_BLK_DEV_NBD) += nbd.o + +obj-$(CONFIG_CDROM_PKTCDVD) += pktcdvd.o ifeq ($(CONFIG_PARIDE),y) SUB_DIRS += paride diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test9/drivers/block/cpqarray.c linux/drivers/block/cpqarray.c --- /opt/kernel/linux-2.4.0-test9/drivers/block/cpqarray.c Sun Oct 15 12:51:09 2000 +++ linux/drivers/block/cpqarray.c Tue Oct 10 20:36:04 2000 @@ -443,7 +443,9 @@ continue; } - + blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR + i), request_fns[i]); + blk_queue_headactive(BLK_DEFAULT_QUEUE(MAJOR_NR + i), 0); + hba[i]->access.set_intr_mask(hba[i], 0); if (request_irq(hba[i]->intr, do_ida_intr, SA_INTERRUPT|SA_SHIRQ, hba[i]->devname, hba[i])) { @@ -451,6 +453,7 @@ printk(KERN_ERR "cpqarray: Unable to get irq %d for %s\n", hba[i]->intr, hba[i]->devname); unregister_blkdev(MAJOR_NR+i, hba[i]->devname); + blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR + i)); continue; } num_cntlrs_reg++; @@ -468,6 +471,7 @@ kfree(hba[i]->cmd_pool); free_irq(hba[i]->intr, hba[i]); unregister_blkdev(MAJOR_NR+i, hba[i]->devname); + blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR + i)); num_cntlrs_reg--; printk( KERN_ERR "cpqarray: out of memory"); @@ -498,9 +502,6 @@ ida_procinit(i); - blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR + i), - request_fns[i]); - blk_queue_headactive(BLK_DEFAULT_QUEUE(MAJOR_NR + i), 0); blksize_size[MAJOR_NR+i] = ida_blocksizes + (i*256); hardsect_size[MAJOR_NR+i] = ida_hardsizes + (i*256); read_ahead[MAJOR_NR+i] = READ_AHEAD; diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test9/drivers/block/elevator.c linux/drivers/block/elevator.c --- /opt/kernel/linux-2.4.0-test9/drivers/block/elevator.c Wed Aug 23 07:33:46 2000 +++ linux/drivers/block/elevator.c Sun Sep 10 14:38:45 2000 @@ -24,6 +24,7 @@ #include #include #include +#include #include /* @@ -51,50 +52,67 @@ } int elevator_linus_merge(request_queue_t *q, struct request **req, + struct list_head * head, struct buffer_head *bh, int rw, - int *max_sectors, int *max_segments) + int max_sectors, int max_segments) { - struct list_head *entry, *head = &q->queue_head; + struct list_head *entry; unsigned int count = bh->b_size >> 9, ret = ELEVATOR_NO_MERGE; + int front = 0, back = 0; - entry = head; - if (q->head_active && !q->plugged) - head = head->next; - - while ((entry = entry->prev) != head) { + entry = &q->queue_head; + while ((entry = entry->prev) != head && !back && !front) { struct request *__rq = *req = blkdev_entry_to_request(entry); - if (__rq->sem) - continue; + if (__rq->cmd != rw) continue; - if (__rq->nr_sectors + count > *max_sectors) - continue; if (__rq->rq_dev != bh->b_rdev) continue; - if (__rq->sector + __rq->nr_sectors == bh->b_rsector) { + if (__rq->sector + __rq->nr_sectors == bh->b_rsector) + back = 1; + else if (__rq->sector - count == bh->b_rsector) + front = 1; + + if (__rq->nr_sectors + count > max_sectors) + continue; + if (__rq->sem) + continue; + + if (back) { ret = ELEVATOR_BACK_MERGE; break; } if (!__rq->elevator_sequence) break; - if (__rq->sector - count == bh->b_rsector) { - __rq->elevator_sequence--; + if (front) { ret = ELEVATOR_FRONT_MERGE; break; } } + return ret; +} + +void elevator_linus_merge_cleanup(request_queue_t *q, struct request *req, int ret, int front) +{ + struct list_head *entry = &req->queue, *head = &q->queue_head; + + if (front) + req->elevator_sequence--; + /* * second pass scan of requests that got passed over, if any */ - if (ret != ELEVATOR_NO_MERGE && *req) { - while ((entry = entry->next) != &q->queue_head) { - struct request *tmp = blkdev_entry_to_request(entry); - tmp->elevator_sequence--; - } + while ((entry = entry->next) != head) { + struct request *tmp = blkdev_entry_to_request(entry); + tmp->elevator_sequence--; } +} - return ret; +void elevator_linus_merge_req(struct request *req, struct request *next) +{ + if (next->elevator_sequence < req->elevator_sequence) + req->elevator_sequence = next->elevator_sequence; } /* @@ -108,41 +126,46 @@ } /* - * See if we can find a request that is buffer can be coalesced with. + * See if we can find a request that this buffer can be coalesced with. */ int elevator_noop_merge(request_queue_t *q, struct request **req, + struct list_head * head, struct buffer_head *bh, int rw, - int *max_sectors, int *max_segments) + int max_sectors, int max_segments) { - struct list_head *entry, *head = &q->queue_head; + struct list_head *entry; unsigned int count = bh->b_size >> 9; + int back = 0, front = 0; - if (q->head_active && !q->plugged) - head = head->next; - - entry = head; - while ((entry = entry->prev) != head) { + entry = &q->queue_head; + while ((entry = entry->prev) != head && !back && !front) { struct request *__rq = *req = blkdev_entry_to_request(entry); - if (__rq->sem) - continue; + if (__rq->cmd != rw) continue; - if (__rq->nr_sectors + count > *max_sectors) - continue; if (__rq->rq_dev != bh->b_rdev) continue; if (__rq->sector + __rq->nr_sectors == bh->b_rsector) + back = 1; + else if (__rq->sector - count == bh->b_rsector) + front = 1; + + if (__rq->nr_sectors + count > max_sectors) + continue; + if (__rq->sem) + continue; + + if (back) return ELEVATOR_BACK_MERGE; - if (__rq->sector - count == bh->b_rsector) + if (front) return ELEVATOR_FRONT_MERGE; } return ELEVATOR_NO_MERGE; } -/* - * The noop "elevator" does not do any accounting - */ -void elevator_noop_dequeue(struct request *req) {} +void elevator_noop_merge_cleanup(request_queue_t *q, struct request *req, int ret, int front) {} + +void elevator_noop_merge_req(struct request *req, struct request *next) {} int blkelvget_ioctl(elevator_t * elevator, blkelv_ioctl_arg_t * arg) { @@ -183,3 +206,5 @@ *elevator = type; elevator->queue_ID = queue_ID++; } + +EXPORT_SYMBOL(elevator_init); diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test9/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c --- /opt/kernel/linux-2.4.0-test9/drivers/block/ll_rw_blk.c Sun Oct 15 12:51:09 2000 +++ linux/drivers/block/ll_rw_blk.c Tue Oct 10 20:36:04 2000 @@ -125,7 +125,7 @@ return max_sectors[MAJOR(dev)][MINOR(dev)]; } -static inline request_queue_t *__blk_get_queue(kdev_t dev) +inline request_queue_t *__blk_get_queue(kdev_t dev) { struct blk_dev_struct *bdev = blk_dev + MAJOR(dev); @@ -153,17 +153,14 @@ static int __blk_cleanup_queue(struct list_head *head) { - struct list_head *entry; struct request *rq; int i = 0; if (list_empty(head)) return 0; - entry = head->next; do { - rq = list_entry(entry, struct request, table); - entry = entry->next; + rq = list_entry(head->next, struct request, table); list_del(&rq->table); kmem_cache_free(request_cachep, rq); i++; @@ -189,6 +186,8 @@ { int count = QUEUE_NR_REQUESTS; + count -= __blk_cleanup_queue(&q->pending_freelist[READ]); + count -= __blk_cleanup_queue(&q->pending_freelist[WRITE]); count -= __blk_cleanup_queue(&q->request_freelist[READ]); count -= __blk_cleanup_queue(&q->request_freelist[WRITE]); @@ -280,7 +279,6 @@ { if (req->nr_segments < max_segments) { req->nr_segments++; - q->elevator.nr_segments++; return 1; } return 0; @@ -317,7 +315,6 @@ if (total_segments > max_segments) return 0; - q->elevator.nr_segments -= same_segment; req->nr_segments = total_segments; return 1; } @@ -354,7 +351,7 @@ } } -static void generic_unplug_device(void *data) +void generic_unplug_device(void *data) { request_queue_t *q = (request_queue_t *) data; unsigned long flags; @@ -369,18 +366,21 @@ struct request *rq; int i; + INIT_LIST_HEAD(&q->request_freelist[READ]); + INIT_LIST_HEAD(&q->request_freelist[WRITE]); + /* - * Divide requests in half between read and write. This used to - * be a 2/3 advantage for reads, but now reads can steal from - * the write free list. + * Divide requests in half between read and write */ for (i = 0; i < QUEUE_NR_REQUESTS; i++) { rq = kmem_cache_alloc(request_cachep, SLAB_KERNEL); + memset(rq, 0, sizeof(struct request)); rq->rq_status = RQ_INACTIVE; list_add(&rq->table, &q->request_freelist[i & 1]); } - init_waitqueue_head(&q->wait_for_request); + init_waitqueue_head(&q->wait_for_request[READ]); + init_waitqueue_head(&q->wait_for_request[WRITE]); spin_lock_init(&q->request_lock); } @@ -414,16 +414,14 @@ * blk_queue_headactive(). * * Note: - * blk_init_queue() must be paired with a blk_cleanup-queue() call + * blk_init_queue() must be paired with a blk_cleanup_queue() call * when the block device is deactivated (such as at module unload). **/ static int __make_request(request_queue_t * q, int rw, struct buffer_head * bh); void blk_init_queue(request_queue_t * q, request_fn_proc * rfn) { INIT_LIST_HEAD(&q->queue_head); - INIT_LIST_HEAD(&q->request_freelist[READ]); - INIT_LIST_HEAD(&q->request_freelist[WRITE]); - elevator_init(&q->elevator, ELEVATOR_LINUS); + elevator_init(&q->elevator, ELEVATOR_NOOP); blk_init_free_list(q); q->request_fn = rfn; q->back_merge_fn = ll_back_merge_fn; @@ -442,8 +440,15 @@ */ q->plug_device_fn = generic_plug_device; q->head_active = 1; -} + /* + * Holds requests when doing batched freeing + */ + INIT_LIST_HEAD(&q->pending_freelist[READ]); + INIT_LIST_HEAD(&q->pending_freelist[WRITE]); + q->pending_free[READ] = 0; + q->pending_free[WRITE] = 0; +} #define blkdev_free_rq(list) list_entry((list)->next, struct request, table); /* @@ -452,34 +457,22 @@ */ static inline struct request *get_request(request_queue_t *q, int rw) { - struct list_head *list = &q->request_freelist[rw]; struct request *rq; + int list_rw = rw; - /* - * Reads get preferential treatment and are allowed to steal - * from the write free list if necessary. - */ - if (!list_empty(list)) { - rq = blkdev_free_rq(list); + if (!list_empty(&q->request_freelist[rw])) goto got_rq; - } - /* - * if the WRITE list is non-empty, we know that rw is READ - * and that the READ list is empty. allow reads to 'steal' - * from the WRITE list. - */ if (!list_empty(&q->request_freelist[WRITE])) { - list = &q->request_freelist[WRITE]; - rq = blkdev_free_rq(list); + list_rw = WRITE; goto got_rq; } return NULL; - got_rq: + rq = blkdev_free_rq(&q->request_freelist[list_rw]); list_del(&rq->table); - rq->free_list = list; + rq->free_list = list_rw; rq->rq_status = RQ_ACTIVE; rq->special = NULL; rq->q = q; @@ -494,7 +487,7 @@ register struct request *rq; DECLARE_WAITQUEUE(wait, current); - add_wait_queue_exclusive(&q->wait_for_request, &wait); + add_wait_queue_exclusive(&q->wait_for_request[rw], &wait); for (;;) { __set_current_state(TASK_UNINTERRUPTIBLE | TASK_EXCLUSIVE); spin_lock_irq(&io_request_lock); @@ -505,7 +498,7 @@ generic_unplug_device(q); schedule(); } - remove_wait_queue(&q->wait_for_request, &wait); + remove_wait_queue(&q->wait_for_request[rw], &wait); current->state = TASK_RUNNING; return rq; } @@ -606,20 +599,40 @@ (q->request_fn)(q); } +void inline blk_refill_freelist(request_queue_t *q, int rw) +{ + list_splice(&q->pending_freelist[rw], &q->request_freelist[rw]); + INIT_LIST_HEAD(&q->pending_freelist[rw]); + q->pending_free[rw] = 0; +} + /* * Must be called with io_request_lock held and interrupts disabled */ void inline blkdev_release_request(struct request *req) { + request_queue_t *q = req->q; + int rw = req->free_list; + req->rq_status = RQ_INACTIVE; + req->q = NULL; /* * Request may not have originated from ll_rw_blk */ - if (req->free_list) { - list_add(&req->table, req->free_list); - req->free_list = NULL; - wake_up(&req->q->wait_for_request); + if (q == NULL) + return; + + if (!waitqueue_active(&q->wait_for_request[rw])) { + blk_refill_freelist(q, rw); + list_add(&req->table, &q->request_freelist[rw]); + return; + } + + list_add(&req->table, &q->pending_freelist[rw]); + if (++q->pending_free[rw] >= 1) { + blk_refill_freelist(q, rw); + wake_up_nr(&q->wait_for_request[rw], q->pending_free[rw]); } } @@ -647,6 +660,7 @@ if(!(q->merge_requests_fn)(q, req, next, max_segments)) return; + q->elevator.elevator_merge_req_fn(req, next); req->bhtail->b_reqnext = next->bh; req->bhtail = next->bhtail; req->nr_sectors = req->hard_nr_sectors += next->hard_nr_sectors; @@ -683,9 +697,9 @@ { unsigned int sector, count; int max_segments = MAX_SEGMENTS; - struct request * req = NULL; + struct request * req = NULL, *freereq = NULL; int rw_ahead, max_sectors, el_ret; - struct list_head *head = &q->queue_head; + struct list_head *head; int latency; elevator_t *elevator = &q->elevator; @@ -698,6 +712,7 @@ rw_ahead = 1; rw = READ; /* drop into READ */ case READ: + break; case WRITE: break; default: @@ -729,33 +744,30 @@ latency = elevator_request_latency(elevator, rw); +again: /* * Now we acquire the request spinlock, we have to be mega careful * not to schedule or do something nonatomic */ spin_lock_irq(&io_request_lock); - /* - * skip first entry, for devices with active queue head - */ - if (q->head_active && !q->plugged) - head = head->next; - + head = &q->queue_head; if (list_empty(head)) { q->plug_device_fn(q, bh->b_rdev); /* is atomic */ goto get_rq; - } + } else if (q->head_active && !q->plugged) + head = head->next; - el_ret = elevator->elevator_merge_fn(q, &req, bh, rw, &max_sectors, &max_segments); + el_ret = elevator->elevator_merge_fn(q, &req, head, bh, rw, max_sectors, max_segments); switch (el_ret) { case ELEVATOR_BACK_MERGE: if (!q->back_merge_fn(q, req, bh, max_segments)) break; + elevator->elevator_merge_cleanup_fn(q, req, el_ret, 0); req->bhtail->b_reqnext = bh; req->bhtail = bh; req->nr_sectors = req->hard_nr_sectors += count; - req->e = elevator; drive_stat_acct(req->rq_dev, req->cmd, count, 0); attempt_back_merge(q, req, max_sectors, max_segments); goto out; @@ -763,16 +775,20 @@ case ELEVATOR_FRONT_MERGE: if (!q->front_merge_fn(q, req, bh, max_segments)) break; + elevator->elevator_merge_cleanup_fn(q, req, el_ret, 1); bh->b_reqnext = req->bh; req->bh = bh; req->buffer = bh->b_data; req->current_nr_sectors = count; req->sector = req->hard_sector = sector; req->nr_sectors = req->hard_nr_sectors += count; - req->e = elevator; drive_stat_acct(req->rq_dev, req->cmd, count, 0); attempt_front_merge(q, head, req, max_sectors, max_segments); goto out; + + case ELEVATOR_HOLE_MERGE: + goto out; + /* * elevator says don't/can't merge. get new request */ @@ -791,19 +807,16 @@ * are not crucial. */ get_rq: - if ((req = get_request(q, rw)) == NULL) { + if (freereq) { + req = freereq; + freereq = NULL; + } else if ((req = get_request(q, rw)) == NULL) { spin_unlock_irq(&io_request_lock); if (rw_ahead) goto end_io; - req = __get_request_wait(q, rw); - spin_lock_irq(&io_request_lock); - - if (q->head_active) { - head = &q->queue_head; - if (!q->plugged) - head = head->next; - } + freereq = __get_request_wait(q, rw); + goto again; } /* fill up the request-info, and add it to the queue */ @@ -819,11 +832,10 @@ req->bh = bh; req->bhtail = bh; req->rq_dev = bh->b_rdev; - req->e = elevator; add_request(q, req, head, latency); out: - if (!q->plugged) - (q->request_fn)(q); + if (freereq) + blkdev_release_request(freereq); spin_unlock_irq(&io_request_lock); return 0; end_io: @@ -878,7 +890,6 @@ buffer_IO_error(bh); break; } - } while (q->make_request_fn(q, rw, bh)); } @@ -996,6 +1007,8 @@ if ((bh = req->bh) != NULL) { nsect = bh->b_size >> 9; req->bh = bh->b_reqnext; + if (req->bh && (bh->b_rsector + (bh->b_size >> 9)) != req->bh->b_rsector) + printk("%s: %lu is followed by %lu\n", name, bh->b_rsector, req->bh->b_rsector); bh->b_reqnext = NULL; bh->b_end_io(bh, uptodate); if ((bh = req->bh) != NULL) { @@ -1018,10 +1031,6 @@ void end_that_request_last(struct request *req) { - if (req->e) { - printk("end_that_request_last called with non-dequeued req\n"); - BUG(); - } if (req->sem != NULL) up(req->sem); @@ -1159,9 +1168,11 @@ EXPORT_SYMBOL(end_that_request_last); EXPORT_SYMBOL(blk_init_queue); EXPORT_SYMBOL(blk_get_queue); +EXPORT_SYMBOL(__blk_get_queue); EXPORT_SYMBOL(blk_cleanup_queue); EXPORT_SYMBOL(blk_queue_headactive); EXPORT_SYMBOL(blk_queue_pluggable); EXPORT_SYMBOL(blk_queue_make_request); EXPORT_SYMBOL(generic_make_request); EXPORT_SYMBOL(blkdev_release_request); +EXPORT_SYMBOL(generic_unplug_device); diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test9/drivers/block/paride/pd.c linux/drivers/block/paride/pd.c --- /opt/kernel/linux-2.4.0-test9/drivers/block/paride/pd.c Tue Jun 20 16:24:52 2000 +++ linux/drivers/block/paride/pd.c Fri Sep 8 01:10:41 2000 @@ -392,7 +392,6 @@ if (req->nr_segments < max_segments) { req->nr_segments++; - q->elevator.nr_segments++; return 1; } return 0; @@ -432,7 +431,6 @@ if (total_segments > max_segments) return 0; - q->elevator.nr_segments -= same_segment; req->nr_segments = total_segments; return 1; } diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test9/drivers/block/paride/pf.c linux/drivers/block/paride/pf.c --- /opt/kernel/linux-2.4.0-test9/drivers/block/paride/pf.c Wed Apr 5 04:25:14 2000 +++ linux/drivers/block/paride/pf.c Fri Sep 8 01:10:48 2000 @@ -346,7 +346,6 @@ if (req->nr_segments < max_segments) { req->nr_segments++; - q->elevator.nr_segments++; return 1; } return 0; @@ -386,7 +385,6 @@ if (total_segments > max_segments) return 0; - q->elevator.nr_segments -= same_segment; req->nr_segments = total_segments; return 1; } diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test9/drivers/block/pktcdvd.c linux/drivers/block/pktcdvd.c --- /opt/kernel/linux-2.4.0-test9/drivers/block/pktcdvd.c Thu Jan 1 01:00:00 1970 +++ linux/drivers/block/pktcdvd.c Tue Sep 26 19:07:56 2000 @@ -0,0 +1,2030 @@ +/* + * Copyright (C) 2000 Jens Axboe + * + * May be copied or modified under the terms of the GNU General Public + * License. See linux/COPYING for more information. + * + * Packet writing layer for ATAPI and SCSI CD-R, CD-RW, DVD-R, and + * DVD-RW devices (aka an exercise in block layer masturbation) + * + * + * TODO: (circa order of when I will fix it) + * - Only able to write on CD-RW media right now. + * - check host application code on media and set it in write page + * - Generic interface for UDF to submit large packets for variable length + * packet writing (kiovec of dirty pages) + * - (in correlation with above) interface for UDF <-> packet to negotiate + * a new location when a write fails. + * - handle OPC, especially for -RW media + * + * ------------------------------------------------------------------------ + * + * 0.0.2d (24/09/2000) + * - (scsi) use implicit segment recounting for all hba's + * - fix speed setting, was consistenly off on most drives + * - only print capacity when opening for write + * - fix off-by-two error in getting/setting write+read speed (affected + * reporting as well as actual speed used) + * - possible to enable write caching on drive + * - do ioctl marshalling on sparc64 from Ben Collins + * - avoid unaligned access on flags, should have been unsigned long of course + * - fixed missed wakeup in kpacketd + * - b_dev error (two places) + * - fix buffer head b_count bugs + * - fix hole merge bug, where tail could be added twice + * - fsync and invalidate buffers on close + * - check hash table for buffers first before using our own + * - add read-ahead + * - fixed several list races + * - fix proc reporting for more than one device + * - change to O_CREAT for creating devices + * - added media_change hook + * - added free buffers config option + * - pkt_lock_tray fails on failed open (and oopses), remove it. unlock + * is done explicitly in pkt_remove dev anyway. + * - added proper elevator insertion (should probably be part of elevator.c) + * - moved kernel thread info to private device, spawn one for each writer + * - added separate buffer list for dirty packet buffers + * - fixed nasty data corruption bug + * - remember to account request even when we don't gather data for it + * - add ioctl to force wakeup of kernel thread (for debug) + * - fixed packet size setting bug on zero detected + * + * 0.0.2c (08/09/2000) + * - inc usage count of buffer heads + * - add internal buffer pool to avoid deadlock on oom + * - gather data for as many buffers as we have, before initiating write. this + * allows the laser to stay on longer, giving better performance. + * - fix always busy when tray can't be locked + * - remove request duplication nastiness, inject directly into the target + * - adapted to devfs and elevator changes + * - added proc interface + * + * 0.0.2b (21/06/2000) + * - fix io_request_lock typos (missing '&') + * - grab pkt_sem before invoking pkt_handle_queue + * - SCSI uses queuedata too, mirror that in pd->queuedata (hack) + * - remove SCSI sr debug messages + * - really activate empty block querying (requires cvs UDF, CDRW branch) + * - make sure sync_buffers doesn't consider us, or we can deadlock + * - make sure people don't swap on us (for now ;) + * + * 0.0.2a (19/06/2000) + * - add kpacketd kernel thread to handle actual data gathering + * - pd->pkt_dev is now real device, not just minor + * - add support for super_operations block_empty fn, to query fs for + * unused blocks that don't need reading + * - "cache" blocks that are contained in the UDF file/dir packet + * - rewrite pkt_gather_data to a one-step solution + * - add private pktcdvd elevator + * - shutdown write access to device upon write failure + * - fix off-by-one bug in capacity + * - setup sourceforge project (packet-cd.sourceforge.net) + * - add more blk ioctls to pkt_ioctl + * - set inactive request queue head + * - change panic calls to BUG, better with kdb + * - have pkt_gather_data check correct block size and kill rq if wrong + * - rework locking + * - introduce per-pd queues, simplifies pkt_request + * - store pd in queuedata + * + *************************************************************************/ + +#define VERSION_CODE "v0.0.2d 24/09/2000 Jens Axboe (axboe@suse.de)" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* + * 32 buffers of 2048 bytes + */ +#define PACKET_MAX_SIZE 32 + +#define NEXT_BH(bh, nbh) (((bh)->b_rsector + ((bh)->b_size >> 9)) == (nbh)->b_rsector) + +#define BH_IN_ORDER(b1, b2) ((b1)->b_rsector < (b2)->b_rsector) + +#define CONTIG_BH(b1, b2) ((b1)->b_data + (b1)->b_size == (b2)->b_data) + +#define ZONE(sector, span, offset) \ + (((sector) + (offset)) - (((sector) + (offset)) % (span))) + +static int *pkt_sizes; +static int *pkt_blksize; +static int *pkt_readahead; +static struct pktcdvd_device *pkt_devs; + +/* + * a bit of a kludge, but we want to be able to pass both real and packet + * dev and get the right one. + */ +static inline struct pktcdvd_device *pkt_find_dev(kdev_t dev) +{ + int i; + + for (i = 0; i < MAX_WRITERS; i++) + if (pkt_devs[i].dev == dev || pkt_devs[i].pkt_dev == dev) + return &pkt_devs[i]; + + return NULL; +} + +static void pkt_recheck_segments(struct request *rq) +{ + struct buffer_head *bh; + int nr_segments = 1, sectors; + + bh = rq->bh; + sectors = bh->b_size >> 9; + + while (bh->b_reqnext) { + if (!CONTIG_BH(bh, bh->b_reqnext)) + nr_segments++; + bh = bh->b_reqnext; + sectors += bh->b_size >> 9; + } + + /* + * this will happen + */ + if (nr_segments != rq->nr_segments) + rq->nr_segments = nr_segments; + + if (sectors != rq->nr_sectors) { + printk("%u != %lu\n", sectors, rq->nr_sectors); + BUG(); + } +} + +/* + * The following three functions are the plugins to the ll_rw_blk + * layer and decides whether a given request / buffer head can be + * merged. We differ in a couple of ways from "normal" block + * devices: + * + * - don't merge when the buffer / request crosses a packet block + * boundary + * - merge buffer head even though it can't be added directly to the + * front or back of the list. this gives us better performance, since + * what would otherwise require multiple requests can now be handled + * in one (hole merging) + * - we only care about write merging, reads use device original defaults. + * + * The device original merge_ functions are stored in the packet device + * queue (pd->q) + * + */ +static int pkt_front_merge_fn(request_queue_t *q, struct request *rq, + struct buffer_head *bh, int max_segs) +{ + struct pktcdvd_device *pd = pkt_find_dev(rq->rq_dev); + struct packet_cdrw *cdrw = &pd->cdrw; + int span = pd->settings.size, ret; + void *ptr = q->queuedata; + + /* + * dev might not be setup yet, so check span + */ + if (span) { + if (ZONE(rq->sector, span, pd->offset) != + ZONE(bh->b_rsector, span, pd->offset)) + return ELEVATOR_NO_MERGE; + } + + q->queuedata = cdrw->queuedata; + ret = cdrw->front_merge_fn(q, rq, bh, max_segs); + q->queuedata = ptr; + return ret; +} + +static int pkt_back_merge_fn(request_queue_t *q, struct request *rq, + struct buffer_head *bh, int max_segs) +{ + struct pktcdvd_device *pd = pkt_find_dev(rq->rq_dev); + struct packet_cdrw *cdrw = &pd->cdrw; + int span = pd->settings.size, ret; + void *ptr = q->queuedata; + + /* + * dev might not be setup yet, so check span + */ + if (span) { + if (ZONE(rq->sector, span, pd->offset) != + ZONE(bh->b_rsector, span, pd->offset)) + return ELEVATOR_NO_MERGE; + } + + q->queuedata = cdrw->queuedata; + ret = cdrw->back_merge_fn(q, rq, bh, max_segs); + q->queuedata = ptr; + return ret; +} + +/* + * rules similar to above + */ +static int pkt_merge_requests_fn(request_queue_t *q, struct request *rq, + struct request *next, int max_segs) +{ + struct pktcdvd_device *pd = pkt_find_dev(rq->rq_dev); + struct packet_cdrw *cdrw = &pd->cdrw; + int span = pd->settings.size, ret; + void *ptr = q->queuedata; + + if (span) { + if (ZONE(rq->sector, span, pd->offset) != + ZONE(next->sector + next->nr_sectors, span, pd->offset)) + return 0; + } + + q->queuedata = cdrw->queuedata; + ret = cdrw->merge_requests_fn(q, rq, next, max_segs); + q->queuedata = ptr; + return ret; +} + +/* + * The logic here is try queue default merge first and if is says ok, + * fine. If not, we find the appropriate place to insert this buffer + * head in the list, so that they are ordered before we receive the + * request. + */ +static int pkt_hole_merge_fn(request_queue_t *q, struct request *rq, + struct buffer_head *bh, int max_segs) +{ + struct pktcdvd_device *pd = pkt_find_dev(rq->rq_dev); + int span = pd->settings.size; + struct buffer_head *tmp; + + /* + * holes only supported for writing + */ + if (rq->cmd == READ) + return ELEVATOR_NO_MERGE; + + /* + * there may be a possibility for the back buffer to be + * added twice. if this triggers, let me know. + */ + if (bh->b_rsector == rq->bhtail->b_rsector) { + printk("pktcdvd: tell jens, tail dupe (%lu)\n", bh->b_rsector); + BUG(); + } + + if (span) { + if (ZONE(rq->sector, span, pd->offset) != + ZONE(bh->b_rsector, span, pd->offset)) + return ELEVATOR_NO_MERGE; + } + + if (rq->nr_segments >= max_segs) + return ELEVATOR_NO_MERGE; + + /* + * stuff in front? + */ + if (bh->b_rsector < rq->sector) { + bh->b_reqnext = rq->bh; + rq->bh = bh; + rq->sector = rq->hard_sector = bh->b_rsector; + rq->current_nr_sectors = bh->b_size >> 9; + goto out; + } + + /* + * stuff in back? + */ + if (bh->b_rsector > rq->bhtail->b_rsector) { + rq->bhtail->b_reqnext = bh; + rq->bhtail = bh; + goto out; + } + + /* + * find sweet spot to insert buffer + */ + for (tmp = rq->bh; tmp->b_reqnext; tmp = tmp->b_reqnext) { + if (tmp->b_rsector == bh->b_rsector) { + spin_unlock_irq(&io_request_lock); + BUG(); + } + if (BH_IN_ORDER(tmp, bh) && BH_IN_ORDER(bh, tmp->b_reqnext)) + break; + } + + bh->b_reqnext = tmp->b_reqnext; + tmp->b_reqnext = bh; +out: + rq->buffer = rq->bh->b_data; + rq->nr_sectors += (bh->b_size >> 9); + rq->hard_nr_sectors = rq->nr_sectors; + rq->nr_segments++; + return ELEVATOR_HOLE_MERGE; +} + +static int pkt_grow_bhlist(struct pktcdvd_device *pd, int count) +{ + struct packet_cdrw *cdrw = &pd->cdrw; + struct buffer_head *bh; + int i = 0; + + while (i < count) { + bh = kmalloc(sizeof(struct buffer_head), GFP_KERNEL); + if (bh == NULL) + break; + + bh->b_data = kmalloc(CD_FRAMESIZE, GFP_KERNEL); + if (bh->b_data == NULL) { + kfree(bh); + break; + } + spin_lock_irq(&pd->lock); + bh->b_pprev = &cdrw->bhlist; + bh->b_next = cdrw->bhlist; + cdrw->bhlist = bh; + spin_unlock_irq(&pd->lock); + + bh->b_size = CD_FRAMESIZE; + bh->b_list = PKT_BUF_LIST; + atomic_inc(&cdrw->free_bh); + i++; + } + return i; +} + +static int pkt_shrink_bhlist(struct pktcdvd_device *pd, int count) +{ + struct packet_cdrw *cdrw = &pd->cdrw; + struct buffer_head *bh; + int i = 0; + + spin_lock_irq(&pd->lock); + while ((i < count) && cdrw->bhlist) { + bh = cdrw->bhlist; + cdrw->bhlist = bh->b_next; + if (bh->b_list != PKT_BUF_LIST) { + printk("found %d list in shrink\n", bh->b_list); + BUG(); + } + kfree(bh->b_data); + kfree(bh); + atomic_dec(&cdrw->free_bh); + i++; + } + spin_unlock_irq(&pd->lock); + return i; +} + +static request_queue_t *pkt_my_queue(kdev_t dev) +{ + struct pktcdvd_device *pd = pkt_find_dev(dev); + return &pd->cdrw.r_queue; +} + +static void pkt_end_io_read(struct buffer_head *bh, int uptodate) +{ + mark_buffer_uptodate(bh, uptodate); + unlock_buffer(bh); +} + +static inline struct buffer_head *pkt_get_hash(kdev_t dev, unsigned long block, + int size) +{ + struct buffer_head *bh; + + bh = get_hash_table(dev, block, size); + if (bh) { + if (!test_and_set_bit(BH_Lock, &bh->b_state)) + return bh; + brelse(bh); + } + return NULL; +} + +static void pkt_end_io_write(struct buffer_head *bh, int uptodate); + +static struct buffer_head *pkt_get_buffer(struct pktcdvd_device *pd, + unsigned long sector, int size) +{ + struct buffer_head *bh; + + if ((bh = pkt_get_hash(pd->pkt_dev, sector / (size >> 9), size))) { + bh->b_private = pd; + bh->b_end_io = pkt_end_io_write; + goto out; + } + + /* + * should not happen... + */ + if (!atomic_read(&pd->cdrw.free_bh)) { + printk("pktcdvd: no buffers available!\n"); + return NULL; + } + + atomic_dec(&pd->cdrw.free_bh); + atomic_inc(&pd->cdrw.pending_bh); + + spin_lock_irq(&pd->lock); + bh = pd->cdrw.bhlist; + pd->cdrw.bhlist = bh->b_next; + bh->b_next = NULL; + spin_unlock_irq(&pd->lock); + + init_buffer(bh, pkt_end_io_read, pd); + + bh->b_next_free = NULL; + bh->b_prev_free = NULL; + bh->b_this_page = NULL; + bh->b_pprev = NULL; + bh->b_reqnext = NULL; + + init_waitqueue_head(&bh->b_wait); + atomic_set(&bh->b_count, 1); + bh->b_blocknr = sector / (size >> 9); + bh->b_list = PKT_BUF_LIST; + bh->b_state = (1 << BH_Mapped) | (1 << BH_Lock); +out: + bh->b_state |= (1 << BH_Req); + bh->b_rsector = sector; + bh->b_rdev = pd->dev; + return bh; +} + +static void pkt_put_buffer(struct buffer_head *bh) +{ + struct pktcdvd_device *pd = bh->b_private; + unsigned long flags; + + if (bh->b_list != PKT_BUF_LIST) + BUG(); + + bh->b_private = NULL; + bh->b_state = 0; + bh->b_reqnext = NULL; + + spin_lock_irqsave(&pd->lock, flags); + bh->b_next = pd->cdrw.bhlist; + pd->cdrw.bhlist = bh; + spin_unlock_irqrestore(&pd->lock, flags); + atomic_inc(&pd->cdrw.free_bh); +} + +/* + * we use this as our default b_end_io handler, since we need to take + * the entire request off the list if just on of the clusters fail. + * later one this should also talk to UDF about relocating blocks -- for + * now we just drop the rq entirely. when doing the relocating we must also + * lock the bh down to ensure that we can easily reconstruct the write should + * it fail. + */ +static void pkt_end_io_write(struct buffer_head *bh, int uptodate) +{ + struct pktcdvd_device *pd = (struct pktcdvd_device *) bh->b_private; + struct request *rq; + + if (pd->rq == NULL) + BUG(); + + spin_lock(&pd->lock); + rq = pd->rq; + if (rq->bhtail->b_rsector == bh->b_rsector) { + printk("finished last\n"); + pd->rq = NULL; + } + spin_unlock(&pd->lock); + + mark_buffer_uptodate(bh, uptodate); + unlock_buffer(bh); + brelse(bh); + + if (bh->b_list == PKT_BUF_LIST) { + pkt_put_buffer(bh); + + if (atomic_dec_and_test(&pd->cdrw.pending_bh)) { + if (pd->rq == NULL) { + printk("doing wakeup from end_io\n"); + wake_up(&pd->wqueue); + } + } + } + + /* + * obviously, more needs to be done here. + */ + if (!uptodate) { + printk("pktcdvd: %s: write error\n", pd->name); + set_bit(PACKET_READONLY, &pd->flags); + } + pd->stats.bh_e++; +} + +static void pkt_init_bh(struct pktcdvd_device *pd, struct request *rq) +{ + struct buffer_head *bh = rq->bh; + unsigned cnt = 0; + + while (bh) { + if (bh->b_list == PKT_BUF_LIST) + bh->b_end_io = pkt_end_io_write; + + /* + * the buffer better be uptodate, mapped, and locked! + */ + if (!buffer_uptodate(bh)) + printk("%lu not uptodate\n", bh->b_rsector); + if (!buffer_locked(bh)) + printk("%lu not locked\n", bh->b_rsector); + if (!buffer_mapped(bh)) + printk("%lu not mapped\n", bh->b_rsector); + + /* + * make sure that any leftover buffers get requeued to + * us and not the mapped device + */ + bh->b_rdev = pd->pkt_dev; + + /* + * if this happens, do report + */ + if (bh->b_reqnext) { + if ((bh->b_rsector + (bh->b_size >> 9)) != bh->b_reqnext->b_rsector) + printk("tell jens, %lu follows %lu\n", bh->b_reqnext->b_rsector, bh->b_rsector); + if (bh->b_rsector >= bh->b_reqnext->b_rsector) + + printk("tell jens, order %lu >= %lu\n", bh->b_rsector, bh->b_reqnext->b_rsector); + } + bh = bh->b_reqnext; + cnt += rq->current_nr_sectors; + } + + if (cnt != rq->nr_sectors) { + printk("botched request %u (%lu)\n", cnt, rq->nr_sectors); + BUG(); + } +} + +/* + * really crude stats for now... + */ +static void pkt_account_rq(struct pktcdvd_device *pd, int read, int written, + int bs) +{ + pd->stats.bh_s += (written / bs); + pd->stats.secs_w += written; + pd->stats.secs_r += read; +} + +/* + * does request span two packets? 0 == yes, 1 == no + */ +static int pkt_same_zone(struct pktcdvd_device *pd, struct request *rq) +{ + int span = pd->settings.size; + + if (ZONE(rq->sector, span, pd->offset) != + ZONE(rq->sector + rq->nr_sectors - 1, span, pd->offset)) + return 0; + + return 1; +} + +#if 0 +static void pkt_init_buffer(struct buffer_head *bh) +{ + set_bit(BH_Uptodate, &bh->b_state); + set_bit(BH_Dirty, &bh->b_state); + memset(bh->b_data, 0, bh->b_size); +} +#endif + +/* + * basically just does a ll_rw_block for the bhs given to use, but we + * don't return until we have them. + */ +static void pkt_read_bh(struct pktcdvd_device *pd, struct buffer_head *bh) +{ +//#ifdef CONFIG_CDROM_PKTCDVD_BLOCKFREE +#if 0 + struct super_block *sb = get_super(pd->pkt_dev); + struct super_operations *sop = sb ? sb->s_op : NULL; + unsigned long packet = 0, blocknr = bh->b_blocknr; + + if (sop && sop->block_empty) { + if (sop->block_empty(sb, blocknr, &packet)) { + pkt_init_buffer(pd, bh); + return; + } + } +#endif + atomic_inc(&bh->b_count); + generic_make_request(READ, bh); + VPRINTK("waiting on buffer %lu\n", bh->b_rsector); + lock_buffer(bh); + VPRINTK("got buffer\n"); + + /* + * read error, whole packet should be remapped + */ + if (!buffer_uptodate(bh)) + printk("huh, %lu not uptodate\n", bh->b_rsector); + + atomic_dec(&bh->b_count); +} + +inline void __pkt_kill_request(struct request *rq, int uptodate, char *name) +{ + while (end_that_request_first(rq, uptodate, name)) + ; + end_that_request_last(rq); +} + + +void pkt_kill_request(struct request *rq, int uptodate, char *name, char *msg) +{ + printk("pktcdvd: killing request, reason: %s\n", msg); + spin_lock_irq(&io_request_lock); + __pkt_kill_request(rq, uptodate, name); + spin_unlock_irq(&io_request_lock); +} + +/* + * fill in the holes of a request + * + * Returns: 0, keep 'em coming -- 1, stop queueing + */ +static int pkt_gather_data(struct pktcdvd_device *pd, struct request *rq) +{ + unsigned long start_s, end_s, sector; + struct buffer_head *bh; + unsigned int sectors; + + /* + * all calculations are done with 512 byte sectors + */ + sectors = pd->settings.size - rq->nr_sectors; + start_s = rq->sector - (rq->sector % pd->settings.size); + end_s = start_s + pd->settings.size; + + VPRINTK("need %d sectors for %s\n", sectors, kdevname(pd->dev)); + VPRINTK("from %lu to %lu ", start_s, end_s); + VPRINTK("(%lu - %lu)\n", rq->bh->b_rsector, rq->bhtail->b_rsector + + rq->current_nr_sectors); + + if (blksize_size[MAJOR(pd->dev)]) { + if (rq->bh->b_size != blksize_size[MAJOR(pd->dev)][MINOR(pd->dev)]) { + printk("pktcdvd: wrong (%u) block size\n", rq->bh->b_size); + pkt_kill_request(rq, 0, pd->name, "eek"); + return 0; + } + } + + /* + * get remaining blocks + */ + bh = rq->bh; + for (sector = start_s; sector < end_s; sector += (bh->b_size >> 9)) { + struct buffer_head *foo_bh; + + if (sector < bh->b_rsector) + goto new; + + if (sector == bh->b_rsector) + continue; + + if (bh->b_reqnext && NEXT_BH(bh, bh->b_reqnext)) { + bh = bh->b_reqnext; + continue; + } + + /* + * new buffer -- first search the buffer cache, if it's + * not there grab one from our pool + */ + new: + foo_bh = pkt_get_buffer(pd, sector, bh->b_size); + if (!buffer_uptodate(foo_bh)) + pkt_read_bh(pd, foo_bh); + + if (sector < bh->b_rsector) { + foo_bh->b_reqnext = bh; + rq->bh = foo_bh; + } else { + foo_bh->b_reqnext = bh->b_reqnext; + bh->b_reqnext = foo_bh; + } + bh = foo_bh; + rq->nr_sectors += (bh->b_size >> 9); + if (bh->b_rsector > rq->bhtail->b_rsector) + rq->bhtail = bh; + } + + rq->buffer = rq->bh->b_data; + rq->current_nr_sectors = rq->bh->b_size >> 9; + rq->hard_nr_sectors = rq->nr_sectors; + rq->sector = rq->hard_sector = start_s; + rq->cmd = WRITE_PACKET; + + VPRINTK("unlocked last %lu\n", rq->bhtail->b_rsector); + pkt_recheck_segments(rq); + pkt_init_bh(pd, rq); + pkt_account_rq(pd, sectors, rq->nr_sectors, rq->current_nr_sectors); + + /* + * sanity check + */ + if (rq->nr_sectors != pd->settings.size) { + printk("pktcdvd: request mismatch %lu (should be %u)\n", + rq->nr_sectors, pd->settings.size); + BUG(); + } + return 0; +} + +static inline void pkt_plug_queue(request_queue_t *q, kdev_t dev) +{ + if (list_empty(&q->queue_head)) + q->plug_device_fn(q, dev); +} + +static inline void __pkt_inject_request(request_queue_t *q, struct request *rq) +{ + int lat = elevator_request_latency(&q->elevator, rq->cmd); + struct list_head *head = &q->queue_head; + + pkt_plug_queue(q, rq->rq_dev); + + list_del(&rq->queue); + + if (q->head_active && !q->plugged) + head = head->next; + + q->elevator.elevator_fn(rq, &q->elevator, &q->queue_head, head, lat); +} + +static void pkt_inject_request(request_queue_t *q, struct request *rq) +{ + spin_lock_irq(&io_request_lock); + __pkt_inject_request(q, rq); + spin_unlock_irq(&io_request_lock); +} + +/* + * Returns: 1, keep 'em coming -- 0, wait for wakeup + */ +static int pkt_do_request(struct pktcdvd_device *pd, struct request *rq, + request_queue_t *pdq) +{ + /* + * perfect match. the merge_* functions have already made sure that + * a request doesn't cross a packet boundary, so if the sector + * count matches it's good. + */ + if (rq->nr_sectors == pd->settings.size) { + rq->cmd = WRITE_PACKET; + spin_lock_irq(&pd->lock); + pd->rq = NULL; + spin_unlock_irq(&pd->lock); + pkt_account_rq(pd, 0, rq->nr_sectors, rq->current_nr_sectors); + return 1; + } + + /* + * paranoia... + */ + if (rq->nr_sectors > pd->settings.size) { + printk("pktcdvd: request too big! BUG! %lu\n", rq->nr_sectors); + BUG(); + } + + return pkt_gather_data(pd, rq); +} + +#define list_next_entry(head, type, mbr) list_entry((head)->next, type, mbr) +/* + * handle the requests that got queued for this writer + * + * Locks: pd->lock + * + */ +static int pkt_handle_queue(struct pktcdvd_device *pd, request_queue_t *q) +{ + struct request *rq; + int ret; + + spin_lock_irq(&pd->lock); + rq = pd->rq; + spin_unlock_irq(&pd->lock); + + if (rq->cmd == READ) + BUG(); + + if ((rq->current_nr_sectors << 9) != CD_FRAMESIZE) { + pkt_kill_request(rq, 0, pd->name, "wrong size"); + return 1; + } + + ret = pkt_do_request(pd, rq, q); + + pkt_inject_request(q, rq); + return ret; +} + +/* + * kpacketd is woken up, when writes have been queued for one of our + * registered devices + */ +static int kcdrwd(void *foobar) +{ + struct pktcdvd_device *pd = foobar; + request_queue_t *q, *my_queue; + + set_bit(PACKET_THREAD, &pd->flags); + daemonize(); + exit_files(current); + + printk("pktcdvd: kernel thread %s started\n", pd->name); + + current->session = 1; + current->pgrp = 1; + current->policy = SCHED_OTHER; + current->nice = -20; + sprintf(current->comm, pd->name); + + spin_lock_irq(¤t->sigmask_lock); + siginitsetinv(¤t->blocked, sigmask(SIGKILL)); + flush_signals(current); + spin_unlock_irq(¤t->sigmask_lock); + + q = blk_get_queue(pd->dev); + my_queue = blk_get_queue(pd->pkt_dev); + + for (;;) { + DECLARE_WAITQUEUE(wait, current); + + add_wait_queue(&pd->wqueue, &wait); + set_current_state(TASK_INTERRUPTIBLE); + + /* + * if pkt_handle_queue returns true, we can queue + * another request. otherwise we need to unplug the + * cd-rom queue and wait for buffers to be flushed + * (which will then wake us up again when done). + */ + do { + spin_lock_irq(&io_request_lock); + if (list_empty(&my_queue->queue_head)) { + spin_unlock_irq(&io_request_lock); + generic_unplug_device(q); + break; + } + my_queue->request_fn(my_queue); + spin_unlock_irq(&io_request_lock); + + spin_lock(&pd->lock); + if (pd->rq) { + spin_unlock(&pd->lock); + pkt_handle_queue(pd, q); + } else + spin_unlock(&pd->lock); + + generic_unplug_device(q); + } while (pd->rq == NULL); + + schedule(); + set_current_state(TASK_RUNNING); + remove_wait_queue(&pd->wqueue, &wait); + + /* + * got SIGKILL + */ + if (signal_pending(current)) + break; + + } + printk("pktcdvd: kernel thread %s stopped\n", pd->name); + clear_bit(PACKET_THREAD, &pd->flags); + return 0; +} + +/* + * our request function. we empty the request list in one go and queue + * requests for the writer. three main scenarios here: + * + * - reads are just tossed directly to the device, we don't care. + * - writes that fit exactly with the packet size are tossed directly at + * the device. our merge functions have already made sure that a request + * does not span two packet boundaries. + * - write does not cover entire packet span -- invoke pkt_gather_data + * to get the blocks that are missing. + * + * Locks: io_request_lock held + * + * Notes: all writers have their own queue, so all requests are for the + * the same device + */ +static void pkt_request(request_queue_t *q) +{ + struct pktcdvd_device *pd = (struct pktcdvd_device *) q->queuedata; + request_queue_t *pdq = NULL; + + if (list_empty(&q->queue_head)) + return; + + pdq = __blk_get_queue(pd->dev); + + while (!list_empty(&q->queue_head)) { + struct request *rq = blkdev_entry_next_request(&q->queue_head); + + rq->rq_dev = pd->dev; + + /* + * rq will be dequeued in __inject + */ + if (rq->cmd == READ) { + __pkt_inject_request(pdq, rq); + continue; + } + + /* + * UDF had a bug, where it submitted a write to a ro file + * system, this is just to prevent accidents like that from + * happening again + */ + if (test_bit(PACKET_READONLY, &pd->flags)) { + __pkt_kill_request(rq, 0, pd->name); + continue; + } + + /* + * paranoia, shouldn't trigger... + */ + if (!pkt_same_zone(pd, rq)) + BUG(); + + /* + * already gathering data for another read. the + * rfn will be reinvoked once that is done + */ + spin_lock_irq(&pd->lock); + if (pd->rq) { + spin_unlock_irq(&pd->lock); + return; + } + + blkdev_dequeue_request(rq); + pd->rq = rq; + spin_unlock_irq(&pd->lock); + } + + wake_up(&pd->wqueue); +} + +static void pkt_print_settings(struct pktcdvd_device *pd) +{ + printk("pktcdvd: %s packets, ", pd->settings.fp ? "Fixed" : "Variable"); + printk("%u blocks / packet, ", pd->settings.size >> 2); + printk("Mode-%c disc\n", pd->settings.block_mode == 8 ? '1' : '2'); +} + +/* + * A generic sense dump / resolve mechanism should be implemented across + * all ATAPI + SCSI devices. + */ +static void pkt_dump_sense(struct request_sense *sense) +{ + char *info[9] = { "No sense", "Recovered error", "Not ready", + "Medium error", "Hardware error", "Illegal request", + "Unit attention", "Data protect", "Blank check" }; + + if (sense == NULL) + return; + + if (sense->sense_key > 8) { + printk("pktcdvd: sense invalid\n"); + return; + } + + printk("pktcdvd: sense category %s ", info[sense->sense_key]); + printk("asc(%02x), ascq(%02x)\n", sense->asc, sense->ascq); +} + +/* + * write mode select package based on pd->settings + */ +static int pkt_write_settings(struct pktcdvd_device *pd) +{ + struct cdrom_device_info *cdi = pd->cdi; + struct cdrom_generic_command cgc; + write_param_page *wp; + char buffer[128]; + int ret, size; + + memset(buffer, 0, sizeof(buffer)); + init_cdrom_command(&cgc, buffer, sizeof(*wp), CGC_DATA_READ); + if ((ret = cdrom_mode_sense(cdi, &cgc, GPMODE_WRITE_PARMS_PAGE, 0))) + return ret; + + size = 2 + ((buffer[0] << 8) | (buffer[1] & 0xff)); + pd->mode_offset = (buffer[6] << 8) | (buffer[7] & 0xff); + if (size > sizeof(buffer)) + size = sizeof(buffer); + + /* + * now get it all + */ + init_cdrom_command(&cgc, buffer, size, CGC_DATA_READ); + if ((ret = cdrom_mode_sense(cdi, &cgc, GPMODE_WRITE_PARMS_PAGE, 0))) + return ret; + + /* + * write page is offset header + block descriptor length + */ + wp = (write_param_page *) &buffer[sizeof(struct mode_page_header) + pd->mode_offset]; + + wp->fp = pd->settings.fp; + wp->track_mode = pd->settings.track_mode; + wp->write_type = pd->settings.write_type; + wp->data_block_type = pd->settings.block_mode; + + wp->multi_session = 0; + +#ifdef PACKET_USE_LS + wp->link_size = 7; + wp->ls_v = 1; +#endif + + if (wp->data_block_type == 8) { + wp->session_format = 0; + wp->subhdr2 = 0x20; + } else if (wp->data_block_type == 10) { + wp->session_format = 0x20; + wp->subhdr2 = 8; +#if 0 + wp->mcn[0] = 0x80; + memcpy(&wp->mcn[1], PACKET_MCN, sizeof(wp->mcn) - 1); +#endif + } else { + /* + * paranoia + */ + printk("pktcdvd: some how write mode got screwed (%d)\n", + wp->data_block_type); + return 1; + } + wp->packet_size = cpu_to_be32(pd->settings.size >> 2); + + cgc.buflen = cgc.cmd[8] = size; + if ((ret = cdrom_mode_select(cdi, &cgc))) { + pkt_dump_sense(cgc.sense); + return ret; + } + + pkt_print_settings(pd); + return 0; +} + +/* + * 0 -- we can write to this track, 1 -- we can't + */ +static int pkt_good_track(track_information *ti) +{ + /* + * only good for CD-RW at the moment, not DVD-RW + */ + + /* + * FIXME: only for FP + */ + if (ti->fp == 0) + return 0; + + /* + * "good" settings as per Mt Fuji. + */ + if (ti->rt == 0 && ti->blank == 0 && ti->packet == 1) + return 0; + + if (ti->rt == 0 && ti->blank == 1 && ti->packet == 1) + return 0; + + if (ti->rt == 1 && ti->blank == 0 && ti->packet == 1) + return 0; + + printk("pktcdvd: bad state %d-%d-%d\n", ti->rt, ti->blank, ti->packet); + return 1; +} + +/* + * 0 -- we can write to this disc, 1 -- we can't + */ +static int pkt_good_disc(struct pktcdvd_device *pd, disc_information *di) +{ + /* + * for disc type 0xff we should probably reserve a new track. + * but i'm not sure, should we leave this to user apps? probably. + */ + if (di->disc_type == 0xff) { + printk("pktcdvd: Unknown disc. No track?\n"); + return 1; + } + + if (di->disc_type != 0x20 && di->disc_type != 0) { + printk("pktcdvd: Wrong disc type (%x)\n", di->disc_type); + return 1; + } + + if (di->erasable == 0) { + printk("pktcdvd: Disc not erasable\n"); + return 1; + } + + if (pd->track_status == PACKET_SESSION_RESERVED) { + printk("pktcdvd: Can't write to last track (reserved)\n"); + return 1; + } + + return 0; +} + +static int pkt_probe_settings(struct pktcdvd_device *pd) +{ + disc_information di; + track_information ti; + int ret, track; + + memset(&di, 0, sizeof(disc_information)); + memset(&ti, 0, sizeof(track_information)); + + if ((ret = cdrom_get_disc_info(pd->dev, &di))) { + printk("failed get_disc\n"); + return ret; + } + + pd->disc_status = di.disc_status; + pd->track_status = di.border_status; + + if (pkt_good_disc(pd, &di)) + return -ENXIO; + + printk("pktcdvd: inserted media is CD-R%s\n", di.erasable ? "W" : ""); + pd->type = di.erasable ? PACKET_CDRW : PACKET_CDR; + + track = 1; /* (di.last_track_msb << 8) | di.last_track_lsb; */ + if ((ret = cdrom_get_track_info(pd->dev, track, 1, &ti))) { + printk("pktcdvd: failed get_track\n"); + return ret; + } + + if (pkt_good_track(&ti)) { + printk("pktcdvd: can't write to this track\n"); + return -ENXIO; + } + + /* + * we keep packet size in 512 byte units, makes it easier to + * deal with request calculations. + */ + pd->settings.size = be32_to_cpu(ti.fixed_packet_size) << 2; + if (pd->settings.size == 0) { + printk("pktcdvd: detected zero packet size!\n"); + pd->settings.size = 128; + } + pd->settings.fp = ti.fp; + pd->offset = (be32_to_cpu(ti.track_start) << 2) % pd->settings.size; + + if (ti.nwa_v) { + pd->nwa = be32_to_cpu(ti.next_writable); + set_bit(PACKET_NWA_VALID, &pd->flags); + } + + /* + * in theory we could use lra on -RW media as well and just zero + * blocks that haven't been written yet, but in practice that + * is just a no-go. we'll use that for -R, naturally. + */ + if (ti.lra_v) { + pd->lra = be32_to_cpu(ti.last_rec_address); + set_bit(PACKET_LRA_VALID, &pd->flags); + } else { + pd->lra = 0xffffffff; + set_bit(PACKET_LRA_VALID, &pd->flags); + } + + /* + * fine for now + */ + pd->settings.link_loss = 7; + pd->settings.write_type = 0; /* packet */ + pd->settings.track_mode = ti.track_mode; + + /* + * mode1 or mode2 + */ + switch (ti.data_mode) { + case 1: pd->settings.block_mode = 8; break; + case 2: pd->settings.block_mode = 10; break; + default: printk("pktcdvd: unknown data mode\n"); return 1; + } + return 0; +} + +/* + * enable/disable write caching on drive + */ +static int pkt_write_caching(struct pktcdvd_device *pd, int set) +{ + struct cdrom_generic_command cgc; + unsigned char buf[64]; + int ret; + + memset(buf, 0, sizeof(buf)); + init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_READ); + cgc.buflen = pd->mode_offset + 12; + + if ((ret = cdrom_mode_sense(pd->cdi, &cgc, GPMODE_WCACHING_PAGE, 0))) + return ret; + + buf[pd->mode_offset + 10] |= (!!set << 2); + + cgc.buflen = cgc.cmd[8] = 2 + ((buf[0] << 8) | (buf[1] & 0xff)); + if (!(ret = cdrom_mode_select(pd->cdi, &cgc))) + printk("pktcdvd: %sabled write caching on %s\n", set ? "en" : "dis", pd->name); + return ret; +} + +/* + * flush the drive cache to media + */ +static int pkt_flush_cache(struct pktcdvd_device *pd) +{ + struct cdrom_generic_command cgc; + + init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE); + cgc.cmd[0] = GPCMD_FLUSH_CACHE; + + /* + * the IMMED bit -- we default to not setting it, although that + * would allow a much faster close + */ +#if 0 + cgc.cmd[1] = 1 << 1; +#endif + return pd->cdi->ops->generic_packet(pd->cdi, &cgc); +} + +/* + * Returns drive current write speed + */ +static int pkt_get_speed(struct pktcdvd_device *pd) +{ + struct cdrom_generic_command cgc; + unsigned char buf[64]; + int ret, offset; + + memset(buf, 0, sizeof(buf)); + init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_UNKNOWN); + + ret = cdrom_mode_sense(pd->cdi, &cgc, GPMODE_CAPABILITIES_PAGE, 0); + if (ret) { + cgc.buflen = pd->mode_offset + buf[pd->mode_offset + 9] + 2 + + sizeof(struct mode_page_header); + ret = cdrom_mode_sense(pd->cdi, &cgc, GPMODE_CAPABILITIES_PAGE, 0); + if (ret) + return ret; + } + + offset = pd->mode_offset + 26; + pd->speed = ((buf[offset] << 8) | buf[offset + 1]) / 0xb0; + return 0; +} + +/* + * speed is given as the normal factor, e.g. 4 for 4x + */ +static int pkt_set_speed(struct pktcdvd_device *pd, unsigned speed) +{ + struct cdrom_generic_command cgc; + unsigned read_speed; + + /* + * we set read and write time so that read spindle speed is one and + * a half as fast as write. although a drive can typically read much + * faster than write, this minimizes the spin up/down when we write + * and gather data. maybe 1/1 factor is faster, needs a bit of testing. + */ + speed = speed * 0xb0; + read_speed = (speed * 3) >> 1; + + init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE); + cgc.cmd[0] = 0xbb; + cgc.cmd[2] = (read_speed >> 8) & 0xff; + cgc.cmd[3] = read_speed & 0xff; + cgc.cmd[4] = (speed >> 8) & 0xff; + cgc.cmd[5] = speed & 0xff; + + return pd->cdi->ops->generic_packet(pd->cdi, &cgc); +} + +/* + * Give me full power, Captain + */ +static int pkt_max_speed(struct pktcdvd_device *pd) +{ + disc_information di; + int ret; + + /* + * FIXME: do proper unified cap page, also, this isn't proper + * Mt Fuji, but I think we can safely assume all drives support + * it. A hell of a lot more than support the GET_PERFORMANCE + * command (besides, we also use the old set speed command, + * not the streaming feature). + */ + if ((ret = pkt_set_speed(pd, 8))) + return ret; + + /* + * just do something with the disc -- next read will contain the + * maximum speed with this media + */ + if ((ret = cdrom_get_disc_info(pd->dev, &di))) + return ret; + + if ((ret = pkt_get_speed(pd))) { + printk("pktcdvd: failed get speed\n"); + return ret; + } + + DPRINTK("pktcdvd: speed (R/W) %u/%u\n", (pd->speed * 3) / 2, pd->speed); + return 0; +} + +static int pkt_lock_tray(struct pktcdvd_device *pd, int lock) +{ + return pd->cdi->ops->lock_door(pd->cdi, !!lock); +} + +#if 0 +static int pkt_track_capacity(struct pktcdvd_device *pd) +{ + disc_information di; + track_information ti; + int l_track, i, ret; + unsigned long size = 0; + + memset(&di, 0, sizeof(disc_information)); + memset(&ti, 0, sizeof(track_information)); + + if ((ret = cdrom_get_disc_info(pd->dev, &di))) { + DPRINTK("failed get_disc\n"); + return ret; + } + + l_track = di.last_track_lsb | di.last_track_msb >> 8; + DPRINTK("pktcdvd: last track %d\n", l_track); + for (i = di.n_first_track; i <= l_track; i++) { + if ((ret = cdrom_get_track_info(pd->dev, i, 1, &ti))) { + DPRINTK("pktcdvd: failed get_track\n"); + return ret; + } + size += be32_to_cpu(ti.track_size); + } + pkt_sizes[MINOR(pd->pkt_dev)] = size << 1; + return 0; +} + +static int pkt_set_capacity(struct pktcdvd_device *pd) +{ + struct cdrom_generic_command cgc; + struct cdrom_device_info *cdi = pd->cdi; + struct cdvd_capacity cap; + int ret; + + init_cdrom_command(&cgc, &cap, sizeof(cap)); + cgc.cmd[0] = GPCMD_READ_CDVD_CAPACITY; + if ((ret = cdi->ops->generic_packet(cdi, &cgc))) + return ret; + + /* + * We should probably give up if read capacity fails, since then + * then disc is not ready to be written to -- for now I use + * raw devices and this is fine. + */ + pkt_sizes[MINOR(pd->pkt_dev)] = be32_to_cpu(cap.lba) << 1; + return 0; +} +#endif + +static int pkt_open_write(struct pktcdvd_device *pd) +{ + int ret; + + if ((ret = pkt_probe_settings(pd))) { + DPRINTK("pktcdvd: %s failed probe\n", pd->name); + return -EIO; + } + + if ((ret = pkt_write_settings(pd))) { + DPRINTK("pktcdvd: %s failed saving write settings\n", pd->name); + return -EIO; + } + + (void) pkt_write_caching(pd, USE_WCACHING); + + if ((ret = pkt_max_speed(pd))) { + DPRINTK("pktcdvd: %s couldn't set write speed\n", pd->name); + return -EIO; + } + return 0; +} + +/* + * called at open time. return 1 if the device can only be opened read-only. + */ +static int pkt_open_dev(struct pktcdvd_device *pd, int write) +{ + int ret; + long lba; + + if (!pd->dev) + return 0; + + if ((ret = cdrom_get_last_written(pd->dev, &lba))) + return ret; + + pkt_sizes[MINOR(pd->pkt_dev)] = 1 + (lba << 1); + + if (write) { + if ((ret = pkt_open_write(pd))) + return ret; + clear_bit(PACKET_READONLY, &pd->flags); + } else { + set_bit(PACKET_READONLY, &pd->flags); + } + + if (write) + printk("pktcdvd: %luKB available on disc\n", lba << 1); + return 0; +} + +/* + * called when the device is closed. makes sure that the device flushes + * the internal cache before we close. + */ +static void pkt_release_dev(struct pktcdvd_device *pd, int flush) +{ + fsync_dev(pd->pkt_dev); + invalidate_buffers(pd->pkt_dev); + + if (flush) + if (pkt_flush_cache(pd)) + DPRINTK("pktcdvd: %s not flushing cache\n", pd->name); + + atomic_dec(&pd->refcnt); +} + +static int pkt_open(struct inode *inode, struct file *file) +{ + struct pktcdvd_device *pd = NULL; + int ret = 0; + + VPRINTK("pktcdvd: entering open\n"); + + MOD_INC_USE_COUNT; + + /* + * should this really be necessary?? + */ + if (!inode) { + MOD_DEC_USE_COUNT; + return -EINVAL; + } + + if (MINOR(inode->i_rdev) >= MAX_WRITERS) { + printk("pktcdvd: max %d writers supported\n", MAX_WRITERS); + MOD_DEC_USE_COUNT; + return -ENODEV; + } + + /* + * either device is not configured, or pktsetup is old and doesn't + * use O_CREAT to create device + */ + pd = &pkt_devs[MINOR(inode->i_rdev)]; + if (!pd->dev && !(file->f_flags & O_CREAT)) { + ret = -ENXIO; + goto out_dec; + } + + ret = -EBUSY; + atomic_inc(&pd->refcnt); + if ((atomic_read(&pd->refcnt) > 1) && (file->f_mode & FMODE_WRITE)) + goto out; + + ret = -EIO; + if (pkt_open_dev(pd, file->f_mode & FMODE_WRITE)) + goto out; + + /* + * needed here as well, since ext2 (among others) may change + * the blocksize at mount time + */ + set_blocksize(pd->pkt_dev, CD_FRAMESIZE); + return 0; +out_dec: + atomic_dec(&pd->refcnt); +out: + VPRINTK("pktcdvd: failed open\n"); + MOD_DEC_USE_COUNT; + return ret; +} + +static int pkt_close(struct inode *inode, struct file *file) +{ + struct pktcdvd_device *pd = &pkt_devs[MINOR(inode->i_rdev)]; + int ret = 0; + + if (pd->dev) + pkt_release_dev(pd, 1); + MOD_DEC_USE_COUNT; + return ret; +} + +/* + * pktcdvd i/o elevator + * + * rules: always merge whenever possible, and support hole merges + */ +static void pkt_elevator(struct request *rq, elevator_t *elevator, + struct list_head *real_head, struct list_head *head, + int orig_latency) +{ + struct list_head *entry = real_head; + struct request *tmp; + int pass = 0; + + while ((entry = entry->prev) != head) { + tmp = blkdev_entry_to_request(entry); + if (IN_ORDER(tmp, rq) || (pass && !IN_ORDER(tmp, blkdev_next_request(tmp)))) + break; + pass = 1; + } + list_add(&rq->queue, entry); +} + +static int pkt_elevator_merge(request_queue_t *q, struct request **rq, + struct list_head *head, struct buffer_head *bh, + int rw, int max_secs, int max_segs) +{ + unsigned int count = bh->b_size >> 9; + int ret = ELEVATOR_NO_MERGE; + struct list_head *entry; + + entry = &q->queue_head; + while ((entry = entry->prev) != head && !ret) { + struct request *__rq = *rq = blkdev_entry_to_request(entry); + if (__rq->sem) + continue; + if (__rq->cmd != rw) + continue; + if (__rq->nr_sectors + count > max_secs) + continue; + if (__rq->rq_dev != bh->b_rdev) + continue; + if (__rq->sector + __rq->nr_sectors == bh->b_rsector) + ret = ELEVATOR_BACK_MERGE; + else if (__rq->sector - count == bh->b_rsector) + ret = ELEVATOR_FRONT_MERGE; + else if (q->hole_merge_fn(q, __rq, bh, max_segs)) + ret = ELEVATOR_HOLE_MERGE; + } + return ret; +} + +static void pkt_init_queue(struct pktcdvd_device *pd) +{ + request_queue_t *q = &pd->cdrw.r_queue; + + blk_init_queue(q, pkt_request); + blk_queue_headactive(q, 0); + elevator_init(&q->elevator, ELEVATOR_PKTCDVD); + q->front_merge_fn = pkt_front_merge_fn; + q->back_merge_fn = pkt_back_merge_fn; + q->hole_merge_fn = pkt_hole_merge_fn; + q->merge_requests_fn = pkt_merge_requests_fn; + q->queuedata = pd; +} + +static int pkt_new_dev(struct pktcdvd_device *pd, kdev_t dev) +{ + struct cdrom_device_info *cdi; + request_queue_t *q; + int i; + + for (i = 0; i < MAX_WRITERS; i++) { + if (pkt_devs[i].dev == dev) { + printk("pktcdvd: %s already setup\n", kdevname(dev)); + return -EBUSY; + } + } + + for (i = 0; i < MAX_WRITERS; i++) + if (pd == &pkt_devs[i]) + break; + + if (i == MAX_WRITERS) { + printk("pktcdvd: max %d writers supported\n", MAX_WRITERS); + return -ENXIO; + } + + cdi = cdrom_find_device(dev); + if (cdi == NULL) { + printk("pktcdvd: %s is not a CD-ROM\n", kdevname(dev)); + return -ENXIO; + } + + MOD_INC_USE_COUNT; + + memset(pd, 0, sizeof(struct pktcdvd_device)); + atomic_set(&pd->cdrw.pending_bh, 0); + atomic_set(&pd->cdrw.free_bh, 0); + spin_lock_init(&pd->lock); + if (pkt_grow_bhlist(pd, PACKET_MAX_SIZE) < PACKET_MAX_SIZE) { + printk("pktcdvd: not enough memory for buffers\n"); + return -ENOMEM; + } + set_blocksize(dev, CD_FRAMESIZE); + pd->cdi = cdi; + pd->dev = dev; + pd->pkt_dev = MKDEV(PACKET_MAJOR, i); + sprintf(pd->name, "pktcdvd%d", i); + atomic_set(&pd->refcnt, 0); + init_waitqueue_head(&pd->wqueue); + + /* + * store device merge functions (SCSI uses them to build + * scatter-gather tables) + */ + q = blk_get_queue(dev); + spin_lock_irq(&io_request_lock); + pkt_init_queue(pd); + pd->cdrw.front_merge_fn = q->front_merge_fn; + pd->cdrw.back_merge_fn = q->back_merge_fn; + pd->cdrw.merge_requests_fn = q->merge_requests_fn; + pd->cdrw.queuedata = q->queuedata; + spin_unlock_irq(&io_request_lock); + + pd->cdrw.pid = kernel_thread(kcdrwd, pd, CLONE_FS | CLONE_FILES | CLONE_SIGHAND); + + DPRINTK("pktcdvd: writer %s sucessfully registered\n", cdi->name); + return 0; +} + +/* + * arg contains file descriptor of CD-ROM device. + */ +static int pkt_setup_dev(struct pktcdvd_device *pd, unsigned int arg) +{ + struct inode *inode; + struct file *file; + int ret; + + if ((file = fget(arg)) == NULL) { + printk("pktcdvd: bad file descriptor passed\n"); + return -EBADF; + } + + ret = -EINVAL; + if ((inode = file->f_dentry->d_inode) == NULL) { + printk("pktcdvd: huh? file descriptor contains no inode?\n"); + goto out; + } + ret = -ENOTBLK; + if (!S_ISBLK(inode->i_mode)) { + printk("pktcdvd: device is not a block device (duh)\n"); + goto out; + } + ret = blkdev_get(inode->i_bdev, file->f_mode, file->f_flags, BDEV_FILE); + if (ret) + goto out; + ret = -EROFS; + if (IS_RDONLY(inode)) { + printk("pktcdvd: Can't write to read-only dev\n"); + goto out; + } + if ((ret = pkt_new_dev(pd, inode->i_rdev))) { + printk("pktcdvd: all booked up\n"); + goto out; + } + + pd->pkt_dentry = dget(file->f_dentry); + atomic_inc(&pd->refcnt); + + if ((ret = pkt_lock_tray(pd, 1))) + printk("pktcdvd: can't lock drive tray\n"); + +out: + fput(file); + return ret; +} + +static int pkt_remove_dev(struct pktcdvd_device *pd) +{ + int ret; + + /* + * will also invalidate buffers for CD-ROM + */ + blkdev_put(pd->pkt_dentry->d_inode->i_bdev, BDEV_FILE); + dput(pd->pkt_dentry); + invalidate_buffers(pd->pkt_dev); + + /* + * Unlock CD-ROM device + */ + (void) pkt_lock_tray(pd, 0); + + ret = pkt_shrink_bhlist(pd, PACKET_MAX_SIZE); + if (ret != PACKET_MAX_SIZE) + printk("pktcdvd: possible buffer leak (%d)\n", ret); + + if ((ret = kill_proc(pd->cdrw.pid, SIGKILL, 1)) == 0) { + int count = 10; + while (test_bit(PACKET_THREAD, &pd->flags) && --count) { + __set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ / 10); + } + if (!count) + printk("pkt_exit: can't kill kernel thread\n"); + } + + blk_cleanup_queue(&pd->cdrw.r_queue); + DPRINTK("pktcdvd: writer %s unregistered\n", pd->cdi->name); + memset(pd, 0, sizeof(struct pktcdvd_device)); + MOD_DEC_USE_COUNT; + return 0; +} + +static int pkt_media_change(kdev_t dev) +{ + struct pktcdvd_device *pd = pkt_find_dev(dev); + struct cdrom_device_info *cdi = pd->cdi; + + if (pd == NULL) + return 0; + + return cdi->ops->dev_ioctl(cdi, CDROM_MEDIA_CHANGED, CDSL_CURRENT); +} + +static int pkt_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct pktcdvd_device *pd = &pkt_devs[MINOR(inode->i_rdev)]; + + if ((cmd != PACKET_SETUP_DEV) && !pd->dev) { + DPRINTK("pktcdvd: dev not setup\n"); + return -ENXIO; + } + + switch (cmd) { + case PACKET_GET_STATS: + if (copy_to_user(&arg, &pd->stats, sizeof(struct packet_stats))) + return -EFAULT; + + case PACKET_SETUP_DEV: + if (pd->dev) { + printk("pktcdvd: dev already setup\n"); + return -EBUSY; + } + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + return pkt_setup_dev(pd, arg); + + case PACKET_TEARDOWN_DEV: + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (atomic_read(&pd->refcnt) != 1) + return -EBUSY; + return pkt_remove_dev(pd); + + case PACKET_WAKEUP: + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + wake_up(&pd->wqueue); + + case BLKGETSIZE: + return put_user(blk_size[PACKET_MAJOR][MINOR(inode->i_rdev)] << 1, (long *)arg); + + case BLKROSET: + case BLKROGET: + case BLKSSZGET: + case BLKRASET: + case BLKRAGET: + case BLKFLSBUF: + return blk_ioctl(inode->i_rdev, cmd, arg); + + /* + * forward selected CDROM ioctls to CD-ROM, for UDF + */ + case CDROMMULTISESSION: + case CDROMREADTOCENTRY: + case CDROM_LAST_WRITTEN: + case CDROM_SEND_PACKET: + return pd->cdi->ops->dev_ioctl(pd->cdi, cmd, arg); + + default: + printk("pktcdvd: Unknown ioctl for %s (%x)\n", pd->name, cmd); + return -ENOTTY; + } + + return 0; +} + +static struct block_device_operations pktcdvd_ops = { + open: pkt_open, + release: pkt_close, + ioctl: pkt_ioctl, + check_media_change: pkt_media_change, +}; + +static int list_nr_items(struct pktcdvd_device *pd, struct list_head *head, + spinlock_t *lock) +{ + struct list_head *foo; + int i; + + spin_lock_irq(lock); + if (list_empty(head)) { + spin_unlock_irq(lock); + return 0; + } + + i = 0; + list_for_each(foo, head) + i++; + + spin_unlock_irq(lock); + return i; +} + +static int pkt_proc_device(struct pktcdvd_device *pd, char *buf) +{ + char *b = buf; + + b += sprintf(b, "\nWriter %s (%s):\n", pd->name, kdevname(pd->dev)); + + b += sprintf(b, "\nSettings:\n"); + b += sprintf(b, "\tpacket size:\t\t%dKB\n", pd->settings.size / 2); + b += sprintf(b, "\tpacket type:\t\t%s\n", pd->settings.fp ? "fixed" : "variable"); + b += sprintf(b, "\tlink loss:\t\t%d\n", pd->settings.link_loss); + b += sprintf(b, "\twrite type:\t\t%d\n", pd->settings.write_type); + b += sprintf(b, "\ttrack mode:\t\t%d\n", pd->settings.track_mode); + b += sprintf(b, "\tblock mode:\t\t%d\n", pd->settings.block_mode); + + b += sprintf(b, "\nStatistics:\n"); + b += sprintf(b, "\tbuffers started:\t%lu\n", pd->stats.bh_s); + b += sprintf(b, "\tbuffers ended:\t\t%lu\n", pd->stats.bh_e); + b += sprintf(b, "\tsectors written:\t%lu\n", pd->stats.secs_w); + b += sprintf(b, "\tsectors read:\t\t%lu\n", pd->stats.secs_r); + + b += sprintf(b, "\nMisc:\n"); + b += sprintf(b, "\treference count:\t%d\n", atomic_read(&pd->refcnt)); + b += sprintf(b, "\tflags:\t\t\t%lx\n", pd->flags); + b += sprintf(b, "\twrite speed:\t\t%uKB/sec\n", pd->speed * 150); + b += sprintf(b, "\tstart offset:\t\t%lu\n", pd->offset); + b += sprintf(b, "\tmode page offset:\t%u\n", pd->mode_offset); + + b += sprintf(b, "\nQueue state:\n"); + b += sprintf(b, "\tfree buffers:\t\t%u\n", atomic_read(&pd->cdrw.free_bh)); + b += sprintf(b, "\tpending buffers:\t%u\n", atomic_read(&pd->cdrw.pending_bh)); + + b += sprintf(b, "\tpending request:\t%u\n", pd->rq ? 1 : 0); + b += sprintf(b, "\tqueue requests:\t\t%u\n", list_nr_items(pd, &pd->cdrw.r_queue.queue_head, &io_request_lock)); + + return b - buf; +} + +static int pkt_read_proc(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + struct pktcdvd_device *pd; + char *buf = page; + int len, i; + + len = sprintf(buf, "%s\n", VERSION_CODE); + buf += len; + + for (i = 0; i < MAX_WRITERS; i++) { + pd = &pkt_devs[i]; + if (pd->dev) { + len += pkt_proc_device(pd, buf); + buf += len; + } + } + + if (len <= off + count) + *eof = 1; + + *start = page + off; + len -= off; + if (len > count) + len = count; + if (len < 0) + len = 0; + + return len; +} + +int __init pkt_init(void) +{ + devfs_register(NULL, "pktcdvd", 0, DEVFS_FL_DEFAULT, PACKET_MAJOR, + S_IFBLK | S_IRUSR | S_IWUSR, &pktcdvd_ops, NULL); + if (devfs_register_blkdev(PACKET_MAJOR, "pktcdvd", &pktcdvd_ops)) { + printk("unable to register pktcdvd device\n"); + return -EIO; + } + + pkt_sizes = kmalloc(MAX_WRITERS * sizeof(int), GFP_KERNEL); + if (pkt_sizes == NULL) + goto err; + + pkt_blksize = kmalloc(MAX_WRITERS * sizeof(int), GFP_KERNEL); + if (pkt_blksize == NULL) + goto err; + + pkt_readahead = kmalloc(MAX_WRITERS * sizeof(int), GFP_KERNEL); + if (pkt_readahead == NULL) + goto err; + + pkt_devs = kmalloc(MAX_WRITERS * sizeof(struct pktcdvd_device), GFP_KERNEL); + if (pkt_devs == NULL) + goto err; + + memset(pkt_devs, 0, MAX_WRITERS * sizeof(struct pktcdvd_device)); + memset(pkt_sizes, 0, MAX_WRITERS * sizeof(int)); + memset(pkt_blksize, 0, MAX_WRITERS * sizeof(int)); + memset(pkt_readahead, 0, MAX_WRITERS * sizeof(int)); + + blk_size[PACKET_MAJOR] = pkt_sizes; + blksize_size[PACKET_MAJOR] = pkt_blksize; + max_readahead[PACKET_MAJOR] = pkt_readahead; + read_ahead[PACKET_MAJOR] = 128; + set_blocksize(MKDEV(PACKET_MAJOR, 0), CD_FRAMESIZE); + + blk_dev[PACKET_MAJOR].queue = pkt_my_queue; + + create_proc_read_entry("driver/pktcdvd", 0, 0, pkt_read_proc, NULL); + + DPRINTK("pktcdvd: %s\n", VERSION_CODE); + return 0; + +err: + printk("pktcdvd: out of memory\n"); + devfs_unregister(devfs_find_handle(NULL, "pktcdvd", 0, 0, + DEVFS_SPECIAL_BLK, 0)); + devfs_unregister_blkdev(PACKET_MAJOR, "pktcdvd"); + kfree(pkt_devs); + kfree(pkt_sizes); + kfree(pkt_blksize); + kfree(pkt_readahead); + return -ENOMEM; +} + +void __exit pkt_exit(void) +{ + devfs_unregister(devfs_find_handle(NULL, "pktcdvd", 0, 0, + DEVFS_SPECIAL_BLK, 0)); + devfs_unregister_blkdev(PACKET_MAJOR, "pktcdvd"); + + remove_proc_entry("driver/pktcdvd", NULL); + kfree(pkt_sizes); + kfree(pkt_blksize); + kfree(pkt_devs); + kfree(pkt_readahead); +} + +MODULE_DESCRIPTION("Packet writing layer for CD/DVD drives"); +MODULE_AUTHOR("Jens Axboe "); + +module_init(pkt_init); +module_exit(pkt_exit); + diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test9/drivers/block/rd.c linux/drivers/block/rd.c --- /opt/kernel/linux-2.4.0-test9/drivers/block/rd.c Thu Jul 13 06:58:42 2000 +++ linux/drivers/block/rd.c Fri Sep 8 01:04:37 2000 @@ -203,10 +203,12 @@ repeat: INIT_REQUEST; + spin_unlock_irq(&io_request_lock); minor = MINOR(CURRENT->rq_dev); if (minor >= NUM_RAMDISKS) { + spin_lock_irq(&io_request_lock); end_request(0); goto repeat; } @@ -215,12 +217,14 @@ len = CURRENT->current_nr_sectors << 9; if ((offset + len) > rd_length[minor]) { + spin_lock_irq(&io_request_lock); end_request(0); goto repeat; } if ((CURRENT->cmd != READ) && (CURRENT->cmd != WRITE)) { printk(KERN_INFO "RAMDISK: bad command: %d\n", CURRENT->cmd); + spin_lock_irq(&io_request_lock); end_request(0); goto repeat; } @@ -236,6 +240,7 @@ mark_buffer_protected(rbh); brelse(rbh); + spin_lock_irq(&io_request_lock); end_request(1); goto repeat; } diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test9/drivers/i2o/i2o_block.c linux/drivers/i2o/i2o_block.c --- /opt/kernel/linux-2.4.0-test9/drivers/i2o/i2o_block.c Fri Jul 7 04:24:51 2000 +++ linux/drivers/i2o/i2o_block.c Fri Sep 8 01:04:37 2000 @@ -392,7 +392,6 @@ if (req->nr_segments < max_segments) { req->nr_segments++; - q->elevator.nr_segments++; return 1; } return 0; @@ -436,7 +435,6 @@ if (total_segments > max_segments) return 0; - q->elevator.nr_segments -= same_segment; req->nr_segments = total_segments; return 1; } diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test9/drivers/ide/ide-cd.c linux/drivers/ide/ide-cd.c --- /opt/kernel/linux-2.4.0-test9/drivers/ide/ide-cd.c Sun Oct 15 12:50:57 2000 +++ linux/drivers/ide/ide-cd.c Tue Sep 12 14:02:14 2000 @@ -285,9 +285,11 @@ * 4.58 May 1, 2000 - Clean up ACER50 stuff. * - Fix small problem with ide_cdrom_capacity * + * 4.99 - Added write support for packet writing. + * *************************************************************************/ -#define IDECD_VERSION "4.58" +#define IDECD_VERSION "4.99" #include #include @@ -369,6 +371,15 @@ if (sense->sense_key == 0x05 && sense->asc == 0x24) return; + /* + * If a read toc is executed for a CD-R or CD-RW medium where + * the first toc has not been recorded yet, it will fail with + * 05/24/00 + */ + if (failed_command && failed_command->c[0] == GPCMD_READ_TOC_PMA_ATIP) + if (sense->sense_key == 0x05 && sense->asc == 0x24) + return; + #if VERBOSE_IDE_CD_ERRORS { int i; @@ -533,8 +544,10 @@ (struct packet_command *) pc->sense, (struct request_sense *) (pc->buffer - pc->c[4])); } - if (rq->cmd == READ && !rq->current_nr_sectors) - uptodate = 1; + + if (rq->cmd == READ || rq->cmd == WRITE_PACKET) + if (!rq->current_nr_sectors) + uptodate = 1; ide_end_request (uptodate, HWGROUP(drive)); } @@ -542,8 +555,8 @@ /* Returns 0 if the request should be continued. Returns 1 if the request was ended. */ -static int cdrom_decode_status (ide_startstop_t *startstop, ide_drive_t *drive, - int good_stat, int *stat_ret) +static int cdrom_decode_status(ide_startstop_t *startstop, ide_drive_t *drive, + int good_stat, int *stat_ret) { struct request *rq = HWGROUP(drive)->rq; int stat, err, sense_key; @@ -576,13 +589,13 @@ cdrom_end_request (1, drive); *startstop = ide_error (drive, "request sense failure", stat); return 1; - } else if (rq->cmd == PACKET_COMMAND) { /* All other functions, except for READ. */ + struct semaphore *sem = NULL; pc = (struct packet_command *) rq->buffer; - /* Check for tray open. */ + /* Check for tray open. */ if (sense_key == NOT_READY) { cdrom_saw_media_change (drive); } else if (sense_key == UNIT_ATTENTION) { @@ -592,7 +605,8 @@ return 0; } else { /* Otherwise, print an error. */ - ide_dump_status(drive, "packet command error", stat); + ide_dump_status (drive, "packet command error", + stat); } /* Set the error flag and complete the request. @@ -613,9 +627,10 @@ cdrom_end_request (1, drive); if ((stat & ERR_STAT) != 0) - cdrom_queue_request_sense(drive, sem, pc->sense, pc); + cdrom_queue_request_sense(drive, sem, pc->sense, + pc); } else { - /* Handle errors from READ requests. */ + /* Handle errors from READ and WRITE requests. */ if (sense_key == NOT_READY) { /* Tray open. */ @@ -666,11 +681,22 @@ struct packet_command *pc = (struct packet_command *) rq->buffer; unsigned long wait = 0; - /* blank and format can take an extremly long time to - * complete, if the IMMED bit was not set. + /* + * Some commands are *slow* and normally take a long time to + * complete. Usually we can use the ATAPI "disconnect" to bypass + * this, but not all commands/drives support that. Let + * ide_timer_expiry keep polling us for these. */ - if (pc->c[0] == GPCMD_BLANK || pc->c[0] == GPCMD_FORMAT_UNIT) - wait = 60*60*HZ; + switch (pc->c[0]) { + case GPCMD_BLANK: + case GPCMD_FORMAT_UNIT: + case GPCMD_RESERVE_RZONE_TRACK: + wait = WAIT_CMD; + break; + default: + wait = 0; + break; + } return wait; } @@ -684,7 +710,8 @@ static ide_startstop_t cdrom_start_packet_command(ide_drive_t *drive, int xferlen, - ide_handler_t *handler) + ide_handler_t *handler, + int cmd) { ide_startstop_t startstop; struct cdrom_info *info = drive->driver_data; @@ -693,8 +720,15 @@ if (ide_wait_stat(&startstop, drive, 0, BUSY_STAT, WAIT_READY)) return startstop; - if (info->dma) - info->dma = !HWIF(drive)->dmaproc(ide_dma_read, drive); + if (info->dma) { + if (cmd == READ) { + info->dma = !HWIF(drive)->dmaproc(ide_dma_read, drive); + } else if (cmd == WRITE) { + info->dma = !HWIF(drive)->dmaproc(ide_dma_write, drive); + } else { + printk("ide-cd: DMA set, but not allowed\n"); + } + } /* Set up the controller registers. */ OUT_BYTE (info->dma, IDE_FEATURE_REG); @@ -724,9 +758,10 @@ by cdrom_start_packet_command. HANDLER is the interrupt handler to call when the command completes or there's data ready. */ -static ide_startstop_t cdrom_transfer_packet_command (ide_drive_t *drive, +static ide_startstop_t cdrom_transfer_packet_command(ide_drive_t *drive, unsigned char *cmd_buf, int cmd_len, - ide_handler_t *handler) + ide_handler_t *handler, + unsigned timeout) { if (CDROM_CONFIG_FLAGS (drive)->drq_interrupt) { /* Here we should have been called after receiving an interrupt @@ -745,10 +780,10 @@ } /* Arm the interrupt handler. */ - ide_set_handler (drive, handler, WAIT_CMD, cdrom_timer_expiry); + ide_set_handler(drive, handler, timeout, cdrom_timer_expiry); /* Send the command to the device. */ - atapi_output_bytes (drive, cmd_buf, cmd_len); + atapi_output_bytes(drive, cmd_buf, cmd_len); return ide_started; } @@ -840,8 +875,8 @@ drive->name, ireason); } - cdrom_end_request (0, drive); - return -1; + cdrom_end_request(0, drive); + return 1; } /* @@ -1082,7 +1117,7 @@ (65534 / CD_FRAMESIZE) : 65535); /* Set up the command */ - memset (&pc.c, 0, sizeof (pc.c)); + memset(&pc.c, 0, sizeof (pc.c)); pc.c[0] = GPCMD_READ_10; pc.c[7] = (nframes >> 8); pc.c[8] = (nframes & 0xff); @@ -1090,7 +1125,7 @@ /* Send the command to the drive and return. */ return cdrom_transfer_packet_command(drive, pc.c, sizeof(pc.c), - &cdrom_read_intr); + &cdrom_read_intr, WAIT_CMD); } @@ -1133,7 +1168,8 @@ memset (&pc.c, 0, sizeof (pc.c)); pc.c[0] = GPCMD_SEEK; put_unaligned(cpu_to_be32(frame), (unsigned int *) &pc.c[2]); - return cdrom_transfer_packet_command (drive, pc.c, sizeof (pc.c), &cdrom_seek_intr); + return cdrom_transfer_packet_command(drive, pc.c, sizeof(pc.c), + &cdrom_seek_intr, WAIT_CMD); } static ide_startstop_t cdrom_start_seek (ide_drive_t *drive, unsigned int block) @@ -1142,7 +1178,7 @@ info->dma = 0; info->start_seek = jiffies; - return cdrom_start_packet_command (drive, 0, cdrom_start_seek_continuation); + return cdrom_start_packet_command(drive, 0, cdrom_start_seek_continuation, 0); } /* Fix up a possibly partially-processed request so that we can @@ -1194,7 +1230,7 @@ info->dma = 0; /* Start sending the read request to the drive. */ - return cdrom_start_packet_command(drive, 32768, cdrom_start_read_continuation); + return cdrom_start_packet_command(drive, 32768, cdrom_start_read_continuation, READ); } /**************************************************************************** @@ -1309,8 +1345,8 @@ struct packet_command *pc = (struct packet_command *)rq->buffer; /* Send the command to the drive and return. */ - return cdrom_transfer_packet_command (drive, pc->c, - sizeof (pc->c), &cdrom_pc_intr); + return cdrom_transfer_packet_command(drive, pc->c, sizeof(pc->c), + &cdrom_pc_intr, WAIT_CMD); } @@ -1326,7 +1362,7 @@ len = pc->buflen; /* Start sending the command to the drive. */ - return cdrom_start_packet_command (drive, len, cdrom_do_pc_continuation); + return cdrom_start_packet_command(drive, len, cdrom_do_pc_continuation, 0); } @@ -1387,16 +1423,182 @@ return pc->stat ? -EIO : 0; } +/* + * Write handling + */ +static inline int cdrom_write_check_ireason(ide_drive_t *drive, int len, + int ireason) +{ + /* Two notes about IDE interrupt reason here - 0 means that + * the drive wants to receive data from us, 2 means that + * the drive is expecting data from us. + */ + ireason &= 3; + + if (ireason == 2) { + /* Whoops... The drive wants to send data. */ + printk("%s: cdrom_write_intr: wrong transfer direction!\n", + drive->name); + + /* Throw some data at the drive so it doesn't hang + and quit this request. */ + while (len > 0) { + int dum = 0; + atapi_output_bytes(drive, &dum, sizeof(dum)); + len -= sizeof(dum); + } + } else { + /* Drive wants a command packet, or invalid ireason... */ + printk("%s: cdrom_write_intr: bad interrupt reason %d\n", + drive->name, ireason); + } + + cdrom_end_request(0, drive); + return 1; +} + +static ide_startstop_t cdrom_write_intr(ide_drive_t *drive) +{ + int stat, ireason, len, sectors_to_transfer; + struct cdrom_info *info = drive->driver_data; + int i, dma_error = 0, dma = info->dma; + ide_startstop_t startstop; + + struct request *rq = HWGROUP(drive)->rq; + + /* Check for errors. */ + if (dma) { + info->dma = 0; + if ((dma_error = HWIF(drive)->dmaproc(ide_dma_end, drive))) { + printk("ide-cd: write dma error\n"); + HWIF(drive)->dmaproc(ide_dma_off, drive); + } + } + + if (cdrom_decode_status(&startstop, drive, 0, &stat)) { + printk("ide-cd: write_intr decode_status bad\n"); + return startstop; + } + + if (dma) { + if (dma_error) + return ide_error(drive, "dma error", stat); + + rq = HWGROUP(drive)->rq; + for (i = rq->nr_sectors; i > 0;) { + i -= rq->current_nr_sectors; + ide_end_request(1, HWGROUP(drive)); + } + return ide_stopped; + } + + /* Read the interrupt reason and the transfer length. */ + ireason = IN_BYTE(IDE_NSECTOR_REG); + len = IN_BYTE(IDE_LCYL_REG) + 256 * IN_BYTE(IDE_HCYL_REG); + + /* If DRQ is clear, the command has completed. */ + if ((stat & DRQ_STAT) == 0) { + /* If we're not done writing, complain. + * Otherwise, complete the command normally. + */ + if (rq->current_nr_sectors > 0) { + printk("%s: write_intr: data underrun (%ld blocks)\n", + drive->name, rq->current_nr_sectors); + cdrom_end_request(0, drive); + } else + cdrom_end_request(1, drive); + return ide_stopped; + } + + /* Check that the drive is expecting to do the same thing we are. */ + if (ireason & 3) + if (cdrom_write_check_ireason(drive, len, ireason)) + return ide_stopped; + + /* The number of sectors we need to read from the drive. */ + sectors_to_transfer = len / SECTOR_SIZE; + + /* Now loop while we still have data to read from the drive. DMA + * transfers will already have been complete + */ + while (sectors_to_transfer > 0) { + /* If we've filled the present buffer but there's another + chained buffer after it, move on. */ + if (rq->current_nr_sectors == 0 && rq->nr_sectors > 0) + cdrom_end_request(1, drive); + + atapi_output_bytes(drive, rq->buffer, rq->current_nr_sectors); + rq->nr_sectors -= rq->current_nr_sectors; + rq->current_nr_sectors = 0; + rq->sector += rq->current_nr_sectors; + sectors_to_transfer -= rq->current_nr_sectors; + } + + /* arm handler */ + ide_set_handler(drive, &cdrom_write_intr, 5 * WAIT_CMD, NULL); + return ide_started; +} + +static ide_startstop_t cdrom_start_write_cont(ide_drive_t *drive) +{ + struct packet_command pc; + struct request *rq = HWGROUP(drive)->rq; + unsigned nframes, frame; + + nframes = rq->nr_sectors >> 2; + frame = rq->sector >> 2; + +#define IDECD_WRITE_PARANOIA +#ifdef IDECD_WRITE_PARANOIA + if (nframes != 32 || frame % 32) { + printk("ide-cd: got WRONG write! %u %u\n", frame, nframes); + ide_end_request(1, HWGROUP(drive)); + return ide_stopped; + } +#endif + + memset(&pc.c, 0, sizeof(pc.c)); + /* + * we might as well use WRITE_12, but none of the devices I have + * support the streaming feature anyway, so who cares. + */ + pc.c[0] = GPCMD_WRITE_10; +#if 0 + pc.c[1] = 1 << 3; /* FUA bit */ +#endif + pc.c[7] = (nframes >> 8) & 0xff; + pc.c[8] = nframes & 0xff; + put_unaligned(cpu_to_be32(frame), (unsigned int *)&pc.c[2]); + + return cdrom_transfer_packet_command(drive, pc.c, sizeof(pc.c), + cdrom_write_intr, 2 * WAIT_CMD); +} + +static ide_startstop_t cdrom_start_write(ide_drive_t *drive) +{ + struct cdrom_info *info = drive->driver_data; + + info->nsectors_buffered = 0; + + /* use dma, if possible. we don't need to check more, since we + * know that the transfer is always (at least!) 2KB aligned */ + info->dma = drive->using_dma ? 1 : 0; + + /* Start sending the read request to the drive. */ + return cdrom_start_packet_command(drive, 32768, cdrom_start_write_cont, WRITE); +} + /**************************************************************************** * cdrom driver request routine. */ static ide_startstop_t -ide_do_rw_cdrom (ide_drive_t *drive, struct request *rq, unsigned long block) +ide_do_rw_cdrom(ide_drive_t *drive, struct request *rq, unsigned long block) { ide_startstop_t action; struct cdrom_info *info = drive->driver_data; switch (rq->cmd) { + case WRITE_PACKET: case READ: { if (CDROM_CONFIG_FLAGS(drive)->seeking) { unsigned long elpased = jiffies - info->start_seek; @@ -1413,8 +1615,12 @@ } if (IDE_LARGE_SEEK(info->last_block, block, IDECD_SEEK_THRESHOLD) && drive->dsc_overlap) action = cdrom_start_seek (drive, block); - else - action = cdrom_start_read (drive, block); + else { + if (rq->cmd == READ) + action = cdrom_start_read(drive, block); + else + action = cdrom_start_write(drive); + } info->last_block = block; return action; } @@ -1428,9 +1634,8 @@ cdrom_end_request(1, drive); return ide_do_reset(drive); } - default: { - printk("ide-cd: bad cmd %d\n", rq -> cmd); + printk("ide-cd: bad cmd %d\n", rq->cmd); cdrom_end_request(0, drive); return ide_stopped; } @@ -1777,6 +1982,9 @@ HWIF(drive)->gd->sizes[drive->select.b.unit << PARTN_BITS] = (toc->capacity * SECTORS_PER_FRAME) >> (BLOCK_SIZE_BITS - 9); drive->part[0].nr_sects = toc->capacity * SECTORS_PER_FRAME; + HWIF(drive)->gd->sizes[drive->select.b.unit << PARTN_BITS] = (toc->capacity * SECTORS_PER_FRAME) >> (BLOCK_SIZE_BITS - 9); + drive->part[0].nr_sects = toc->capacity * SECTORS_PER_FRAME; + /* Remember that we've read this stuff. */ CDROM_STATE_FLAGS (drive)->toc_valid = 1; @@ -1822,8 +2030,9 @@ pc.c[2] = (speed >> 8) & 0xff; /* Read Drive speed in kbytes/second LSB */ pc.c[3] = speed & 0xff; - if ( CDROM_CONFIG_FLAGS(drive)->cd_r || - CDROM_CONFIG_FLAGS(drive)->cd_rw ) { + if (CDROM_CONFIG_FLAGS(drive)->cd_r || + CDROM_CONFIG_FLAGS(drive)->cd_rw || + CDROM_CONFIG_FLAGS(drive)->dvd_r) { /* Write Drive speed in kbytes/second MSB */ pc.c[4] = (speed >> 8) & 0xff; /* Write Drive speed in kbytes/second LSB */ @@ -1875,10 +2084,6 @@ return 0; } - - - - /* the generic packet interface to cdrom.c */ static int ide_cdrom_packet(struct cdrom_device_info *cdi, struct cdrom_generic_command *cgc) @@ -2371,15 +2576,11 @@ printk(" %dX", CDROM_CONFIG_FLAGS(drive)->max_speed); printk(" %s", CDROM_CONFIG_FLAGS(drive)->dvd ? "DVD-ROM" : "CD-ROM"); - if (CDROM_CONFIG_FLAGS (drive)->dvd_r|CDROM_CONFIG_FLAGS (drive)->dvd_ram) - printk (" DVD%s%s", - (CDROM_CONFIG_FLAGS (drive)->dvd_r)? "-R" : "", - (CDROM_CONFIG_FLAGS (drive)->dvd_ram)? "AM" : ""); - - if (CDROM_CONFIG_FLAGS (drive)->cd_r|CDROM_CONFIG_FLAGS (drive)->cd_rw) - printk (" CD%s%s", - (CDROM_CONFIG_FLAGS (drive)->cd_r)? "-R" : "", - (CDROM_CONFIG_FLAGS (drive)->cd_rw)? "/RW" : ""); + if (CDROM_CONFIG_FLAGS(drive)->dvd_r || CDROM_CONFIG_FLAGS(drive)->dvd_ram) + printk (" DVD-R%s", (CDROM_CONFIG_FLAGS (drive)->dvd_ram)? "AM" : ""); + + if (CDROM_CONFIG_FLAGS(drive)->cd_r ||CDROM_CONFIG_FLAGS(drive)->cd_rw) + printk (" CD-R%s", (CDROM_CONFIG_FLAGS (drive)->cd_rw)? "/RW" : ""); if (CDROM_CONFIG_FLAGS (drive)->is_changer) printk (" changer w/%d slots", nslots); @@ -2402,7 +2603,7 @@ int major = HWIF(drive)->major; int minor = drive->select.b.unit << PARTN_BITS; - ide_add_setting(drive, "breada_readahead", SETTING_RW, BLKRAGET, BLKRASET, TYPE_INT, 0, 255, 1, 2, &read_ahead[major], NULL); + ide_add_setting(drive, "breada_readahead", SETTING_RW, BLKRAGET, BLKRASET, TYPE_INT, 0, 255, 1, 1024, &read_ahead[major], NULL); ide_add_setting(drive, "file_readahead", SETTING_RW, BLKFRAGET, BLKFRASET, TYPE_INTA, 0, INT_MAX, 1, 1024, &max_readahead[major][minor], NULL); ide_add_setting(drive, "max_kb_per_request", SETTING_RW, BLKSECTGET, BLKSECTSET, TYPE_INTA, 1, 255, 1, 2, &max_sectors[major][minor], NULL); ide_add_setting(drive, "dsc_overlap", SETTING_RW, -1, -1, TYPE_BYTE, 0, 1, 1, 1, &drive->dsc_overlap, NULL); @@ -2416,7 +2617,7 @@ int minor = drive->select.b.unit << PARTN_BITS; int nslots; - set_device_ro(MKDEV(HWIF(drive)->major, minor), 1); + set_device_ro(MKDEV(HWIF(drive)->major, minor), 0); set_blocksize(MKDEV(HWIF(drive)->major, minor), CD_FRAMESIZE); drive->special.all = 0; @@ -2623,7 +2824,7 @@ struct cdrom_info *info = drive->driver_data; struct cdrom_device_info *devinfo = &info->devinfo; - if (ide_unregister_subdriver (drive)) + if (ide_unregister_subdriver(drive)) return 1; if (info->buffer != NULL) kfree(info->buffer); @@ -2631,7 +2832,7 @@ kfree(info->toc); if (info->changer_info != NULL) kfree(info->changer_info); - if (devinfo->handle == drive && unregister_cdrom (devinfo)) + if (devinfo->handle == drive && unregister_cdrom(devinfo)) printk ("%s: ide_cdrom_cleanup failed to unregister device from the cdrom driver.\n", drive->name); kfree(info); drive->driver_data = NULL; @@ -2710,7 +2911,7 @@ kfree (info); continue; } - memset (info, 0, sizeof (struct cdrom_info)); + memset(info, 0, sizeof (struct cdrom_info)); drive->driver_data = info; DRIVER(drive)->busy++; if (ide_cdrom_setup (drive)) { diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test9/drivers/ide/ide-cd.h linux/drivers/ide/ide-cd.h --- /opt/kernel/linux-2.4.0-test9/drivers/ide/ide-cd.h Wed Aug 23 20:36:49 2000 +++ linux/drivers/ide/ide-cd.h Sun Sep 24 14:46:00 2000 @@ -628,7 +628,9 @@ "Logical unit not ready - in progress [sic] of becoming ready" }, { 0x020402, "Logical unit not ready - initializing command required" }, { 0x020403, "Logical unit not ready - manual intervention required" }, - { 0x020404, "In process of becoming ready - writing" }, + { 0x020404, "Logical unit not ready - format in progress" }, + { 0x020407, "Logical unit not ready - operation in progress" }, + { 0x020408, "Logical unit not ready - long write in progress" }, { 0x020600, "No reference position found (media may be upside down)" }, { 0x023000, "Incompatible medium installed" }, { 0x023a00, "Medium not present" }, @@ -678,7 +680,6 @@ { 0x04b600, "Media load mechanism failed" }, { 0x051a00, "Parameter list length error" }, { 0x052000, "Invalid command operation code" }, - { 0x052c00, "Command sequence error" }, { 0x052100, "Logical block address out of range" }, { 0x052102, "Invalid address for write" }, { 0x052400, "Invalid field in command packet" }, diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test9/drivers/ide/ide-dma.c linux/drivers/ide/ide-dma.c --- /opt/kernel/linux-2.4.0-test9/drivers/ide/ide-dma.c Fri Jul 28 01:40:57 2000 +++ linux/drivers/ide/ide-dma.c Fri Sep 8 01:04:37 2000 @@ -224,6 +224,9 @@ unsigned char *virt_addr = bh->b_data; unsigned int size = bh->b_size; + if (nents >= PRD_ENTRIES) + return 0; + while ((bh = bh->b_reqnext) != NULL) { if ((virt_addr + size) != (unsigned char *) bh->b_data) break; @@ -257,6 +260,9 @@ HWIF(drive)->sg_nents = i = ide_build_sglist(HWIF(drive), HWGROUP(drive)->rq); + if (!i) + return 0; + sg = HWIF(drive)->sg_table; while (i && sg_dma_len(sg)) { u32 cur_addr; @@ -266,7 +272,7 @@ cur_len = sg_dma_len(sg); while (cur_len) { - if (++count >= PRD_ENTRIES) { + if (count++ >= PRD_ENTRIES) { printk("%s: DMA table too small\n", drive->name); pci_unmap_sg(HWIF(drive)->pci_dev, HWIF(drive)->sg_table, diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test9/drivers/ide/ide-probe.c linux/drivers/ide/ide-probe.c --- /opt/kernel/linux-2.4.0-test9/drivers/ide/ide-probe.c Fri Aug 4 01:29:49 2000 +++ linux/drivers/ide/ide-probe.c Fri Sep 8 01:04:37 2000 @@ -761,9 +761,10 @@ for (unit = 0; unit < minors; ++unit) { *bs++ = BLOCK_SIZE; #ifdef CONFIG_BLK_DEV_PDC4030 - *max_sect++ = ((hwif->chipset == ide_pdc4030) ? 127 : MAX_SECTORS); + *max_sect++ = ((hwif->chipset == ide_pdc4030) ? 127 : 256); #else - *max_sect++ = MAX_SECTORS; + /* IDE can do up to 128K per request. */ + *max_sect++ = 256; #endif *max_ra++ = MAX_READAHEAD; } diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test9/drivers/scsi/scsi_lib.c linux/drivers/scsi/scsi_lib.c --- /opt/kernel/linux-2.4.0-test9/drivers/scsi/scsi_lib.c Sun Oct 15 12:51:20 2000 +++ linux/drivers/scsi/scsi_lib.c Tue Oct 10 20:36:10 2000 @@ -87,7 +87,6 @@ SCpnt->request.cmd = SPECIAL; SCpnt->request.special = (void *) SCpnt; SCpnt->request.q = NULL; - SCpnt->request.free_list = NULL; SCpnt->request.nr_segments = 0; /* @@ -718,6 +717,11 @@ switch (SCpnt->sense_buffer[2]) { case ILLEGAL_REQUEST: + /* don't requeue a failed WRITE_PACKET request */ + if (SCpnt->request.cmd == WRITE_PACKET) { + SCpnt = scsi_end_request(SCpnt, 0, this_count); + return; + } if (SCpnt->device->ten) { SCpnt->device->ten = 0; /* @@ -870,7 +874,7 @@ * if the device itself is blocked, or if the host is fully * occupied. */ - if (SHpnt->in_recovery || q->plugged) + if (SHpnt->in_recovery) return; /* @@ -883,7 +887,7 @@ * released the lock and grabbed it again, so each time * we need to check to see if the queue is plugged or not. */ - if (SHpnt->in_recovery || q->plugged) + if (SHpnt->in_recovery) return; /* @@ -896,10 +900,11 @@ || (SHpnt->host_blocked) || (SHpnt->host_self_blocked)) { /* - * If we are unable to process any commands at all for this - * device, then we consider it to be starved. What this means - * is that there are no outstanding commands for this device - * and hence we need a little help getting it started again + * If we are unable to process any commands at all for + * this device, then we consider it to be starved. + * What this means is that there are no outstanding + * commands for this device and hence we need a + * little help getting it started again * once the host isn't quite so busy. */ if (SDpnt->device_busy == 0) { @@ -1000,8 +1005,8 @@ } /* * If so, we are ready to do something. Bump the count - * while the queue is locked and then break out of the loop. - * Otherwise loop around and try another request. + * while the queue is locked and then break out of the + * loop. Otherwise loop around and try another request. */ if (!SCpnt) { break; @@ -1029,8 +1034,9 @@ memcpy(&SCpnt->request, req, sizeof(struct request)); /* - * We have copied the data out of the request block - it is now in - * a field in SCpnt. Release the request block. + * We have copied the data out of the request block - + * it is now in a field in SCpnt. Release the request + * block. */ blkdev_release_request(req); } @@ -1047,12 +1053,14 @@ /* * This will do a couple of things: * 1) Fill in the actual SCSI command. - * 2) Fill in any other upper-level specific fields (timeout). + * 2) Fill in any other upper-level specific fields + * (timeout). * - * If this returns 0, it means that the request failed (reading - * past end of disk, reading offline device, etc). This won't - * actually talk to the device, but some kinds of consistency - * checking may cause the request to be rejected immediately. + * If this returns 0, it means that the request failed + * (reading past end of disk, reading offline device, + * etc). This won't actually talk to the device, but + * some kinds of consistency checking may cause the + * request to be rejected immediately. */ if (STpnt == NULL) { STpnt = scsi_get_request_dev(req); @@ -1103,8 +1111,8 @@ scsi_dispatch_cmd(SCpnt); /* - * Now we need to grab the lock again. We are about to mess with - * the request queue and try to find another command. + * Now we need to grab the lock again. We are about to mess + * with the request queue and try to find another command. */ spin_lock_irq(&io_request_lock); } diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test9/drivers/scsi/scsi_merge.c linux/drivers/scsi/scsi_merge.c --- /opt/kernel/linux-2.4.0-test9/drivers/scsi/scsi_merge.c Tue Apr 25 03:59:07 2000 +++ linux/drivers/scsi/scsi_merge.c Sat Sep 9 00:21:49 2000 @@ -324,7 +324,6 @@ req->nr_segments >= SHpnt->sg_tablesize) return 0; req->nr_segments++; - q->elevator.nr_segments++; return 1; } @@ -341,11 +340,8 @@ if (req->nr_hw_segments >= SHpnt->sg_tablesize || req->nr_segments >= SHpnt->sg_tablesize) return 0; - if (req->nr_segments >= max_segments) - return 0; req->nr_hw_segments++; req->nr_segments++; - q->elevator.nr_segments++; return 1; } #else @@ -361,7 +357,6 @@ * counter. */ req->nr_segments++; - q->elevator.nr_segments++; return 1; } else { return 0; @@ -417,8 +412,10 @@ SDpnt = (Scsi_Device *) q->queuedata; SHpnt = SDpnt->host; +#ifdef DMA_CHUNK_SIZE if (max_segments > 64) max_segments = 64; +#endif if (use_clustering) { /* @@ -471,8 +468,10 @@ SDpnt = (Scsi_Device *) q->queuedata; SHpnt = SDpnt->host; +#ifdef DMA_CHUNK_SIZE if (max_segments > 64) max_segments = 64; +#endif if (use_clustering) { /* @@ -601,10 +600,10 @@ SDpnt = (Scsi_Device *) q->queuedata; SHpnt = SDpnt->host; +#ifdef DMA_CHUNK_SIZE if (max_segments > 64) max_segments = 64; -#ifdef DMA_CHUNK_SIZE /* If it would not fit into prepared memory space for sg chain, * then don't allow the merge. */ @@ -664,7 +663,6 @@ * This one is OK. Let it go. */ req->nr_segments += next->nr_segments - 1; - q->elevator.nr_segments--; #ifdef DMA_CHUNK_SIZE req->nr_hw_segments += next->nr_hw_segments - 1; #endif @@ -820,11 +818,7 @@ /* * First we need to know how many scatter gather segments are needed. */ - if (!sg_count_valid) { - count = __count_segments(req, use_clustering, dma_host, NULL); - } else { - count = req->nr_segments; - } + count = __count_segments(req, use_clustering, dma_host, NULL); /* * If the dma pool is nearly empty, then queue a minimal request diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test9/drivers/scsi/sr.c linux/drivers/scsi/sr.c --- /opt/kernel/linux-2.4.0-test9/drivers/scsi/sr.c Sun Oct 15 12:51:20 2000 +++ linux/drivers/scsi/sr.c Tue Oct 10 20:36:10 2000 @@ -28,12 +28,16 @@ * Modified by Jens Axboe - support DVD-RAM * transparently and loose the GHOST hack * + * Modified by Jens Axboe - support packet writing + * through generic packet layer. + * * Modified by Arnaldo Carvalho de Melo * check resource allocation in sr_init and some cleanups * */ #include +#include #include #include @@ -83,7 +87,7 @@ finish:sr_finish, attach:sr_attach, detach:sr_detach, - init_command:sr_init_command + init_command:sr_init_command, }; Scsi_CD *scsi_CDs; @@ -190,8 +194,9 @@ } /* - * rw_intr is the interrupt routine for the device driver. It will be notified on the - * end of a SCSI read / write, and will take on of several actions based on success or failure. + * rw_intr is the interrupt routine for the device driver. It will be notified + * on the end of a SCSI read / write, and will take on of several actions + * based on success or failure. */ static void rw_intr(Scsi_Cmnd * SCpnt) @@ -201,13 +206,11 @@ int good_sectors = (result == 0 ? this_count : 0); int block_sectors = 0; -#ifdef DEBUG - printk("sr.c done: %x %x\n", result, SCpnt->request.bh->b_data); -#endif /* - Handle MEDIUM ERRORs or VOLUME OVERFLOWs that indicate partial success. - Since this is a relatively rare error condition, no care is taken to - avoid unnecessary additional work such as memcpy's that could be avoided. + * Handle MEDIUM ERRORs or VOLUME OVERFLOWs that indicate partial + * success. Since this is a relatively rare error condition, no care + * is taken to avoid unnecessary additional work such as memcpy's that + * could be avoided. */ @@ -241,6 +244,7 @@ scsi_CDs[device_nr].capacity - error_sector < 4 * 75) sr_sizes[device_nr] = error_sector >> 1; } + /* * This calls the generic completion function, now that we know * how many actual sectors finished, and how many sectors we need @@ -249,7 +253,6 @@ scsi_io_completion(SCpnt, good_sectors, block_sectors); } - static request_queue_t *sr_find_queue(kdev_t dev) { /* @@ -263,7 +266,8 @@ static int sr_init_command(Scsi_Cmnd * SCpnt) { - int dev, devm, block, this_count; + int dev, devm, this_count; + unsigned long block; devm = MINOR(SCpnt->request.rq_dev); dev = DEVICE_NR(SCpnt->request.rq_dev); @@ -288,8 +292,8 @@ } if (scsi_CDs[dev].device->changed) { /* - * quietly refuse to do anything to a changed disc until the changed - * bit has been reset + * quietly refuse to do anything to a changed disc until + * the changed bit has been reset */ /* printk("SCSI disk has been changed. Prohibiting further I/O.\n"); */ return 0; @@ -310,7 +314,7 @@ if (scsi_CDs[dev].device->sector_size == 1024) { if ((block & 1) || (SCpnt->request.nr_sectors & 1)) { - printk("sr.c:Bad 1K block number requested (%d %ld)", + printk("sr.c:Bad 1K block number requested (%lu %ld)", block, SCpnt->request.nr_sectors); return 0; } else { @@ -320,7 +324,7 @@ } if (scsi_CDs[dev].device->sector_size == 2048) { if ((block & 3) || (SCpnt->request.nr_sectors & 3)) { - printk("sr.c:Bad 2K block number requested (%d %ld)", + printk("sr.c:Bad 2K block number requested (%ld %ld)", block, SCpnt->request.nr_sectors); return 0; } else { @@ -329,6 +333,10 @@ } } switch (SCpnt->request.cmd) { + case WRITE_PACKET: + SCpnt->cmnd[0] = WRITE_10; + SCpnt->sc_data_direction = SCSI_DATA_WRITE; + break; case WRITE: SCpnt->cmnd[0] = WRITE_10; SCpnt->sc_data_direction = SCSI_DATA_WRITE; @@ -472,23 +480,24 @@ int sector_size; Scsi_Request *SRpnt; - buffer = (unsigned char *) scsi_malloc(512); - + if ((buffer = (unsigned char *) scsi_malloc(512)) == NULL) + return; - SRpnt = scsi_allocate_request(scsi_CDs[i].device); + if ((SRpnt = scsi_allocate_request(scsi_CDs[i].device)) == NULL) + return; retries = 3; do { cmd[0] = READ_CAPACITY; cmd[1] = (scsi_CDs[i].device->lun << 5) & 0xe0; memset((void *) &cmd[2], 0, 8); - SRpnt->sr_request.rq_status = RQ_SCSI_BUSY; /* Mark as really busy */ + /* Mark as really busy */ + SRpnt->sr_request.rq_status = RQ_SCSI_BUSY; SRpnt->sr_cmd_len = 0; memset(buffer, 0, 8); /* Do the command and wait.. */ - SRpnt->sr_data_direction = SCSI_DATA_READ; scsi_wait_req(SRpnt, (void *) cmd, (void *) buffer, 8, SR_TIMEOUT, MAX_RETRIES); @@ -504,17 +513,17 @@ if (the_result) { scsi_CDs[i].capacity = 0x1fffff; - sector_size = 2048; /* A guess, just in case */ + /* A guess, just in case */ + sector_size = 2048; scsi_CDs[i].needs_sector_size = 1; } else { -#if 0 if (cdrom_get_last_written(MKDEV(MAJOR_NR, i), - (long *) &scsi_CDs[i].capacity)) -#endif + (long *) &scsi_CDs[i].capacity)) { scsi_CDs[i].capacity = 1 + ((buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]); + } sector_size = (buffer[4] << 24) | (buffer[5] << 16) | (buffer[6] << 8) | buffer[7]; switch (sector_size) { @@ -650,10 +659,8 @@ unsigned char *buffer = cgc->buffer; int buflen; - /* get the device */ - SRpnt = scsi_allocate_request(device); - if (SRpnt == NULL) - return -ENODEV; /* this just doesn't seem right /axboe */ + if ((SRpnt = scsi_allocate_request(device)) == NULL) + return -ENOMEM; /* use buffer for ISA DMA */ buflen = (cgc->buflen + 511) & ~511; @@ -676,10 +683,10 @@ SRpnt->sr_data_direction = cgc->data_direction; scsi_wait_req(SRpnt, (void *) cgc->cmd, (void *) buffer, cgc->buflen, - SR_TIMEOUT, MAX_RETRIES); + (60 * 60 * HZ), MAX_RETRIES); - if ((cgc->stat = SRpnt->sr_result)) - cgc->sense = (struct request_sense *) SRpnt->sr_sense_buffer; + cgc->stat = SRpnt->sr_result; + cgc->sense = (struct request_sense *) SRpnt->sr_sense_buffer; /* release */ SRpnt->sr_request.rq_dev = MKDEV(0, 0); @@ -692,7 +699,6 @@ scsi_free(buffer, buflen); } - return cgc->stat; } @@ -707,7 +713,7 @@ if (!sr_registered) { if (devfs_register_blkdev(MAJOR_NR, "sr", &cdrom_fops)) { - printk("Unable to get major %d for SCSI-CD\n", MAJOR_NR); + printk("Unable to get major %d for SCSI-CD\n",MAJOR_NR); return 1; } sr_registered++; @@ -759,6 +765,7 @@ { int i; char name[6]; + struct cdrom_device_info *cdi; blk_dev[MAJOR_NR].queue = sr_find_queue; blk_size[MAJOR_NR] = sr_sizes; @@ -769,9 +776,11 @@ if (scsi_CDs[i].capacity) continue; scsi_CDs[i].capacity = 0x1fffff; - scsi_CDs[i].device->sector_size = 2048; /* A guess, just in case */ + /* A guess, just in case */ + scsi_CDs[i].device->sector_size = 2048; scsi_CDs[i].needs_sector_size = 1; - scsi_CDs[i].device->changed = 1; /* force recheck CD type */ + /* force recheck CD type */ + scsi_CDs[i].device->changed = 1; #if 0 /* seems better to leave this for later */ get_sectorsize(i); @@ -785,34 +794,35 @@ scsi_CDs[i].readcd_cdda = 0; sr_sizes[i] = scsi_CDs[i].capacity >> (BLOCK_SIZE_BITS - 9); - scsi_CDs[i].cdi.ops = &sr_dops; - scsi_CDs[i].cdi.handle = &scsi_CDs[i]; - scsi_CDs[i].cdi.dev = MKDEV(MAJOR_NR, i); - scsi_CDs[i].cdi.mask = 0; - scsi_CDs[i].cdi.capacity = 1; + cdi = &scsi_CDs[i].cdi; + cdi->ops = &sr_dops; + cdi->handle = &scsi_CDs[i]; + cdi->dev = MKDEV(MAJOR_NR, i); + cdi->mask = 0; + cdi->capacity = 1; get_capabilities(i); sr_vendor_init(i); sprintf(name, "sr%d", i); - strcpy(scsi_CDs[i].cdi.name, name); - scsi_CDs[i].cdi.de = - devfs_register (scsi_CDs[i].device->de, "cd", - DEVFS_FL_DEFAULT, MAJOR_NR, i, - S_IFBLK | S_IRUGO | S_IWUGO, - &cdrom_fops, NULL); - register_cdrom(&scsi_CDs[i].cdi); + strcpy(cdi->name, name); + cdi->de = devfs_register(scsi_CDs[i].device->de, "cd", + DEVFS_FL_DEFAULT, MAJOR_NR, i, + S_IFBLK | S_IRUGO | S_IWUGO, + &cdrom_fops, NULL); + register_cdrom(cdi); } /* If our host adapter is capable of scatter-gather, then we increase * the read-ahead to 16 blocks (32 sectors). If not, we use * a two block (4 sector) read ahead. */ - if (scsi_CDs[0].device && scsi_CDs[0].device->host->sg_tablesize) - read_ahead[MAJOR_NR] = 32; /* 32 sector read-ahead. Always removable. */ - else - read_ahead[MAJOR_NR] = 4; /* 4 sector read-ahead */ - - return; + if (scsi_CDs[0].device && scsi_CDs[0].device->host->sg_tablesize) { + /* 32 sector read-ahead. Always removable. */ + read_ahead[MAJOR_NR] = 32; + } else { + /* 4 sector read-ahead */ + read_ahead[MAJOR_NR] = 4; + } } static void sr_detach(Scsi_Device * SDp) @@ -824,20 +834,23 @@ if (cpnt->device == SDp) { kdev_t devi = MKDEV(MAJOR_NR, i); struct super_block *sb = get_super(devi); + struct cdrom_device_info *cdi; /* - * Since the cdrom is read-only, no need to sync the device. - * We should be kind to our buffer cache, however. + * Since the cdrom is read-only, no need to sync the + * device. We should be kind to our buffer cache, + * however. */ if (sb) invalidate_inodes(sb); invalidate_buffers(devi); /* - * Reset things back to a sane state so that one can re-load a new - * driver (perhaps the same one). + * Reset things back to a sane state so that one can + * re-load a new driver (perhaps the same one). */ - unregister_cdrom(&(cpnt->cdi)); + cdi = &(cpnt->cdi); + unregister_cdrom(cdi); cpnt->device = NULL; cpnt->capacity = 0; SDp->attached--; @@ -846,29 +859,28 @@ sr_sizes[i] = 0; return; } - return; } -static int __init init_sr(void) +static int __init sr_module_init(void) { sr_template.module = THIS_MODULE; return scsi_register_module(MODULE_SCSI_DEV, &sr_template); } -static void __exit exit_sr(void) +static void __exit sr_module_exit(void) { scsi_unregister_module(MODULE_SCSI_DEV, &sr_template); devfs_unregister_blkdev(MAJOR_NR, "sr"); sr_registered--; if (scsi_CDs != NULL) { - kfree(scsi_CDs); + kfree((char *) scsi_CDs); - kfree(sr_sizes); + kfree((char *) sr_sizes); sr_sizes = NULL; - kfree(sr_blocksizes); + kfree((char *) sr_blocksizes); sr_blocksizes = NULL; - kfree(sr_hardsizes); + kfree((char *) sr_hardsizes); sr_hardsizes = NULL; } blksize_size[MAJOR_NR] = NULL; @@ -879,5 +891,5 @@ sr_template.dev_max = 0; } -module_init(init_sr); -module_exit(exit_sr); +module_init(sr_module_init); +module_exit(sr_module_exit); diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test9/drivers/scsi/sr_ioctl.c linux/drivers/scsi/sr_ioctl.c --- /opt/kernel/linux-2.4.0-test9/drivers/scsi/sr_ioctl.c Sun Oct 15 12:50:58 2000 +++ linux/drivers/scsi/sr_ioctl.c Sat Sep 9 00:36:42 2000 @@ -43,7 +43,8 @@ char *bounce_buffer; SDev = scsi_CDs[target].device; - SRpnt = scsi_allocate_request(scsi_CDs[target].device); + if ((SRpnt = scsi_allocate_request(scsi_CDs[target].device)) == NULL) + return -ENOMEM; SRpnt->sr_data_direction = readwrite; /* use ISA DMA buffer if necessary */ diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test9/fs/buffer.c linux/fs/buffer.c --- /opt/kernel/linux-2.4.0-test9/fs/buffer.c Sun Oct 15 12:51:24 2000 +++ linux/fs/buffer.c Tue Oct 10 20:36:12 2000 @@ -159,6 +159,56 @@ atomic_dec(&bh->b_count); } +static inline int __sync_buffers_packet(int wait) +{ + struct buffer_head *bh, *next; + int i, error = 0, pass = 0; + +restart: + if (current->need_resched && pass) + schedule(); + + spin_lock(&lru_list_lock); + if ((bh = lru_list[BUF_PACKET]) == NULL) + goto out; + + for (i = nr_buffers_type[BUF_DIRTY]; i-- > 0 ; bh = next) { + next = bh->b_next_free; + + if (!buffer_dirty(bh)) { + printk("should not trigger\n"); + continue; + } + + /* + * don't wait for the buffer until the 2nd scan, to + * allow better merging (the queue will be unplugged + * for getting the buffer) + */ + if (buffer_locked(bh) && pass) { + atomic_inc(&bh->b_count); + spin_unlock(&lru_list_lock); + wait_on_buffer (bh); + atomic_dec(&bh->b_count); + goto restart; + } + + atomic_inc(&bh->b_count); + spin_unlock(&lru_list_lock); + ll_rw_block(WRITE, 1, &bh); + atomic_dec(&bh->b_count); + goto restart; + } + +out: + spin_unlock(&lru_list_lock); + if (!pass) { + pass = 1; + goto restart; + } + return error; +} + /* Call sync_buffers with wait!=0 to ensure that the call does not * return until all buffer writes have completed. Sync() may return * before the writes have finished; fsync() may not. @@ -174,6 +224,9 @@ int i, retry, pass = 0, err = 0; struct buffer_head * bh, *next; + if (MAJOR(dev) == 97) + return __sync_buffers_packet(wait); + /* One pass for no-wait, three for wait: * 0) write out all dirty, unlocked buffers; * 1) write out all dirty buffers, waiting if locked; @@ -186,6 +239,9 @@ * there to be dirty buffers on any of the other lists. */ repeat: + if (retry && current->need_resched) + schedule(); + spin_lock(&lru_list_lock); bh = lru_list[BUF_DIRTY]; if (!bh) @@ -685,9 +741,9 @@ clear_bit(BH_Uptodate, &bh->b_state); printk(KERN_WARNING "set_blocksize: " - "b_count %d, dev %s, block %lu, from %p\n", + "b_count %d, dev %s, block %lu, from %p, list %d, end_io %p\n", atomic_read(&bh->b_count), bdevname(bh->b_dev), - bh->b_blocknr, __builtin_return_address(0)); + bh->b_blocknr, __builtin_return_address(0), bh->b_list, bh->b_end_io); } write_unlock(&hash_table_lock); if (slept) @@ -727,8 +783,7 @@ static void end_buffer_io_bad(struct buffer_head *bh, int uptodate) { - mark_buffer_uptodate(bh, uptodate); - unlock_buffer(bh); + end_buffer_io_sync(bh, uptodate); BUG(); } @@ -804,12 +859,11 @@ * 14.02.92: changed it to sync dirty buffers a bit: better performance * when the filesystem starts to get full of dirty blocks (I hope). */ -struct buffer_head * getblk(kdev_t dev, int block, int size) +inline struct buffer_head *__getblk(kdev_t dev, int block, int size) { struct buffer_head * bh; int isize; -repeat: spin_lock(&lru_list_lock); write_lock(&hash_table_lock); bh = __get_hash_table(dev, block, size); @@ -818,41 +872,40 @@ isize = BUFSIZE_INDEX(size); spin_lock(&free_list[isize].lock); - bh = free_list[isize].list; - if (bh) { - __remove_from_free_list(bh, isize); - atomic_set(&bh->b_count, 1); + if ((bh = free_list[isize].list) == NULL) { + spin_unlock(&free_list[isize].lock); + goto out; } + + __remove_from_free_list(bh, isize); + atomic_set(&bh->b_count, 1); spin_unlock(&free_list[isize].lock); - /* - * OK, FINALLY we know that this buffer is the only one of - * its kind, we hold a reference (b_count>0), it is unlocked, - * and it is clean. - */ - if (bh) { - init_buffer(bh, end_buffer_io_sync, NULL); - bh->b_dev = dev; - bh->b_blocknr = block; - bh->b_state = 1 << BH_Mapped; + init_buffer(bh, end_buffer_io_sync, NULL); + bh->b_dev = dev; + bh->b_blocknr = block; + bh->b_state = 1 << BH_Mapped; - /* Insert the buffer into the regular lists */ - __insert_into_queues(bh); - out: - write_unlock(&hash_table_lock); - spin_unlock(&lru_list_lock); - touch_buffer(bh); - return bh; - } + /* Insert the buffer into the regular lists */ + __insert_into_queues(bh); - /* - * If we block while refilling the free list, somebody may - * create the buffer first ... search the hashes again. - */ +out: write_unlock(&hash_table_lock); spin_unlock(&lru_list_lock); refill_freelist(size); - goto repeat; + if (bh) + touch_buffer(bh); + return bh; +} + +struct buffer_head * getblk(kdev_t dev, int block, int size) +{ + struct buffer_head *bh; + + while ((bh = __getblk(dev, block, size)) == NULL) + ; + + return bh; } /* -1 -> no need to flush @@ -939,6 +992,8 @@ dispose = BUF_DIRTY; if (buffer_protected(bh)) dispose = BUF_PROTECTED; + if (MAJOR(bh->b_dev) == 97 && dispose == BUF_DIRTY) + dispose = BUF_PACKET; if (dispose != bh->b_list) { __remove_from_lru_list(bh, bh->b_list); bh->b_list = dispose; @@ -962,7 +1017,7 @@ atomic_dec(&buf->b_count); return; } - printk("VFS: brelse: Trying to free free buffer\n"); + printk("VFS: brelse: Trying to free free buffer %lu\n", buf->b_blocknr); } /* @@ -2362,7 +2417,7 @@ int found = 0, locked = 0, dirty = 0, used = 0, lastused = 0; int protected = 0; int nlist; - static char *buf_types[NR_LIST] = { "CLEAN", "LOCKED", "DIRTY", "PROTECTED", }; + static char *buf_types[NR_LIST] = { "CLEAN", "LOCKED", "DIRTY", "PROTECTED", "PACKET", }; #endif printk("Buffer memory: %6dkB\n", @@ -2505,18 +2560,20 @@ NOTENOTENOTENOTE: we _only_ need to browse the DIRTY lru list as all dirty buffers lives _only_ in the DIRTY lru list. As we never browse the LOCKED and CLEAN lru lists they are infact - completly useless. */ + completly useless. + + First flush "regular" buffers, then move to the packet list */ static int flush_dirty_buffers(int check_flushtime) { struct buffer_head * bh, *next; - int flushed = 0, i; + int flushed = 0, i, list = BUF_DIRTY; restart: spin_lock(&lru_list_lock); - bh = lru_list[BUF_DIRTY]; + bh = lru_list[list]; if (!bh) goto out_unlock; - for (i = nr_buffers_type[BUF_DIRTY]; i-- > 0; bh = next) { + for (i = nr_buffers_type[list]; i-- > 0; bh = next) { next = bh->b_next_free; if (!buffer_dirty(bh)) { @@ -2526,6 +2583,9 @@ if (buffer_locked(bh)) continue; + if (list == BUF_PACKET) + goto write; + if (check_flushtime) { /* The dirty lru list is chronologically ordered so if the current bh is not yet timed out, @@ -2539,6 +2599,7 @@ } /* OK, now we are committed to write it out. */ +write: atomic_inc(&bh->b_count); spin_unlock(&lru_list_lock); ll_rw_block(WRITE, 1, &bh); @@ -2550,7 +2611,10 @@ } out_unlock: spin_unlock(&lru_list_lock); - + if (list == BUF_DIRTY) { + list = BUF_PACKET; + goto restart; + } return flushed; } diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test9/include/linux/blk.h linux/include/linux/blk.h --- /opt/kernel/linux-2.4.0-test9/include/linux/blk.h Sun Oct 15 12:51:27 2000 +++ linux/include/linux/blk.h Tue Oct 10 20:38:45 2000 @@ -87,10 +87,6 @@ static inline void blkdev_dequeue_request(struct request * req) { - if (req->e) { - req->e->dequeue_fn(req); - req->e = NULL; - } list_del(&req->queue); } diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test9/include/linux/blkdev.h linux/include/linux/blkdev.h --- /opt/kernel/linux-2.4.0-test9/include/linux/blkdev.h Wed Aug 23 20:35:41 2000 +++ linux/include/linux/blkdev.h Tue Oct 10 20:38:44 2000 @@ -23,7 +23,7 @@ int elevator_sequence; struct list_head table; - struct list_head *free_list; + int free_list; volatile int rq_status; /* should split this into a few status bits */ #define RQ_INACTIVE (-1) @@ -47,7 +47,6 @@ struct buffer_head * bh; struct buffer_head * bhtail; request_queue_t *q; - elevator_t *e; }; #include @@ -78,6 +77,9 @@ */ struct list_head request_freelist[2]; + struct list_head pending_freelist[2]; + int pending_free[2]; + /* * Together with queue_head for cacheline sharing */ @@ -87,6 +89,7 @@ request_fn_proc * request_fn; merge_request_fn * back_merge_fn; merge_request_fn * front_merge_fn; + merge_request_fn * hole_merge_fn; merge_requests_fn * merge_requests_fn; make_request_fn * make_request_fn; plug_device_fn * plug_device_fn; @@ -121,7 +124,9 @@ /* * Tasks wait here for free request */ - wait_queue_head_t wait_for_request; + wait_queue_head_t wait_for_request[2]; + + int total_requests; }; struct blk_dev_struct { @@ -152,6 +157,7 @@ extern void register_disk(struct gendisk *dev, kdev_t first, unsigned minors, struct block_device_operations *ops, long size); extern void generic_make_request(int rw, struct buffer_head * bh); extern request_queue_t *blk_get_queue(kdev_t dev); +extern inline request_queue_t *__blk_get_queue(kdev_t dev); extern void blkdev_release_request(struct request *); /* @@ -162,6 +168,7 @@ extern void blk_queue_headactive(request_queue_t *, int); extern void blk_queue_pluggable(request_queue_t *, plug_device_fn *); extern void blk_queue_make_request(request_queue_t *, make_request_fn *); +extern void generic_unplug_device(void *); extern int * blk_size[MAX_BLKDEV]; @@ -175,9 +182,8 @@ extern int * max_segments[MAX_BLKDEV]; -#define MAX_SECTORS 254 - -#define MAX_SEGMENTS MAX_SECTORS +#define MAX_SEGMENTS 128 +#define MAX_SECTORS (MAX_SEGMENTS*8) #define PageAlignSize(size) (((size) + PAGE_SIZE -1) & PAGE_MASK) @@ -191,8 +197,7 @@ #define blkdev_next_request(req) blkdev_entry_to_request((req)->queue.next) #define blkdev_prev_request(req) blkdev_entry_to_request((req)->queue.prev) -extern void drive_stat_acct (kdev_t dev, int rw, - unsigned long nr_sectors, int new_io); +extern void drive_stat_acct (kdev_t dev, int rw, unsigned long nr_sectors, int new_io); static inline int get_hardsect_size(kdev_t dev) { diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test9/include/linux/cdrom.h linux/include/linux/cdrom.h --- /opt/kernel/linux-2.4.0-test9/include/linux/cdrom.h Wed Aug 23 20:35:41 2000 +++ linux/include/linux/cdrom.h Tue Oct 10 20:38:53 2000 @@ -487,6 +487,7 @@ /* Mode page codes for mode sense/set */ #define GPMODE_R_W_ERROR_PAGE 0x01 #define GPMODE_WRITE_PARMS_PAGE 0x05 +#define GPMODE_WCACHING_PAGE 0x08 #define GPMODE_AUDIO_CTL_PAGE 0x0e #define GPMODE_POWER_PAGE 0x1a #define GPMODE_FAULT_FAIL_PAGE 0x1c @@ -497,7 +498,10 @@ * of MODE_SENSE_POWER_PAGE */ #define GPMODE_CDROM_PAGE 0x0d - +#define GPMODE_PAGE_CURRENT 0 +#define GPMODE_PAGE_CHANGE 1 +#define GPMODE_PAGE_DEFAULT 2 +#define GPMODE_PAGE_SAVE 3 /* DVD struct types */ #define DVD_STRUCT_PHYSICAL 0x00 diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test9/include/linux/elevator.h linux/include/linux/elevator.h --- /opt/kernel/linux-2.4.0-test9/include/linux/elevator.h Wed Jul 19 06:43:10 2000 +++ linux/include/linux/elevator.h Sun Sep 10 14:37:31 2000 @@ -7,34 +7,36 @@ struct list_head *, struct list_head *, int); -typedef int (elevator_merge_fn) (request_queue_t *, struct request **, - struct buffer_head *, int, int *, int *); +typedef int (elevator_merge_fn) (request_queue_t *, struct request **, struct list_head *, + struct buffer_head *, int, int, int); -typedef void (elevator_dequeue_fn) (struct request *); +typedef void (elevator_merge_cleanup_fn) (request_queue_t *, struct request *, int, int); + +typedef void (elevator_merge_req_fn) (struct request *, struct request *); struct elevator_s { - int sequence; - int read_latency; int write_latency; - int max_bomb_segments; - - unsigned int nr_segments; - int read_pendings; + int request_batching; elevator_fn * elevator_fn; elevator_merge_fn *elevator_merge_fn; - elevator_dequeue_fn *dequeue_fn; + elevator_merge_cleanup_fn *elevator_merge_cleanup_fn; + elevator_merge_req_fn *elevator_merge_req_fn; unsigned int queue_ID; }; void elevator_noop(struct request *, elevator_t *, struct list_head *, struct list_head *, int); -int elevator_noop_merge(request_queue_t *, struct request **, struct buffer_head *, int, int *, int *); -void elevator_noop_dequeue(struct request *); +int elevator_noop_merge(request_queue_t *, struct request **, struct list_head *, struct buffer_head *, int, int, int); +void elevator_noop_merge_cleanup(request_queue_t *, struct request *, int, int); +void elevator_noop_merge_req(struct request *, struct request *); + void elevator_linus(struct request *, elevator_t *, struct list_head *, struct list_head *, int); -int elevator_linus_merge(request_queue_t *, struct request **, struct buffer_head *, int, int *, int *); +int elevator_linus_merge(request_queue_t *, struct request **, struct list_head *, struct buffer_head *, int, int, int); +void elevator_linus_merge_cleanup(request_queue_t *, struct request *, int, int); +void elevator_linus_merge_req(struct request *, struct request *); typedef struct blkelv_ioctl_arg_s { int queue_ID; @@ -57,6 +59,7 @@ #define ELEVATOR_NO_MERGE 0 #define ELEVATOR_FRONT_MERGE 1 #define ELEVATOR_BACK_MERGE 2 +#define ELEVATOR_HOLE_MERGE 3 /* * This is used in the elevator algorithm. We don't prioritise reads @@ -80,36 +83,28 @@ return latency; } -#define ELEVATOR_NOOP \ -((elevator_t) { \ - 0, /* sequence */ \ - \ - 0, /* read_latency */ \ - 0, /* write_latency */ \ - 0, /* max_bomb_segments */ \ - \ - 0, /* nr_segments */ \ - 0, /* read_pendings */ \ - \ - elevator_noop, /* elevator_fn */ \ - elevator_noop_merge, /* elevator_merge_fn */ \ - elevator_noop_dequeue, /* dequeue_fn */ \ +#define ELEVATOR_NOOP \ +((elevator_t) { \ + 0, /* read_latency */ \ + 0, /* write_latency */ \ + 0, \ + \ + elevator_noop, /* elevator_fn */ \ + elevator_noop_merge, /* elevator_merge_fn */ \ + elevator_noop_merge_cleanup, /* elevator_merge_cleanup_fn */ \ + elevator_noop_merge_req, /* elevator_merge_req_fn */ \ }) -#define ELEVATOR_LINUS \ -((elevator_t) { \ - 0, /* not used */ \ - \ - 1000000, /* read passovers */ \ - 2000000, /* write passovers */ \ - 0, /* max_bomb_segments */ \ - \ - 0, /* not used */ \ - 0, /* not used */ \ - \ - elevator_linus, /* elevator_fn */ \ - elevator_linus_merge, /* elevator_merge_fn */ \ - elevator_noop_dequeue, /* dequeue_fn */ \ +#define ELEVATOR_LINUS \ +((elevator_t) { \ + 50000, /* read passovers */ \ + 100000, /* write passovers */ \ + 0, \ + \ + elevator_linus, /* elevator_fn */ \ + elevator_linus_merge, /* elevator_merge_fn */ \ + elevator_linus_merge_cleanup, /* elevator_merge_cleanup_fn */ \ + elevator_linus_merge_req, /* elevator_merge_req_fn */ \ }) #endif diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test9/include/linux/fs.h linux/include/linux/fs.h --- /opt/kernel/linux-2.4.0-test9/include/linux/fs.h Sun Oct 15 12:51:27 2000 +++ linux/include/linux/fs.h Tue Oct 10 20:38:44 2000 @@ -71,6 +71,7 @@ #define WRITE 1 #define READA 2 /* read-ahead - don't block if no resources */ #define SPECIAL 4 /* For non-blockdevice requests in request queue */ +#define WRITE_PACKET 5 /* for packet writers */ #define SEL_IN 1 #define SEL_OUT 2 @@ -205,6 +206,7 @@ #define BH_Mapped 4 /* 1 if the buffer has a disk mapping */ #define BH_New 5 /* 1 if the buffer is new and not yet written out */ #define BH_Protected 6 /* 1 if the buffer is protected */ +#define BH_Packet 7 /* 1 if packet writing buffer */ /* * Try to keep the most commonly used fields in single cache lines (16 @@ -800,6 +802,7 @@ int (*remount_fs) (struct super_block *, int *, char *); void (*clear_inode) (struct inode *); void (*umount_begin) (struct super_block *); + int (*block_empty) (struct super_block *, int, unsigned long *); }; struct dquot_operations { @@ -968,7 +971,8 @@ #define BUF_LOCKED 1 /* Buffers scheduled for write */ #define BUF_DIRTY 2 /* Dirty buffers, not yet scheduled for write */ #define BUF_PROTECTED 3 /* Ramdisk persistent storage */ -#define NR_LIST 4 +#define BUF_PACKET 4 /* packet writing buffers */ +#define NR_LIST 5 /* * This is called by bh->b_end_io() handlers when I/O has completed. @@ -1159,6 +1163,7 @@ extern void file_moveto(struct file *new, struct file *old); extern struct buffer_head * get_hash_table(kdev_t, int, int); extern struct buffer_head * getblk(kdev_t, int, int); +extern inline struct buffer_head *__getblk(kdev_t, int, int); extern void ll_rw_block(int, int, struct buffer_head * bh[]); extern int is_read_only(kdev_t); extern void __brelse(struct buffer_head *); diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test9/include/linux/pktcdvd.h linux/include/linux/pktcdvd.h --- /opt/kernel/linux-2.4.0-test9/include/linux/pktcdvd.h Thu Jan 1 01:00:00 1970 +++ linux/include/linux/pktcdvd.h Tue Sep 26 18:46:25 2000 @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2000 Jens Axboe + * + * May be copied or modified under the terms of the GNU General Public + * License. See linux/COPYING for more information. + * + * Packet writing layer for ATAPI and SCSI CD-R, CD-RW, DVD-R, and + * DVD-RW devices. + * + */ + +#ifndef PKTCDVD_H +#define PKTCDVD_H + +/* + * 1 for normal debug messages, 2 is very verbose. 0 to turn it off. + */ +#define PACKET_DEBUG 1 + +#define MAX_WRITERS 8 + +/* + * use drive write caching -- we need deferred error handling to be + * able to sucessfully recover with this option (drive will return good + * status as soon as the cdb is validated). + */ +#define USE_WCACHING 0 + +/* + * No user-servicable parts beyond this point -> + */ + +#if PACKET_DEBUG +#define DPRINTK(fmt, args...) printk(KERN_NOTICE fmt, ##args) +#else +#define DPRINTK(fmt, args...) +#endif + +#if PACKET_DEBUG > 1 +#define VPRINTK(fmt, args...) printk(KERN_NOTICE fmt, ##args) +#else +#define VPRINTK(fmt, args...) +#endif + +#define PACKET_MAJOR 97 + +#define PKT_BUF_LIST 0x89 + +/* + * device types + */ +#define PACKET_CDR 1 +#define PACKET_CDRW 2 +#define PACKET_DVDR 3 +#define PACKET_DVDRW 4 + +/* + * flags + */ +#define PACKET_WRITEABLE 1 /* pd is writeable */ +#define PACKET_NWA_VALID 2 /* next writeable address valid */ +#define PACKET_LRA_VALID 3 /* last recorded address valid */ +#define PACKET_READY 4 +#define PACKET_READONLY 5 /* read only pd */ +#define PACKET_THREAD 6 /* kernel thread running */ + +/* + * Disc status -- from READ_DISC_INFO + */ +#define PACKET_DISC_EMPTY 0 +#define PACKET_DISC_INCOMPLETE 1 +#define PACKET_DISC_COMPLETE 2 +#define PACKET_DISC_OTHER 3 + +/* + * Last session/border status + */ +#define PACKET_SESSION_EMPTY 0 +#define PACKET_SESSION_INCOMPLETE 1 +#define PACKET_SESSION_RESERVED 2 +#define PACKET_SESSION_COMPLETE 3 + +#define PACKET_MCN "4a656e734178626f65323030300000" + +#undef PACKET_USE_LS + +/* + * Very crude stats for now + */ +struct packet_stats { + unsigned long bh_s; + unsigned long bh_e; + unsigned long secs_w; + unsigned long secs_r; +}; + +/* + * packet ioctls + */ +#define PACKET_IOCTL_MAGIC ('X') +#define PACKET_GET_STATS _IOR(PACKET_IOCTL_MAGIC, 0, struct packet_stats) +#define PACKET_SETUP_DEV _IOW(PACKET_IOCTL_MAGIC, 1, unsigned int) +#define PACKET_TEARDOWN_DEV _IOW(PACKET_IOCTL_MAGIC, 2, unsigned int) +#define PACKET_WAKEUP _IO(PACKET_IOCTL_MAGIC, 3) + +#ifdef __KERNEL__ +#include + +struct packet_settings { + __u8 size; /* packet size in frames */ + __u8 fp; /* fixed packets */ + __u8 link_loss; /* the rest is specified + * as per Mt Fuji */ + __u8 write_type; + __u8 track_mode; + __u8 block_mode; +}; + +struct packet_cdrw { + struct buffer_head *bhlist; /* string of bhs */ + atomic_t free_bh; + atomic_t pending_bh; + merge_request_fn *front_merge_fn; + merge_request_fn *back_merge_fn; + merge_request_fn *hole_merge_fn; + merge_requests_fn *merge_requests_fn; + request_queue_t r_queue; + void *queuedata; + pid_t pid; +}; + +struct pktcdvd_device { + kdev_t dev; /* dev attached */ + kdev_t pkt_dev; /* our dev */ + char name[20]; + struct cdrom_device_info *cdi; /* cdrom matching dev */ + struct packet_settings settings; + struct packet_stats stats; + atomic_t refcnt; + __u8 speed; /* cur write speed */ + unsigned long offset; /* start offset */ + __u8 mode_offset; /* 0 / 8 */ + __u8 type; + unsigned long flags; + __u8 disc_status; + __u8 track_status; /* last one */ + __u32 nwa; /* next writable address */ + __u32 lra; /* last recorded address */ + spinlock_t lock; + struct packet_cdrw cdrw; + struct dentry *pkt_dentry; + wait_queue_head_t wqueue; + struct request *rq; /* current request */ +}; + +/* + * following possibly belongs in cdrom.h + */ + +struct cdvd_capacity { + __u32 lba; + __u32 block_length; +}; + +void pkt_elevator_merge_req(struct request *rq, struct request *nxt) {} +void pkt_elevator_cleanup(request_queue_t *q, struct request *rq, int ret, int front) {} + +#define ELEVATOR_PKTCDVD \ +((elevator_t) { \ + 0, /* read_latency */ \ + 0, /* write_latency */ \ + 0, \ + \ + pkt_elevator, /* elevator_fn */ \ + pkt_elevator_merge, /* elevator_merge_fn */ \ + pkt_elevator_cleanup, \ + pkt_elevator_merge_req, \ + }) + +#endif /* __KERNEL__ */ + +#endif /* PKTCDVD_H */ diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test9/include/linux/sched.h linux/include/linux/sched.h --- /opt/kernel/linux-2.4.0-test9/include/linux/sched.h Sun Oct 15 12:51:27 2000 +++ linux/include/linux/sched.h Tue Oct 10 20:38:44 2000 @@ -532,8 +532,8 @@ #define CURRENT_TIME (xtime.tv_sec) -extern void FASTCALL(__wake_up(wait_queue_head_t *q, unsigned int mode)); -extern void FASTCALL(__wake_up_sync(wait_queue_head_t *q, unsigned int mode)); +extern void FASTCALL(__wake_up(wait_queue_head_t *q, unsigned int mode, int nr)); +extern void FASTCALL(__wake_up_sync(wait_queue_head_t *q, unsigned int mode, int nr)); extern void FASTCALL(sleep_on(wait_queue_head_t *q)); extern long FASTCALL(sleep_on_timeout(wait_queue_head_t *q, signed long timeout)); @@ -542,12 +542,17 @@ signed long timeout)); extern void FASTCALL(wake_up_process(struct task_struct * tsk)); -#define wake_up(x) __wake_up((x),TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE | TASK_EXCLUSIVE) -#define wake_up_all(x) __wake_up((x),TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE) -#define wake_up_sync(x) __wake_up_sync((x),TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE | TASK_EXCLUSIVE) -#define wake_up_interruptible(x) __wake_up((x),TASK_INTERRUPTIBLE | TASK_EXCLUSIVE) -#define wake_up_interruptible_all(x) __wake_up((x),TASK_INTERRUPTIBLE) -#define wake_up_interruptible_sync(x) __wake_up_sync((x),TASK_INTERRUPTIBLE | TASK_EXCLUSIVE) +#define wake_up(x) __wake_up((x),TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE | TASK_EXCLUSIVE, 1) +#define wake_up_nr(x, nr) __wake_up((x),TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE | TASK_EXCLUSIVE, nr) +#define wake_up_all(x) __wake_up((x),TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE, 0) +#define wake_up_sync(x) __wake_up_sync((x),TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE | TASK_EXCLUSIVE, 1) +#define wake_up_sync_nr(x, nr) __wake_up_sync((x),TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE | TASK_EXCLUSIVE, nr) +#define wake_up_interruptible(x) __wake_up((x),TASK_INTERRUPTIBLE | TASK_EXCLUSIVE, 1) +#define wake_up_interruptible_nr(x, nr) __wake_up((x),TASK_INTERRUPTIBLE | TASK_EXCLUSIVE, nr) +#define wake_up_interruptible_all(x) __wake_up((x),TASK_INTERRUPTIBLE, 0) +#define wake_up_interruptible_sync(x) __wake_up_sync((x),TASK_INTERRUPTIBLE | TASK_EXCLUSIVE, 1) +#define wake_up_interruptible_sync_nr(x) \ + __wake_up_sync((x),TASK_INTERRUPTIBLE | TASK_EXCLUSIVE, nr) extern int in_group_p(gid_t); extern int in_egroup_p(gid_t); diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test9/kernel/sched.c linux/kernel/sched.c --- /opt/kernel/linux-2.4.0-test9/kernel/sched.c Sun Oct 15 12:51:28 2000 +++ linux/kernel/sched.c Tue Oct 10 20:36:14 2000 @@ -697,11 +697,11 @@ return; } -static inline void __wake_up_common (wait_queue_head_t *q, unsigned int mode, +static inline void __wake_up_common (wait_queue_head_t *q, unsigned int mode, int nr_exclusive, const int sync) { struct list_head *tmp, *head; - struct task_struct *p, *best_exclusive; + struct task_struct *p, *first_exclusive; unsigned long flags; int best_cpu, irq; @@ -710,7 +710,9 @@ best_cpu = smp_processor_id(); irq = in_interrupt(); - best_exclusive = NULL; + if (nr_exclusive != 1) + irq = 0; + first_exclusive = NULL; wq_write_lock_irqsave(&q->lock, flags); #if WAITQUEUE_DEBUG @@ -744,43 +746,42 @@ * CPU. */ if (irq && (state & mode & TASK_EXCLUSIVE)) { - if (!best_exclusive) - best_exclusive = p; - else if ((p->processor == best_cpu) && - (best_exclusive->processor != best_cpu)) - best_exclusive = p; - } else { - if (sync) - wake_up_process_synchronous(p); - else - wake_up_process(p); - if (state & mode & TASK_EXCLUSIVE) - break; + if (p->processor == best_cpu) + first_exclusive = NULL; + else { + if (!first_exclusive) + first_exclusive = p; + continue; + } } + if (sync) + wake_up_process_synchronous(p); + else + wake_up_process(p); + if ((state & mode & TASK_EXCLUSIVE) && !--nr_exclusive) + break; } } - if (best_exclusive) - best_exclusive->state = TASK_RUNNING; wq_write_unlock_irqrestore(&q->lock, flags); - if (best_exclusive) { + if (first_exclusive) { if (sync) - wake_up_process_synchronous(best_exclusive); + wake_up_process_synchronous(first_exclusive); else - wake_up_process(best_exclusive); + wake_up_process(first_exclusive); } out: return; } -void __wake_up(wait_queue_head_t *q, unsigned int mode) +void __wake_up(wait_queue_head_t *q, unsigned int mode, int nr) { - __wake_up_common(q, mode, 0); + __wake_up_common(q, mode, nr, 0); } -void __wake_up_sync(wait_queue_head_t *q, unsigned int mode) +void __wake_up_sync(wait_queue_head_t *q, unsigned int mode, int nr) { - __wake_up_common(q, mode, 1); + __wake_up_common(q, mode, nr, 1); } #define SLEEP_ON_VAR \ diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test9/mm/filemap.c linux/mm/filemap.c --- /opt/kernel/linux-2.4.0-test9/mm/filemap.c Sun Oct 15 12:51:28 2000 +++ linux/mm/filemap.c Tue Oct 10 20:36:14 2000 @@ -867,10 +867,6 @@ * accessed sequentially. */ if (ahead) { - if (reada_ok == 2) { - run_task_queue(&tq_disk); - } - filp->f_ralen += ahead; filp->f_rawin += filp->f_ralen; filp->f_raend = raend + ahead + 1;