pktcdvd.c

来自「linux 内核源代码」· C语言 代码 · 共 2,596 行 · 第 1/5 页

C
2,596
字号
 * a) The number of required segments for the write bio is minimized, which *    is necessary for some scsi controllers. * b) The data can be used as cache to avoid read requests if we receive a *    new write request for the same zone. */static void pkt_make_local_copy(struct packet_data *pkt, struct bio_vec *bvec){	int f, p, offs;	/* Copy all data to pkt->pages[] */	p = 0;	offs = 0;	for (f = 0; f < pkt->frames; f++) {		if (bvec[f].bv_page != pkt->pages[p]) {			void *vfrom = kmap_atomic(bvec[f].bv_page, KM_USER0) + bvec[f].bv_offset;			void *vto = page_address(pkt->pages[p]) + offs;			memcpy(vto, vfrom, CD_FRAMESIZE);			kunmap_atomic(vfrom, KM_USER0);			bvec[f].bv_page = pkt->pages[p];			bvec[f].bv_offset = offs;		} else {			BUG_ON(bvec[f].bv_offset != offs);		}		offs += CD_FRAMESIZE;		if (offs >= PAGE_SIZE) {			offs = 0;			p++;		}	}}static void pkt_end_io_read(struct bio *bio, int err){	struct packet_data *pkt = bio->bi_private;	struct pktcdvd_device *pd = pkt->pd;	BUG_ON(!pd);	VPRINTK("pkt_end_io_read: bio=%p sec0=%llx sec=%llx err=%d\n", bio,		(unsigned long long)pkt->sector, (unsigned long long)bio->bi_sector, err);	if (err)		atomic_inc(&pkt->io_errors);	if (atomic_dec_and_test(&pkt->io_wait)) {		atomic_inc(&pkt->run_sm);		wake_up(&pd->wqueue);	}	pkt_bio_finished(pd);}static void pkt_end_io_packet_write(struct bio *bio, int err){	struct packet_data *pkt = bio->bi_private;	struct pktcdvd_device *pd = pkt->pd;	BUG_ON(!pd);	VPRINTK("pkt_end_io_packet_write: id=%d, err=%d\n", pkt->id, err);	pd->stats.pkt_ended++;	pkt_bio_finished(pd);	atomic_dec(&pkt->io_wait);	atomic_inc(&pkt->run_sm);	wake_up(&pd->wqueue);}/* * Schedule reads for the holes in a packet */static void pkt_gather_data(struct pktcdvd_device *pd, struct packet_data *pkt){	int frames_read = 0;	struct bio *bio;	int f;	char written[PACKET_MAX_SIZE];	BUG_ON(!pkt->orig_bios);	atomic_set(&pkt->io_wait, 0);	atomic_set(&pkt->io_errors, 0);	/*	 * Figure out which frames we need to read before we can write.	 */	memset(written, 0, sizeof(written));	spin_lock(&pkt->lock);	for (bio = pkt->orig_bios; bio; bio = bio->bi_next) {		int first_frame = (bio->bi_sector - pkt->sector) / (CD_FRAMESIZE >> 9);		int num_frames = bio->bi_size / CD_FRAMESIZE;		pd->stats.secs_w += num_frames * (CD_FRAMESIZE >> 9);		BUG_ON(first_frame < 0);		BUG_ON(first_frame + num_frames > pkt->frames);		for (f = first_frame; f < first_frame + num_frames; f++)			written[f] = 1;	}	spin_unlock(&pkt->lock);	if (pkt->cache_valid) {		VPRINTK("pkt_gather_data: zone %llx cached\n",			(unsigned long long)pkt->sector);		goto out_account;	}	/*	 * Schedule reads for missing parts of the packet.	 */	for (f = 0; f < pkt->frames; f++) {		struct bio_vec *vec;		int p, offset;		if (written[f])			continue;		bio = pkt->r_bios[f];		vec = bio->bi_io_vec;		bio_init(bio);		bio->bi_max_vecs = 1;		bio->bi_sector = pkt->sector + f * (CD_FRAMESIZE >> 9);		bio->bi_bdev = pd->bdev;		bio->bi_end_io = pkt_end_io_read;		bio->bi_private = pkt;		bio->bi_io_vec = vec;		bio->bi_destructor = pkt_bio_destructor;		p = (f * CD_FRAMESIZE) / PAGE_SIZE;		offset = (f * CD_FRAMESIZE) % PAGE_SIZE;		VPRINTK("pkt_gather_data: Adding frame %d, page:%p offs:%d\n",			f, pkt->pages[p], offset);		if (!bio_add_page(bio, pkt->pages[p], CD_FRAMESIZE, offset))			BUG();		atomic_inc(&pkt->io_wait);		bio->bi_rw = READ;		pkt_queue_bio(pd, bio);		frames_read++;	}out_account:	VPRINTK("pkt_gather_data: need %d frames for zone %llx\n",		frames_read, (unsigned long long)pkt->sector);	pd->stats.pkt_started++;	pd->stats.secs_rg += frames_read * (CD_FRAMESIZE >> 9);}/* * Find a packet matching zone, or the least recently used packet if * there is no match. */static struct packet_data *pkt_get_packet_data(struct pktcdvd_device *pd, int zone){	struct packet_data *pkt;	list_for_each_entry(pkt, &pd->cdrw.pkt_free_list, list) {		if (pkt->sector == zone || pkt->list.next == &pd->cdrw.pkt_free_list) {			list_del_init(&pkt->list);			if (pkt->sector != zone)				pkt->cache_valid = 0;			return pkt;		}	}	BUG();	return NULL;}static void pkt_put_packet_data(struct pktcdvd_device *pd, struct packet_data *pkt){	if (pkt->cache_valid) {		list_add(&pkt->list, &pd->cdrw.pkt_free_list);	} else {		list_add_tail(&pkt->list, &pd->cdrw.pkt_free_list);	}}/* * recover a failed write, query for relocation if possible * * returns 1 if recovery is possible, or 0 if not * */static int pkt_start_recovery(struct packet_data *pkt){	/*	 * FIXME. We need help from the file system to implement	 * recovery handling.	 */	return 0;#if 0	struct request *rq = pkt->rq;	struct pktcdvd_device *pd = rq->rq_disk->private_data;	struct block_device *pkt_bdev;	struct super_block *sb = NULL;	unsigned long old_block, new_block;	sector_t new_sector;	pkt_bdev = bdget(kdev_t_to_nr(pd->pkt_dev));	if (pkt_bdev) {		sb = get_super(pkt_bdev);		bdput(pkt_bdev);	}	if (!sb)		return 0;	if (!sb->s_op || !sb->s_op->relocate_blocks)		goto out;	old_block = pkt->sector / (CD_FRAMESIZE >> 9);	if (sb->s_op->relocate_blocks(sb, old_block, &new_block))		goto out;	new_sector = new_block * (CD_FRAMESIZE >> 9);	pkt->sector = new_sector;	pkt->bio->bi_sector = new_sector;	pkt->bio->bi_next = NULL;	pkt->bio->bi_flags = 1 << BIO_UPTODATE;	pkt->bio->bi_idx = 0;	BUG_ON(pkt->bio->bi_rw != (1 << BIO_RW));	BUG_ON(pkt->bio->bi_vcnt != pkt->frames);	BUG_ON(pkt->bio->bi_size != pkt->frames * CD_FRAMESIZE);	BUG_ON(pkt->bio->bi_end_io != pkt_end_io_packet_write);	BUG_ON(pkt->bio->bi_private != pkt);	drop_super(sb);	return 1;out:	drop_super(sb);	return 0;#endif}static inline void pkt_set_state(struct packet_data *pkt, enum packet_data_state state){#if PACKET_DEBUG > 1	static const char *state_name[] = {		"IDLE", "WAITING", "READ_WAIT", "WRITE_WAIT", "RECOVERY", "FINISHED"	};	enum packet_data_state old_state = pkt->state;	VPRINTK("pkt %2d : s=%6llx %s -> %s\n", pkt->id, (unsigned long long)pkt->sector,		state_name[old_state], state_name[state]);#endif	pkt->state = state;}/* * Scan the work queue to see if we can start a new packet. * returns non-zero if any work was done. */static int pkt_handle_queue(struct pktcdvd_device *pd){	struct packet_data *pkt, *p;	struct bio *bio = NULL;	sector_t zone = 0; /* Suppress gcc warning */	struct pkt_rb_node *node, *first_node;	struct rb_node *n;	int wakeup;	VPRINTK("handle_queue\n");	atomic_set(&pd->scan_queue, 0);	if (list_empty(&pd->cdrw.pkt_free_list)) {		VPRINTK("handle_queue: no pkt\n");		return 0;	}	/*	 * Try to find a zone we are not already working on.	 */	spin_lock(&pd->lock);	first_node = pkt_rbtree_find(pd, pd->current_sector);	if (!first_node) {		n = rb_first(&pd->bio_queue);		if (n)			first_node = rb_entry(n, struct pkt_rb_node, rb_node);	}	node = first_node;	while (node) {		bio = node->bio;		zone = ZONE(bio->bi_sector, pd);		list_for_each_entry(p, &pd->cdrw.pkt_active_list, list) {			if (p->sector == zone) {				bio = NULL;				goto try_next_bio;			}		}		break;try_next_bio:		node = pkt_rbtree_next(node);		if (!node) {			n = rb_first(&pd->bio_queue);			if (n)				node = rb_entry(n, struct pkt_rb_node, rb_node);		}		if (node == first_node)			node = NULL;	}	spin_unlock(&pd->lock);	if (!bio) {		VPRINTK("handle_queue: no bio\n");		return 0;	}	pkt = pkt_get_packet_data(pd, zone);	pd->current_sector = zone + pd->settings.size;	pkt->sector = zone;	BUG_ON(pkt->frames != pd->settings.size >> 2);	pkt->write_size = 0;	/*	 * Scan work queue for bios in the same zone and link them	 * to this packet.	 */	spin_lock(&pd->lock);	VPRINTK("pkt_handle_queue: looking for zone %llx\n", (unsigned long long)zone);	while ((node = pkt_rbtree_find(pd, zone)) != NULL) {		bio = node->bio;		VPRINTK("pkt_handle_queue: found zone=%llx\n",			(unsigned long long)ZONE(bio->bi_sector, pd));		if (ZONE(bio->bi_sector, pd) != zone)			break;		pkt_rbtree_erase(pd, node);		spin_lock(&pkt->lock);		pkt_add_list_last(bio, &pkt->orig_bios, &pkt->orig_bios_tail);		pkt->write_size += bio->bi_size / CD_FRAMESIZE;		spin_unlock(&pkt->lock);	}	/* check write congestion marks, and if bio_queue_size is	   below, wake up any waiters */	wakeup = (pd->write_congestion_on > 0	 		&& pd->bio_queue_size <= pd->write_congestion_off);	spin_unlock(&pd->lock);	if (wakeup)		clear_bdi_congested(&pd->disk->queue->backing_dev_info, WRITE);	pkt->sleep_time = max(PACKET_WAIT_TIME, 1);	pkt_set_state(pkt, PACKET_WAITING_STATE);	atomic_set(&pkt->run_sm, 1);	spin_lock(&pd->cdrw.active_list_lock);	list_add(&pkt->list, &pd->cdrw.pkt_active_list);	spin_unlock(&pd->cdrw.active_list_lock);	return 1;}/* * Assemble a bio to write one packet and queue the bio for processing * by the underlying block device. */static void pkt_start_write(struct pktcdvd_device *pd, struct packet_data *pkt){	struct bio *bio;	int f;	int frames_write;	struct bio_vec *bvec = pkt->w_bio->bi_io_vec;	for (f = 0; f < pkt->frames; f++) {		bvec[f].bv_page = pkt->pages[(f * CD_FRAMESIZE) / PAGE_SIZE];		bvec[f].bv_offset = (f * CD_FRAMESIZE) % PAGE_SIZE;	}	/*	 * Fill-in bvec with data from orig_bios.	 */	frames_write = 0;	spin_lock(&pkt->lock);	for (bio = pkt->orig_bios; bio; bio = bio->bi_next) {		int segment = bio->bi_idx;		int src_offs = 0;		int first_frame = (bio->bi_sector - pkt->sector) / (CD_FRAMESIZE >> 9);		int num_frames = bio->bi_size / CD_FRAMESIZE;		BUG_ON(first_frame < 0);		BUG_ON(first_frame + num_frames > pkt->frames);		for (f = first_frame; f < first_frame + num_frames; f++) {			struct bio_vec *src_bvl = bio_iovec_idx(bio, segment);			while (src_offs >= src_bvl->bv_len) {				src_offs -= src_bvl->bv_len;				segment++;				BUG_ON(segment >= bio->bi_vcnt);				src_bvl = bio_iovec_idx(bio, segment);			}			if (src_bvl->bv_len - src_offs >= CD_FRAMESIZE) {				bvec[f].bv_page = src_bvl->bv_page;				bvec[f].bv_offset = src_bvl->bv_offset + src_offs;			} else {				pkt_copy_bio_data(bio, segment, src_offs,						  bvec[f].bv_page, bvec[f].bv_offset);			}			src_offs += CD_FRAMESIZE;			frames_write++;		}	}	pkt_set_state(pkt, PACKET_WRITE_WAIT_STATE);	spin_unlock(&pkt->lock);	VPRINTK("pkt_start_write: Writing %d frames for zone %llx\n",		frames_write, (unsigned long long)pkt->sector);	BUG_ON(frames_write != pkt->write_size);	if (test_bit(PACKET_MERGE_SEGS, &pd->flags) || (pkt->write_size < pkt->frames)) {		pkt_make_local_copy(pkt, bvec);		pkt->cache_valid = 1;	} else {		pkt->cache_valid = 0;	}	/* Start the write request */	bio_init(pkt->w_bio);	pkt->w_bio->bi_max_vecs = PACKET_MAX_SIZE;	pkt->w_bio->bi_sector = pkt->sector;	pkt->w_bio->bi_bdev = pd->bdev;	pkt->w_bio->bi_end_io = pkt_end_io_packet_write;	pkt->w_bio->bi_private = pkt;	pkt->w_bio->bi_io_vec = bvec;	pkt->w_bio->bi_destructor = pkt_bio_destructor;	for (f = 0; f < pkt->frames; f++)		if (!bio_add_page(pkt->w_bio, bvec[f].bv_page, CD_FRAMESIZE, bvec[f].bv_offset))			BUG();	VPRINTK(DRIVER_NAME": vcnt=%d\n", pkt->w_bio->bi_vcnt);	atomic_set(&pkt->io_wait, 1);	pkt->w_bio->bi_rw = WRITE;	pkt_queue_bio(pd, pkt->w_bio);}static void pkt_finish_packet(struct packet_data *pkt, int uptodate){	struct bio *bio, *next;	if (!uptodate)		pkt->cache_valid = 0;	/* Finish all bios corresponding to this packet */	bio = pkt->orig_bios;	while (bio) {		next = bio->bi_next;		bio->bi_next = NULL;		bio_endio(bio, uptodate ? 0 : -EIO);		bio = next;	}	pkt->orig_bios = pkt->orig_bios_tail = NULL;}static void pkt_run_state_machine(struct pktcdvd_device *pd, struct packet_data *pkt){	int uptodate;	VPRINTK("run_state_machine: pkt %d\n", pkt->id);	for (;;) {		switch (pkt->state) {		case PACKET_WAITING_STATE:			if ((pkt->write_size < pkt->frames) && (pkt->sleep_time > 0))				return;			pkt->sleep_time = 0;			pkt_gather_data(pd, pkt);			pkt_set_state(pkt, PACKET_READ_WAIT_STATE);			break;		case PACKET_READ_WAIT_STATE:			if (atomic_read(&pkt->io_wait) > 0)				return;			if (atomic_read(&pkt->io_errors) > 0) {				pkt_set_state(pkt, PACKET_RECOVERY_STATE);			} else {				pkt_start_write(pd, pkt);			}			break;		case PACKET_WRITE_WAIT_STATE:			if (atomic_read(&pkt->io_wait) > 0)				return;			if (test_bit(BIO_UPTODATE, &pkt->w_bio->bi_flags)) {				pkt_set_state(pkt, PACKET_FINISHED_STATE);			} else {				pkt_set_state(pkt, PACKET_RECOVERY_STATE);			}			break;		case PACKET_RECOVERY_STATE:			if (pkt_start_recovery(pkt)) {				pkt_start_write(pd, pkt);			} else {				VPRINTK("No recovery possible\n");				pkt_set_state(pkt, PACKET_FINISHED_STATE);			}			break;		case PACKET_FINISHED_STATE:			uptodate = test_bit(BIO_UPTODATE, &pkt->w_bio->bi_flags);			pkt_finish_packet(pkt, uptodate);			return;		default:			BUG();			break;		}	}}static void pkt_handle_packets(struct pktcdvd_device *pd){	struct packet_data *pkt, *next;	VPRINTK("pkt_handle_packets\n");	/*	 * Run state machine for active packets	 */	list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) {		if (atomic_read(&pkt->run_sm) > 0) {			atomic_set(&pkt->run_sm, 0);			pkt_run_state_machine(pd, pkt);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?