浏览代码

[PATCH] pktcdvd: Don't waste kernel memory

Allocate memory for read-gathering at open time, when it is known just how
much memory is needed.  This avoids wasting kernel memory when the real packet
size is smaller than the maximum packet size supported by the driver.  This is
always the case when using DVD discs.

Signed-off-by: Peter Osterlund <petero2@telia.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Peter Osterlund 19 年之前
父节点
当前提交
e1bc89bc99
共有 3 个文件被更改,包括 32 次插入29 次删除
  1. 2 2
      drivers/block/Kconfig
  2. 28 25
      drivers/block/pktcdvd.c
  3. 2 2
      include/linux/pktcdvd.h

+ 2 - 2
drivers/block/Kconfig

@@ -433,8 +433,8 @@ config CDROM_PKTCDVD_BUFFERS
 	  This controls the maximum number of active concurrent packets. More
 	  This controls the maximum number of active concurrent packets. More
 	  concurrent packets can increase write performance, but also require
 	  concurrent packets can increase write performance, but also require
 	  more memory. Each concurrent packet will require approximately 64Kb
 	  more memory. Each concurrent packet will require approximately 64Kb
-	  of non-swappable kernel memory, memory which will be allocated at
-	  pktsetup time.
+	  of non-swappable kernel memory, memory which will be allocated when
+	  a disc is opened for writing.
 
 
 config CDROM_PKTCDVD_WCACHE
 config CDROM_PKTCDVD_WCACHE
 	bool "Enable write caching (EXPERIMENTAL)"
 	bool "Enable write caching (EXPERIMENTAL)"

+ 28 - 25
drivers/block/pktcdvd.c

@@ -129,7 +129,7 @@ static struct bio *pkt_bio_alloc(int nr_iovecs)
 /*
 /*
  * Allocate a packet_data struct
  * Allocate a packet_data struct
  */
  */
-static struct packet_data *pkt_alloc_packet_data(void)
+static struct packet_data *pkt_alloc_packet_data(int frames)
 {
 {
 	int i;
 	int i;
 	struct packet_data *pkt;
 	struct packet_data *pkt;
@@ -138,11 +138,12 @@ static struct packet_data *pkt_alloc_packet_data(void)
 	if (!pkt)
 	if (!pkt)
 		goto no_pkt;
 		goto no_pkt;
 
 
-	pkt->w_bio = pkt_bio_alloc(PACKET_MAX_SIZE);
+	pkt->frames = frames;
+	pkt->w_bio = pkt_bio_alloc(frames);
 	if (!pkt->w_bio)
 	if (!pkt->w_bio)
 		goto no_bio;
 		goto no_bio;
 
 
-	for (i = 0; i < PAGES_PER_PACKET; i++) {
+	for (i = 0; i < frames / FRAMES_PER_PAGE; i++) {
 		pkt->pages[i] = alloc_page(GFP_KERNEL|__GFP_ZERO);
 		pkt->pages[i] = alloc_page(GFP_KERNEL|__GFP_ZERO);
 		if (!pkt->pages[i])
 		if (!pkt->pages[i])
 			goto no_page;
 			goto no_page;
@@ -150,7 +151,7 @@ static struct packet_data *pkt_alloc_packet_data(void)
 
 
 	spin_lock_init(&pkt->lock);
 	spin_lock_init(&pkt->lock);
 
 
-	for (i = 0; i < PACKET_MAX_SIZE; i++) {
+	for (i = 0; i < frames; i++) {
 		struct bio *bio = pkt_bio_alloc(1);
 		struct bio *bio = pkt_bio_alloc(1);
 		if (!bio)
 		if (!bio)
 			goto no_rd_bio;
 			goto no_rd_bio;
@@ -160,14 +161,14 @@ static struct packet_data *pkt_alloc_packet_data(void)
 	return pkt;
 	return pkt;
 
 
 no_rd_bio:
 no_rd_bio:
-	for (i = 0; i < PACKET_MAX_SIZE; i++) {
+	for (i = 0; i < frames; i++) {
 		struct bio *bio = pkt->r_bios[i];
 		struct bio *bio = pkt->r_bios[i];
 		if (bio)
 		if (bio)
 			bio_put(bio);
 			bio_put(bio);
 	}
 	}
 
 
 no_page:
 no_page:
-	for (i = 0; i < PAGES_PER_PACKET; i++)
+	for (i = 0; i < frames / FRAMES_PER_PAGE; i++)
 		if (pkt->pages[i])
 		if (pkt->pages[i])
 			__free_page(pkt->pages[i]);
 			__free_page(pkt->pages[i]);
 	bio_put(pkt->w_bio);
 	bio_put(pkt->w_bio);
@@ -184,12 +185,12 @@ static void pkt_free_packet_data(struct packet_data *pkt)
 {
 {
 	int i;
 	int i;
 
 
-	for (i = 0; i < PACKET_MAX_SIZE; i++) {
+	for (i = 0; i < pkt->frames; i++) {
 		struct bio *bio = pkt->r_bios[i];
 		struct bio *bio = pkt->r_bios[i];
 		if (bio)
 		if (bio)
 			bio_put(bio);
 			bio_put(bio);
 	}
 	}
-	for (i = 0; i < PAGES_PER_PACKET; i++)
+	for (i = 0; i < pkt->frames / FRAMES_PER_PAGE; i++)
 		__free_page(pkt->pages[i]);
 		__free_page(pkt->pages[i]);
 	bio_put(pkt->w_bio);
 	bio_put(pkt->w_bio);
 	kfree(pkt);
 	kfree(pkt);
@@ -204,17 +205,17 @@ static void pkt_shrink_pktlist(struct pktcdvd_device *pd)
 	list_for_each_entry_safe(pkt, next, &pd->cdrw.pkt_free_list, list) {
 	list_for_each_entry_safe(pkt, next, &pd->cdrw.pkt_free_list, list) {
 		pkt_free_packet_data(pkt);
 		pkt_free_packet_data(pkt);
 	}
 	}
+	INIT_LIST_HEAD(&pd->cdrw.pkt_free_list);
 }
 }
 
 
 static int pkt_grow_pktlist(struct pktcdvd_device *pd, int nr_packets)
 static int pkt_grow_pktlist(struct pktcdvd_device *pd, int nr_packets)
 {
 {
 	struct packet_data *pkt;
 	struct packet_data *pkt;
 
 
-	INIT_LIST_HEAD(&pd->cdrw.pkt_free_list);
-	INIT_LIST_HEAD(&pd->cdrw.pkt_active_list);
-	spin_lock_init(&pd->cdrw.active_list_lock);
+	BUG_ON(!list_empty(&pd->cdrw.pkt_free_list));
+
 	while (nr_packets > 0) {
 	while (nr_packets > 0) {
-		pkt = pkt_alloc_packet_data();
+		pkt = pkt_alloc_packet_data(pd->settings.size >> 2);
 		if (!pkt) {
 		if (!pkt) {
 			pkt_shrink_pktlist(pd);
 			pkt_shrink_pktlist(pd);
 			return 0;
 			return 0;
@@ -949,7 +950,7 @@ try_next_bio:
 
 
 	pd->current_sector = zone + pd->settings.size;
 	pd->current_sector = zone + pd->settings.size;
 	pkt->sector = zone;
 	pkt->sector = zone;
-	pkt->frames = pd->settings.size >> 2;
+	BUG_ON(pkt->frames != pd->settings.size >> 2);
 	pkt->write_size = 0;
 	pkt->write_size = 0;
 
 
 	/*
 	/*
@@ -1985,8 +1986,14 @@ static int pkt_open_dev(struct pktcdvd_device *pd, int write)
 	if ((ret = pkt_set_segment_merging(pd, q)))
 	if ((ret = pkt_set_segment_merging(pd, q)))
 		goto out_unclaim;
 		goto out_unclaim;
 
 
-	if (write)
+	if (write) {
+		if (!pkt_grow_pktlist(pd, CONFIG_CDROM_PKTCDVD_BUFFERS)) {
+			printk("pktcdvd: not enough memory for buffers\n");
+			ret = -ENOMEM;
+			goto out_unclaim;
+		}
 		printk("pktcdvd: %lukB available on disc\n", lba << 1);
 		printk("pktcdvd: %lukB available on disc\n", lba << 1);
+	}
 
 
 	return 0;
 	return 0;
 
 
@@ -2012,6 +2019,8 @@ static void pkt_release_dev(struct pktcdvd_device *pd, int flush)
 	pkt_set_speed(pd, MAX_SPEED, MAX_SPEED);
 	pkt_set_speed(pd, MAX_SPEED, MAX_SPEED);
 	bd_release(pd->bdev);
 	bd_release(pd->bdev);
 	blkdev_put(pd->bdev);
 	blkdev_put(pd->bdev);
+
+	pkt_shrink_pktlist(pd);
 }
 }
 
 
 static struct pktcdvd_device *pkt_find_dev_from_minor(int dev_minor)
 static struct pktcdvd_device *pkt_find_dev_from_minor(int dev_minor)
@@ -2377,12 +2386,6 @@ static int pkt_new_dev(struct pktcdvd_device *pd, dev_t dev)
 	/* This is safe, since we have a reference from open(). */
 	/* This is safe, since we have a reference from open(). */
 	__module_get(THIS_MODULE);
 	__module_get(THIS_MODULE);
 
 
-	if (!pkt_grow_pktlist(pd, CONFIG_CDROM_PKTCDVD_BUFFERS)) {
-		printk("pktcdvd: not enough memory for buffers\n");
-		ret = -ENOMEM;
-		goto out_mem;
-	}
-
 	pd->bdev = bdev;
 	pd->bdev = bdev;
 	set_blocksize(bdev, CD_FRAMESIZE);
 	set_blocksize(bdev, CD_FRAMESIZE);
 
 
@@ -2393,7 +2396,7 @@ static int pkt_new_dev(struct pktcdvd_device *pd, dev_t dev)
 	if (IS_ERR(pd->cdrw.thread)) {
 	if (IS_ERR(pd->cdrw.thread)) {
 		printk("pktcdvd: can't start kernel thread\n");
 		printk("pktcdvd: can't start kernel thread\n");
 		ret = -ENOMEM;
 		ret = -ENOMEM;
-		goto out_thread;
+		goto out_mem;
 	}
 	}
 
 
 	proc = create_proc_entry(pd->name, 0, pkt_proc);
 	proc = create_proc_entry(pd->name, 0, pkt_proc);
@@ -2404,8 +2407,6 @@ static int pkt_new_dev(struct pktcdvd_device *pd, dev_t dev)
 	DPRINTK("pktcdvd: writer %s mapped to %s\n", pd->name, bdevname(bdev, b));
 	DPRINTK("pktcdvd: writer %s mapped to %s\n", pd->name, bdevname(bdev, b));
 	return 0;
 	return 0;
 
 
-out_thread:
-	pkt_shrink_pktlist(pd);
 out_mem:
 out_mem:
 	blkdev_put(bdev);
 	blkdev_put(bdev);
 	/* This is safe: open() is still holding a reference. */
 	/* This is safe: open() is still holding a reference. */
@@ -2501,6 +2502,10 @@ static int pkt_setup_dev(struct pkt_ctrl_command *ctrl_cmd)
 		goto out_mem;
 		goto out_mem;
 	pd->disk = disk;
 	pd->disk = disk;
 
 
+	INIT_LIST_HEAD(&pd->cdrw.pkt_free_list);
+	INIT_LIST_HEAD(&pd->cdrw.pkt_active_list);
+	spin_lock_init(&pd->cdrw.active_list_lock);
+
 	spin_lock_init(&pd->lock);
 	spin_lock_init(&pd->lock);
 	spin_lock_init(&pd->iosched.lock);
 	spin_lock_init(&pd->iosched.lock);
 	sprintf(pd->name, "pktcdvd%d", idx);
 	sprintf(pd->name, "pktcdvd%d", idx);
@@ -2565,8 +2570,6 @@ static int pkt_remove_dev(struct pkt_ctrl_command *ctrl_cmd)
 
 
 	blkdev_put(pd->bdev);
 	blkdev_put(pd->bdev);
 
 
-	pkt_shrink_pktlist(pd);
-
 	remove_proc_entry(pd->name, pkt_proc);
 	remove_proc_entry(pd->name, pkt_proc);
 	DPRINTK("pktcdvd: writer %s unmapped\n", pd->name);
 	DPRINTK("pktcdvd: writer %s unmapped\n", pd->name);
 
 

+ 2 - 2
include/linux/pktcdvd.h

@@ -170,7 +170,7 @@ struct packet_iosched
 #error "PAGE_SIZE must be a multiple of CD_FRAMESIZE"
 #error "PAGE_SIZE must be a multiple of CD_FRAMESIZE"
 #endif
 #endif
 #define PACKET_MAX_SIZE		32
 #define PACKET_MAX_SIZE		32
-#define PAGES_PER_PACKET	(PACKET_MAX_SIZE * CD_FRAMESIZE / PAGE_SIZE)
+#define FRAMES_PER_PAGE		(PAGE_SIZE / CD_FRAMESIZE)
 #define PACKET_MAX_SECTORS	(PACKET_MAX_SIZE * CD_FRAMESIZE >> 9)
 #define PACKET_MAX_SECTORS	(PACKET_MAX_SIZE * CD_FRAMESIZE >> 9)
 
 
 enum packet_data_state {
 enum packet_data_state {
@@ -219,7 +219,7 @@ struct packet_data
 	atomic_t		io_errors;	/* Number of read/write errors during IO */
 	atomic_t		io_errors;	/* Number of read/write errors during IO */
 
 
 	struct bio		*r_bios[PACKET_MAX_SIZE]; /* bios to use during data gathering */
 	struct bio		*r_bios[PACKET_MAX_SIZE]; /* bios to use during data gathering */
-	struct page		*pages[PAGES_PER_PACKET];
+	struct page		*pages[PACKET_MAX_SIZE / FRAMES_PER_PAGE];
 
 
 	int			cache_valid;	/* If non-zero, the data for the zone defined */
 	int			cache_valid;	/* If non-zero, the data for the zone defined */
 						/* by the sector variable is completely cached */
 						/* by the sector variable is completely cached */