From 7d116b626b843c66b17630a62d696e044d049bfc Mon Sep 17 00:00:00 2001 From: Fernando Luis Vázquez Cao Date: Mon, 27 Oct 2008 18:45:15 +0900 Subject: virtio_blk: set queue paravirt flag As a paravirt front-end driver, virtio_blk is not a rotational device so we want do avoid idling in AS/CFQ. Tell the block layer about this. Signed-off-by: Fernando Luis Vazquez Cao Signed-off-by: Jens Axboe --- drivers/block/virtio_blk.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c index 85d79a02d487..f151592ecf73 100644 --- a/drivers/block/virtio_blk.c +++ b/drivers/block/virtio_blk.c @@ -237,6 +237,8 @@ static int virtblk_probe(struct virtio_device *vdev) goto out_put_disk; } + queue_flag_set_unlocked(QUEUE_FLAG_VIRT, vblk->disk->queue); + if (index < 26) { sprintf(vblk->disk->disk_name, "vd%c", 'a' + index % 26); } else if (index < (26 + 1) * 26) { -- cgit v1.2.3 From 66d352e1e410dcea22fecb9fa9ec09dd23a62e1c Mon Sep 17 00:00:00 2001 From: Fernando Luis Vázquez Cao Date: Mon, 27 Oct 2008 18:45:54 +0900 Subject: xen-blkfront: set queue paravirt flag Xen's blkfront sets noop as the default I/O scheduler at initialization time to avoid elevator overheads such as idling, but with the advent of basic disk profiling capabilities this is not necessary anymore. We should just tell the block layer that we are a paravirt front-end driver and the elevator will automatically make the necessary adjustments. Signed-off-by: Fernando Luis Vazquez Cao Signed-off-by: Jens Axboe --- drivers/block/xen-blkfront.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c index 2d19f0cc47f2..918ef725de41 100644 --- a/drivers/block/xen-blkfront.c +++ b/drivers/block/xen-blkfront.c @@ -338,18 +338,12 @@ wait: static int xlvbd_init_blk_queue(struct gendisk *gd, u16 sector_size) { struct request_queue *rq; - elevator_t *old_e; rq = blk_init_queue(do_blkif_request, &blkif_io_lock); if (rq == NULL) return -1; - old_e = rq->elevator; - if (IS_ERR_VALUE(elevator_init(rq, "noop"))) - printk(KERN_WARNING - "blkfront: Switch elevator failed, use default\n"); - else - elevator_exit(old_e); + queue_flag_set_unlocked(QUEUE_FLAG_VIRT, rq); /* Hard sector size and max sectors impersonate the equiv. hardware. */ blk_queue_hardsect_size(rq, sector_size); -- cgit v1.2.3 From 3147c531b6b53fbbf8942689c4aa113c519f5e4e Mon Sep 17 00:00:00 2001 From: Marcin Slusarz Date: Sun, 16 Nov 2008 19:04:47 +0100 Subject: cdrom: split mmc_ioctl to lower stack usage Checkstack output: Before: mmc_ioctl: 584 After: mmc_ioctl_dvd_read_struct: 280 mmc_ioctl_cdrom_subchannel: 152 mmc_ioctl_cdrom_read_data: 120 mmc_ioctl_cdrom_volume: 104 mmc_ioctl_cdrom_read_audio: 104 (mmc_ioctl is inlined into cdrom_ioctl - 104 bytes) Signed-off-by: Marcin Slusarz Cc: Bartlomiej Zolnierkiewicz Cc: Andrew Morton Cc: Jens Axboe Signed-off-by: Jens Axboe --- drivers/cdrom/cdrom.c | 570 +++++++++++++++++++++++++++++--------------------- 1 file changed, 329 insertions(+), 241 deletions(-) (limited to 'drivers') diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c index 7d2e91cccb13..b927aa244247 100644 --- a/drivers/cdrom/cdrom.c +++ b/drivers/cdrom/cdrom.c @@ -2787,271 +2787,359 @@ static int cdrom_switch_blocksize(struct cdrom_device_info *cdi, int size) return cdo->generic_packet(cdi, &cgc); } -static int mmc_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, - unsigned long arg) -{ - struct cdrom_device_ops *cdo = cdi->ops; - struct packet_command cgc; +static noinline int mmc_ioctl_cdrom_read_data(struct cdrom_device_info *cdi, + void __user *arg, + struct packet_command *cgc, + int cmd) +{ struct request_sense sense; - unsigned char buffer[32]; - int ret = 0; - - memset(&cgc, 0, sizeof(cgc)); + struct cdrom_msf msf; + int blocksize = 0, format = 0, lba; + int ret; - /* build a unified command and queue it through - cdo->generic_packet() */ switch (cmd) { case CDROMREADRAW: + blocksize = CD_FRAMESIZE_RAW; + break; case CDROMREADMODE1: - case CDROMREADMODE2: { - struct cdrom_msf msf; - int blocksize = 0, format = 0, lba; - - switch (cmd) { - case CDROMREADRAW: - blocksize = CD_FRAMESIZE_RAW; - break; - case CDROMREADMODE1: - blocksize = CD_FRAMESIZE; - format = 2; - break; - case CDROMREADMODE2: - blocksize = CD_FRAMESIZE_RAW0; - break; - } - IOCTL_IN(arg, struct cdrom_msf, msf); - lba = msf_to_lba(msf.cdmsf_min0,msf.cdmsf_sec0,msf.cdmsf_frame0); - /* FIXME: we need upper bound checking, too!! */ - if (lba < 0) - return -EINVAL; - cgc.buffer = kmalloc(blocksize, GFP_KERNEL); - if (cgc.buffer == NULL) - return -ENOMEM; - memset(&sense, 0, sizeof(sense)); - cgc.sense = &sense; - cgc.data_direction = CGC_DATA_READ; - ret = cdrom_read_block(cdi, &cgc, lba, 1, format, blocksize); - if (ret && sense.sense_key==0x05 && sense.asc==0x20 && sense.ascq==0x00) { - /* - * SCSI-II devices are not required to support - * READ_CD, so let's try switching block size - */ - /* FIXME: switch back again... */ - if ((ret = cdrom_switch_blocksize(cdi, blocksize))) { - kfree(cgc.buffer); - return ret; - } - cgc.sense = NULL; - ret = cdrom_read_cd(cdi, &cgc, lba, blocksize, 1); - ret |= cdrom_switch_blocksize(cdi, blocksize); - } - if (!ret && copy_to_user((char __user *)arg, cgc.buffer, blocksize)) - ret = -EFAULT; - kfree(cgc.buffer); + blocksize = CD_FRAMESIZE; + format = 2; + break; + case CDROMREADMODE2: + blocksize = CD_FRAMESIZE_RAW0; + break; + } + IOCTL_IN(arg, struct cdrom_msf, msf); + lba = msf_to_lba(msf.cdmsf_min0, msf.cdmsf_sec0, msf.cdmsf_frame0); + /* FIXME: we need upper bound checking, too!! */ + if (lba < 0) + return -EINVAL; + + cgc->buffer = kmalloc(blocksize, GFP_KERNEL); + if (cgc->buffer == NULL) + return -ENOMEM; + + memset(&sense, 0, sizeof(sense)); + cgc->sense = &sense; + cgc->data_direction = CGC_DATA_READ; + ret = cdrom_read_block(cdi, cgc, lba, 1, format, blocksize); + if (ret && sense.sense_key == 0x05 && + sense.asc == 0x20 && + sense.ascq == 0x00) { + /* + * SCSI-II devices are not required to support + * READ_CD, so let's try switching block size + */ + /* FIXME: switch back again... */ + ret = cdrom_switch_blocksize(cdi, blocksize); + if (ret) + goto out; + cgc->sense = NULL; + ret = cdrom_read_cd(cdi, cgc, lba, blocksize, 1); + ret |= cdrom_switch_blocksize(cdi, blocksize); + } + if (!ret && copy_to_user(arg, cgc->buffer, blocksize)) + ret = -EFAULT; +out: + kfree(cgc->buffer); + return ret; +} + +static noinline int mmc_ioctl_cdrom_read_audio(struct cdrom_device_info *cdi, + void __user *arg) +{ + struct cdrom_read_audio ra; + int lba; + + IOCTL_IN(arg, struct cdrom_read_audio, ra); + + if (ra.addr_format == CDROM_MSF) + lba = msf_to_lba(ra.addr.msf.minute, + ra.addr.msf.second, + ra.addr.msf.frame); + else if (ra.addr_format == CDROM_LBA) + lba = ra.addr.lba; + else + return -EINVAL; + + /* FIXME: we need upper bound checking, too!! */ + if (lba < 0 || ra.nframes <= 0 || ra.nframes > CD_FRAMES) + return -EINVAL; + + return cdrom_read_cdda(cdi, ra.buf, lba, ra.nframes); +} + +static noinline int mmc_ioctl_cdrom_subchannel(struct cdrom_device_info *cdi, + void __user *arg) +{ + int ret; + struct cdrom_subchnl q; + u_char requested, back; + IOCTL_IN(arg, struct cdrom_subchnl, q); + requested = q.cdsc_format; + if (!((requested == CDROM_MSF) || + (requested == CDROM_LBA))) + return -EINVAL; + q.cdsc_format = CDROM_MSF; + ret = cdrom_read_subchannel(cdi, &q, 0); + if (ret) return ret; - } - case CDROMREADAUDIO: { - struct cdrom_read_audio ra; - int lba; - - IOCTL_IN(arg, struct cdrom_read_audio, ra); - - if (ra.addr_format == CDROM_MSF) - lba = msf_to_lba(ra.addr.msf.minute, - ra.addr.msf.second, - ra.addr.msf.frame); - else if (ra.addr_format == CDROM_LBA) - lba = ra.addr.lba; - else - return -EINVAL; + back = q.cdsc_format; /* local copy */ + sanitize_format(&q.cdsc_absaddr, &back, requested); + sanitize_format(&q.cdsc_reladdr, &q.cdsc_format, requested); + IOCTL_OUT(arg, struct cdrom_subchnl, q); + /* cdinfo(CD_DO_IOCTL, "CDROMSUBCHNL successful\n"); */ + return 0; +} - /* FIXME: we need upper bound checking, too!! */ - if (lba < 0 || ra.nframes <= 0 || ra.nframes > CD_FRAMES) - return -EINVAL; +static noinline int mmc_ioctl_cdrom_play_msf(struct cdrom_device_info *cdi, + void __user *arg, + struct packet_command *cgc) +{ + struct cdrom_device_ops *cdo = cdi->ops; + struct cdrom_msf msf; + cdinfo(CD_DO_IOCTL, "entering CDROMPLAYMSF\n"); + IOCTL_IN(arg, struct cdrom_msf, msf); + cgc->cmd[0] = GPCMD_PLAY_AUDIO_MSF; + cgc->cmd[3] = msf.cdmsf_min0; + cgc->cmd[4] = msf.cdmsf_sec0; + cgc->cmd[5] = msf.cdmsf_frame0; + cgc->cmd[6] = msf.cdmsf_min1; + cgc->cmd[7] = msf.cdmsf_sec1; + cgc->cmd[8] = msf.cdmsf_frame1; + cgc->data_direction = CGC_DATA_NONE; + return cdo->generic_packet(cdi, cgc); +} - return cdrom_read_cdda(cdi, ra.buf, lba, ra.nframes); - } - case CDROMSUBCHNL: { - struct cdrom_subchnl q; - u_char requested, back; - IOCTL_IN(arg, struct cdrom_subchnl, q); - requested = q.cdsc_format; - if (!((requested == CDROM_MSF) || - (requested == CDROM_LBA))) - return -EINVAL; - q.cdsc_format = CDROM_MSF; - if ((ret = cdrom_read_subchannel(cdi, &q, 0))) - return ret; - back = q.cdsc_format; /* local copy */ - sanitize_format(&q.cdsc_absaddr, &back, requested); - sanitize_format(&q.cdsc_reladdr, &q.cdsc_format, requested); - IOCTL_OUT(arg, struct cdrom_subchnl, q); - /* cdinfo(CD_DO_IOCTL, "CDROMSUBCHNL successful\n"); */ - return 0; - } - case CDROMPLAYMSF: { - struct cdrom_msf msf; - cdinfo(CD_DO_IOCTL, "entering CDROMPLAYMSF\n"); - IOCTL_IN(arg, struct cdrom_msf, msf); - cgc.cmd[0] = GPCMD_PLAY_AUDIO_MSF; - cgc.cmd[3] = msf.cdmsf_min0; - cgc.cmd[4] = msf.cdmsf_sec0; - cgc.cmd[5] = msf.cdmsf_frame0; - cgc.cmd[6] = msf.cdmsf_min1; - cgc.cmd[7] = msf.cdmsf_sec1; - cgc.cmd[8] = msf.cdmsf_frame1; - cgc.data_direction = CGC_DATA_NONE; - return cdo->generic_packet(cdi, &cgc); - } - case CDROMPLAYBLK: { - struct cdrom_blk blk; - cdinfo(CD_DO_IOCTL, "entering CDROMPLAYBLK\n"); - IOCTL_IN(arg, struct cdrom_blk, blk); - cgc.cmd[0] = GPCMD_PLAY_AUDIO_10; - cgc.cmd[2] = (blk.from >> 24) & 0xff; - cgc.cmd[3] = (blk.from >> 16) & 0xff; - cgc.cmd[4] = (blk.from >> 8) & 0xff; - cgc.cmd[5] = blk.from & 0xff; - cgc.cmd[7] = (blk.len >> 8) & 0xff; - cgc.cmd[8] = blk.len & 0xff; - cgc.data_direction = CGC_DATA_NONE; - return cdo->generic_packet(cdi, &cgc); - } - case CDROMVOLCTRL: - case CDROMVOLREAD: { - struct cdrom_volctrl volctrl; - char mask[sizeof(buffer)]; - unsigned short offset; +static noinline int mmc_ioctl_cdrom_play_blk(struct cdrom_device_info *cdi, + void __user *arg, + struct packet_command *cgc) +{ + struct cdrom_device_ops *cdo = cdi->ops; + struct cdrom_blk blk; + cdinfo(CD_DO_IOCTL, "entering CDROMPLAYBLK\n"); + IOCTL_IN(arg, struct cdrom_blk, blk); + cgc->cmd[0] = GPCMD_PLAY_AUDIO_10; + cgc->cmd[2] = (blk.from >> 24) & 0xff; + cgc->cmd[3] = (blk.from >> 16) & 0xff; + cgc->cmd[4] = (blk.from >> 8) & 0xff; + cgc->cmd[5] = blk.from & 0xff; + cgc->cmd[7] = (blk.len >> 8) & 0xff; + cgc->cmd[8] = blk.len & 0xff; + cgc->data_direction = CGC_DATA_NONE; + return cdo->generic_packet(cdi, cgc); +} - cdinfo(CD_DO_IOCTL, "entering CDROMVOLUME\n"); +static noinline int mmc_ioctl_cdrom_volume(struct cdrom_device_info *cdi, + void __user *arg, + struct packet_command *cgc, + unsigned int cmd) +{ + struct cdrom_volctrl volctrl; + unsigned char buffer[32]; + char mask[sizeof(buffer)]; + unsigned short offset; + int ret; + + cdinfo(CD_DO_IOCTL, "entering CDROMVOLUME\n"); - IOCTL_IN(arg, struct cdrom_volctrl, volctrl); + IOCTL_IN(arg, struct cdrom_volctrl, volctrl); - cgc.buffer = buffer; - cgc.buflen = 24; - if ((ret = cdrom_mode_sense(cdi, &cgc, GPMODE_AUDIO_CTL_PAGE, 0))) - return ret; + cgc->buffer = buffer; + cgc->buflen = 24; + ret = cdrom_mode_sense(cdi, cgc, GPMODE_AUDIO_CTL_PAGE, 0); + if (ret) + return ret; - /* originally the code depended on buffer[1] to determine - how much data is available for transfer. buffer[1] is - unfortunately ambigious and the only reliable way seem - to be to simply skip over the block descriptor... */ - offset = 8 + be16_to_cpu(*(__be16 *)(buffer+6)); - - if (offset + 16 > sizeof(buffer)) - return -E2BIG; - - if (offset + 16 > cgc.buflen) { - cgc.buflen = offset+16; - ret = cdrom_mode_sense(cdi, &cgc, - GPMODE_AUDIO_CTL_PAGE, 0); - if (ret) - return ret; - } + /* originally the code depended on buffer[1] to determine + how much data is available for transfer. buffer[1] is + unfortunately ambigious and the only reliable way seem + to be to simply skip over the block descriptor... */ + offset = 8 + be16_to_cpu(*(__be16 *)(buffer + 6)); + + if (offset + 16 > sizeof(buffer)) + return -E2BIG; + + if (offset + 16 > cgc->buflen) { + cgc->buflen = offset + 16; + ret = cdrom_mode_sense(cdi, cgc, + GPMODE_AUDIO_CTL_PAGE, 0); + if (ret) + return ret; + } - /* sanity check */ - if ((buffer[offset] & 0x3f) != GPMODE_AUDIO_CTL_PAGE || - buffer[offset+1] < 14) - return -EINVAL; + /* sanity check */ + if ((buffer[offset] & 0x3f) != GPMODE_AUDIO_CTL_PAGE || + buffer[offset + 1] < 14) + return -EINVAL; - /* now we have the current volume settings. if it was only - a CDROMVOLREAD, return these values */ - if (cmd == CDROMVOLREAD) { - volctrl.channel0 = buffer[offset+9]; - volctrl.channel1 = buffer[offset+11]; - volctrl.channel2 = buffer[offset+13]; - volctrl.channel3 = buffer[offset+15]; - IOCTL_OUT(arg, struct cdrom_volctrl, volctrl); - return 0; - } + /* now we have the current volume settings. if it was only + a CDROMVOLREAD, return these values */ + if (cmd == CDROMVOLREAD) { + volctrl.channel0 = buffer[offset+9]; + volctrl.channel1 = buffer[offset+11]; + volctrl.channel2 = buffer[offset+13]; + volctrl.channel3 = buffer[offset+15]; + IOCTL_OUT(arg, struct cdrom_volctrl, volctrl); + return 0; + } - /* get the volume mask */ - cgc.buffer = mask; - if ((ret = cdrom_mode_sense(cdi, &cgc, - GPMODE_AUDIO_CTL_PAGE, 1))) - return ret; + /* get the volume mask */ + cgc->buffer = mask; + ret = cdrom_mode_sense(cdi, cgc, GPMODE_AUDIO_CTL_PAGE, 1); + if (ret) + return ret; - buffer[offset+9] = volctrl.channel0 & mask[offset+9]; - buffer[offset+11] = volctrl.channel1 & mask[offset+11]; - buffer[offset+13] = volctrl.channel2 & mask[offset+13]; - buffer[offset+15] = volctrl.channel3 & mask[offset+15]; + buffer[offset + 9] = volctrl.channel0 & mask[offset + 9]; + buffer[offset + 11] = volctrl.channel1 & mask[offset + 11]; + buffer[offset + 13] = volctrl.channel2 & mask[offset + 13]; + buffer[offset + 15] = volctrl.channel3 & mask[offset + 15]; - /* set volume */ - cgc.buffer = buffer + offset - 8; - memset(cgc.buffer, 0, 8); - return cdrom_mode_select(cdi, &cgc); - } + /* set volume */ + cgc->buffer = buffer + offset - 8; + memset(cgc->buffer, 0, 8); + return cdrom_mode_select(cdi, cgc); +} - case CDROMSTART: - case CDROMSTOP: { - cdinfo(CD_DO_IOCTL, "entering CDROMSTART/CDROMSTOP\n"); - cgc.cmd[0] = GPCMD_START_STOP_UNIT; - cgc.cmd[1] = 1; - cgc.cmd[4] = (cmd == CDROMSTART) ? 1 : 0; - cgc.data_direction = CGC_DATA_NONE; - return cdo->generic_packet(cdi, &cgc); - } +static noinline int mmc_ioctl_cdrom_start_stop(struct cdrom_device_info *cdi, + struct packet_command *cgc, + int cmd) +{ + struct cdrom_device_ops *cdo = cdi->ops; + cdinfo(CD_DO_IOCTL, "entering CDROMSTART/CDROMSTOP\n"); + cgc->cmd[0] = GPCMD_START_STOP_UNIT; + cgc->cmd[1] = 1; + cgc->cmd[4] = (cmd == CDROMSTART) ? 1 : 0; + cgc->data_direction = CGC_DATA_NONE; + return cdo->generic_packet(cdi, cgc); +} - case CDROMPAUSE: - case CDROMRESUME: { - cdinfo(CD_DO_IOCTL, "entering CDROMPAUSE/CDROMRESUME\n"); - cgc.cmd[0] = GPCMD_PAUSE_RESUME; - cgc.cmd[8] = (cmd == CDROMRESUME) ? 1 : 0; - cgc.data_direction = CGC_DATA_NONE; - return cdo->generic_packet(cdi, &cgc); - } +static noinline int mmc_ioctl_cdrom_pause_resume(struct cdrom_device_info *cdi, + struct packet_command *cgc, + int cmd) +{ + struct cdrom_device_ops *cdo = cdi->ops; + cdinfo(CD_DO_IOCTL, "entering CDROMPAUSE/CDROMRESUME\n"); + cgc->cmd[0] = GPCMD_PAUSE_RESUME; + cgc->cmd[8] = (cmd == CDROMRESUME) ? 1 : 0; + cgc->data_direction = CGC_DATA_NONE; + return cdo->generic_packet(cdi, cgc); +} - case DVD_READ_STRUCT: { - dvd_struct *s; - int size = sizeof(dvd_struct); - if (!CDROM_CAN(CDC_DVD)) - return -ENOSYS; - if ((s = kmalloc(size, GFP_KERNEL)) == NULL) - return -ENOMEM; - cdinfo(CD_DO_IOCTL, "entering DVD_READ_STRUCT\n"); - if (copy_from_user(s, (dvd_struct __user *)arg, size)) { - kfree(s); - return -EFAULT; - } - if ((ret = dvd_read_struct(cdi, s))) { - kfree(s); - return ret; - } - if (copy_to_user((dvd_struct __user *)arg, s, size)) - ret = -EFAULT; +static noinline int mmc_ioctl_dvd_read_struct(struct cdrom_device_info *cdi, + void __user *arg) +{ + int ret; + dvd_struct *s; + int size = sizeof(dvd_struct); + + if (!CDROM_CAN(CDC_DVD)) + return -ENOSYS; + + s = kmalloc(size, GFP_KERNEL); + if (!s) + return -ENOMEM; + + cdinfo(CD_DO_IOCTL, "entering DVD_READ_STRUCT\n"); + if (copy_from_user(s, arg, size)) { kfree(s); + return -EFAULT; + } + + ret = dvd_read_struct(cdi, s); + if (ret) + goto out; + + if (copy_to_user(arg, s, size)) + ret = -EFAULT; +out: + kfree(s); + return ret; +} + +static noinline int mmc_ioctl_dvd_auth(struct cdrom_device_info *cdi, + void __user *arg) +{ + int ret; + dvd_authinfo ai; + if (!CDROM_CAN(CDC_DVD)) + return -ENOSYS; + cdinfo(CD_DO_IOCTL, "entering DVD_AUTH\n"); + IOCTL_IN(arg, dvd_authinfo, ai); + ret = dvd_do_auth(cdi, &ai); + if (ret) return ret; - } + IOCTL_OUT(arg, dvd_authinfo, ai); + return 0; +} - case DVD_AUTH: { - dvd_authinfo ai; - if (!CDROM_CAN(CDC_DVD)) - return -ENOSYS; - cdinfo(CD_DO_IOCTL, "entering DVD_AUTH\n"); - IOCTL_IN(arg, dvd_authinfo, ai); - if ((ret = dvd_do_auth (cdi, &ai))) - return ret; - IOCTL_OUT(arg, dvd_authinfo, ai); - return 0; - } +static noinline int mmc_ioctl_cdrom_next_writable(struct cdrom_device_info *cdi, + void __user *arg) +{ + int ret; + long next = 0; + cdinfo(CD_DO_IOCTL, "entering CDROM_NEXT_WRITABLE\n"); + ret = cdrom_get_next_writable(cdi, &next); + if (ret) + return ret; + IOCTL_OUT(arg, long, next); + return 0; +} - case CDROM_NEXT_WRITABLE: { - long next = 0; - cdinfo(CD_DO_IOCTL, "entering CDROM_NEXT_WRITABLE\n"); - if ((ret = cdrom_get_next_writable(cdi, &next))) - return ret; - IOCTL_OUT(arg, long, next); - return 0; - } - case CDROM_LAST_WRITTEN: { - long last = 0; - cdinfo(CD_DO_IOCTL, "entering CDROM_LAST_WRITTEN\n"); - if ((ret = cdrom_get_last_written(cdi, &last))) - return ret; - IOCTL_OUT(arg, long, last); - return 0; - } - } /* switch */ +static noinline int mmc_ioctl_cdrom_last_written(struct cdrom_device_info *cdi, + void __user *arg) +{ + int ret; + long last = 0; + cdinfo(CD_DO_IOCTL, "entering CDROM_LAST_WRITTEN\n"); + ret = cdrom_get_last_written(cdi, &last); + if (ret) + return ret; + IOCTL_OUT(arg, long, last); + return 0; +} + +static int mmc_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, + unsigned long arg) +{ + struct packet_command cgc; + void __user *userptr = (void __user *)arg; + + memset(&cgc, 0, sizeof(cgc)); + + /* build a unified command and queue it through + cdo->generic_packet() */ + switch (cmd) { + case CDROMREADRAW: + case CDROMREADMODE1: + case CDROMREADMODE2: + return mmc_ioctl_cdrom_read_data(cdi, userptr, &cgc, cmd); + case CDROMREADAUDIO: + return mmc_ioctl_cdrom_read_audio(cdi, userptr); + case CDROMSUBCHNL: + return mmc_ioctl_cdrom_subchannel(cdi, userptr); + case CDROMPLAYMSF: + return mmc_ioctl_cdrom_play_msf(cdi, userptr, &cgc); + case CDROMPLAYBLK: + return mmc_ioctl_cdrom_play_blk(cdi, userptr, &cgc); + case CDROMVOLCTRL: + case CDROMVOLREAD: + return mmc_ioctl_cdrom_volume(cdi, userptr, &cgc, cmd); + case CDROMSTART: + case CDROMSTOP: + return mmc_ioctl_cdrom_start_stop(cdi, &cgc, cmd); + case CDROMPAUSE: + case CDROMRESUME: + return mmc_ioctl_cdrom_pause_resume(cdi, &cgc, cmd); + case DVD_READ_STRUCT: + return mmc_ioctl_dvd_read_struct(cdi, userptr); + case DVD_AUTH: + return mmc_ioctl_dvd_auth(cdi, userptr); + case CDROM_NEXT_WRITABLE: + return mmc_ioctl_cdrom_next_writable(cdi, userptr); + case CDROM_LAST_WRITTEN: + return mmc_ioctl_cdrom_last_written(cdi, userptr); + } return -ENOTTY; } -- cgit v1.2.3 From d194139c18edb7dae45e159a24f2aa72091033b8 Mon Sep 17 00:00:00 2001 From: Marcin Slusarz Date: Sun, 16 Nov 2008 19:06:37 +0100 Subject: cdrom: reduce stack usage of mmc_ioctl_dvd_read_struct 1. kmalloc 192 bytes in dvd_read_bca (which is inlined into dvd_read_struct) 2. Pass struct packet_command to all dvd_read_* functions. Checkstack output: Before: mmc_ioctl_dvd_read_struct: 280 After: mmc_ioctl_dvd_read_struct: 56 Signed-off-by: Marcin Slusarz Cc: Bartlomiej Zolnierkiewicz Cc: Andrew Morton Cc: Jens Axboe Signed-off-by: Jens Axboe --- drivers/cdrom/cdrom.c | 139 ++++++++++++++++++++++++++++---------------------- 1 file changed, 77 insertions(+), 62 deletions(-) (limited to 'drivers') diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c index b927aa244247..cceace61ef28 100644 --- a/drivers/cdrom/cdrom.c +++ b/drivers/cdrom/cdrom.c @@ -1712,29 +1712,30 @@ static int dvd_do_auth(struct cdrom_device_info *cdi, dvd_authinfo *ai) return 0; } -static int dvd_read_physical(struct cdrom_device_info *cdi, dvd_struct *s) +static int dvd_read_physical(struct cdrom_device_info *cdi, dvd_struct *s, + struct packet_command *cgc) { unsigned char buf[21], *base; struct dvd_layer *layer; - struct packet_command cgc; struct cdrom_device_ops *cdo = cdi->ops; int ret, layer_num = s->physical.layer_num; if (layer_num >= DVD_LAYERS) return -EINVAL; - init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_READ); - cgc.cmd[0] = GPCMD_READ_DVD_STRUCTURE; - cgc.cmd[6] = layer_num; - cgc.cmd[7] = s->type; - cgc.cmd[9] = cgc.buflen & 0xff; + init_cdrom_command(cgc, buf, sizeof(buf), CGC_DATA_READ); + cgc->cmd[0] = GPCMD_READ_DVD_STRUCTURE; + cgc->cmd[6] = layer_num; + cgc->cmd[7] = s->type; + cgc->cmd[9] = cgc->buflen & 0xff; /* * refrain from reporting errors on non-existing layers (mainly) */ - cgc.quiet = 1; + cgc->quiet = 1; - if ((ret = cdo->generic_packet(cdi, &cgc))) + ret = cdo->generic_packet(cdi, cgc); + if (ret) return ret; base = &buf[4]; @@ -1762,21 +1763,22 @@ static int dvd_read_physical(struct cdrom_device_info *cdi, dvd_struct *s) return 0; } -static int dvd_read_copyright(struct cdrom_device_info *cdi, dvd_struct *s) +static int dvd_read_copyright(struct cdrom_device_info *cdi, dvd_struct *s, + struct packet_command *cgc) { int ret; u_char buf[8]; - struct packet_command cgc; struct cdrom_device_ops *cdo = cdi->ops; - init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_READ); - cgc.cmd[0] = GPCMD_READ_DVD_STRUCTURE; - cgc.cmd[6] = s->copyright.layer_num; - cgc.cmd[7] = s->type; - cgc.cmd[8] = cgc.buflen >> 8; - cgc.cmd[9] = cgc.buflen & 0xff; + init_cdrom_command(cgc, buf, sizeof(buf), CGC_DATA_READ); + cgc->cmd[0] = GPCMD_READ_DVD_STRUCTURE; + cgc->cmd[6] = s->copyright.layer_num; + cgc->cmd[7] = s->type; + cgc->cmd[8] = cgc->buflen >> 8; + cgc->cmd[9] = cgc->buflen & 0xff; - if ((ret = cdo->generic_packet(cdi, &cgc))) + ret = cdo->generic_packet(cdi, cgc); + if (ret) return ret; s->copyright.cpst = buf[4]; @@ -1785,79 +1787,89 @@ static int dvd_read_copyright(struct cdrom_device_info *cdi, dvd_struct *s) return 0; } -static int dvd_read_disckey(struct cdrom_device_info *cdi, dvd_struct *s) +static int dvd_read_disckey(struct cdrom_device_info *cdi, dvd_struct *s, + struct packet_command *cgc) { int ret, size; u_char *buf; - struct packet_command cgc; struct cdrom_device_ops *cdo = cdi->ops; size = sizeof(s->disckey.value) + 4; - if ((buf = kmalloc(size, GFP_KERNEL)) == NULL) + buf = kmalloc(size, GFP_KERNEL); + if (!buf) return -ENOMEM; - init_cdrom_command(&cgc, buf, size, CGC_DATA_READ); - cgc.cmd[0] = GPCMD_READ_DVD_STRUCTURE; - cgc.cmd[7] = s->type; - cgc.cmd[8] = size >> 8; - cgc.cmd[9] = size & 0xff; - cgc.cmd[10] = s->disckey.agid << 6; + init_cdrom_command(cgc, buf, size, CGC_DATA_READ); + cgc->cmd[0] = GPCMD_READ_DVD_STRUCTURE; + cgc->cmd[7] = s->type; + cgc->cmd[8] = size >> 8; + cgc->cmd[9] = size & 0xff; + cgc->cmd[10] = s->disckey.agid << 6; - if (!(ret = cdo->generic_packet(cdi, &cgc))) + ret = cdo->generic_packet(cdi, cgc); + if (!ret) memcpy(s->disckey.value, &buf[4], sizeof(s->disckey.value)); kfree(buf); return ret; } -static int dvd_read_bca(struct cdrom_device_info *cdi, dvd_struct *s) +static int dvd_read_bca(struct cdrom_device_info *cdi, dvd_struct *s, + struct packet_command *cgc) { - int ret; - u_char buf[4 + 188]; - struct packet_command cgc; + int ret, size = 4 + 188; + u_char *buf; struct cdrom_device_ops *cdo = cdi->ops; - init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_READ); - cgc.cmd[0] = GPCMD_READ_DVD_STRUCTURE; - cgc.cmd[7] = s->type; - cgc.cmd[9] = cgc.buflen & 0xff; + buf = kmalloc(size, GFP_KERNEL); + if (!buf) + return -ENOMEM; - if ((ret = cdo->generic_packet(cdi, &cgc))) - return ret; + init_cdrom_command(cgc, buf, size, CGC_DATA_READ); + cgc->cmd[0] = GPCMD_READ_DVD_STRUCTURE; + cgc->cmd[7] = s->type; + cgc->cmd[9] = cgc->buflen & 0xff; + + ret = cdo->generic_packet(cdi, cgc); + if (ret) + goto out; s->bca.len = buf[0] << 8 | buf[1]; if (s->bca.len < 12 || s->bca.len > 188) { cdinfo(CD_WARNING, "Received invalid BCA length (%d)\n", s->bca.len); - return -EIO; + ret = -EIO; + goto out; } memcpy(s->bca.value, &buf[4], s->bca.len); - - return 0; + ret = 0; +out: + kfree(buf); + return ret; } -static int dvd_read_manufact(struct cdrom_device_info *cdi, dvd_struct *s) +static int dvd_read_manufact(struct cdrom_device_info *cdi, dvd_struct *s, + struct packet_command *cgc) { int ret = 0, size; u_char *buf; - struct packet_command cgc; struct cdrom_device_ops *cdo = cdi->ops; size = sizeof(s->manufact.value) + 4; - if ((buf = kmalloc(size, GFP_KERNEL)) == NULL) + buf = kmalloc(size, GFP_KERNEL); + if (!buf) return -ENOMEM; - init_cdrom_command(&cgc, buf, size, CGC_DATA_READ); - cgc.cmd[0] = GPCMD_READ_DVD_STRUCTURE; - cgc.cmd[7] = s->type; - cgc.cmd[8] = size >> 8; - cgc.cmd[9] = size & 0xff; + init_cdrom_command(cgc, buf, size, CGC_DATA_READ); + cgc->cmd[0] = GPCMD_READ_DVD_STRUCTURE; + cgc->cmd[7] = s->type; + cgc->cmd[8] = size >> 8; + cgc->cmd[9] = size & 0xff; - if ((ret = cdo->generic_packet(cdi, &cgc))) { - kfree(buf); - return ret; - } + ret = cdo->generic_packet(cdi, cgc); + if (ret) + goto out; s->manufact.len = buf[0] << 8 | buf[1]; if (s->manufact.len < 0 || s->manufact.len > 2048) { @@ -1868,27 +1880,29 @@ static int dvd_read_manufact(struct cdrom_device_info *cdi, dvd_struct *s) memcpy(s->manufact.value, &buf[4], s->manufact.len); } +out: kfree(buf); return ret; } -static int dvd_read_struct(struct cdrom_device_info *cdi, dvd_struct *s) +static int dvd_read_struct(struct cdrom_device_info *cdi, dvd_struct *s, + struct packet_command *cgc) { switch (s->type) { case DVD_STRUCT_PHYSICAL: - return dvd_read_physical(cdi, s); + return dvd_read_physical(cdi, s, cgc); case DVD_STRUCT_COPYRIGHT: - return dvd_read_copyright(cdi, s); + return dvd_read_copyright(cdi, s, cgc); case DVD_STRUCT_DISCKEY: - return dvd_read_disckey(cdi, s); + return dvd_read_disckey(cdi, s, cgc); case DVD_STRUCT_BCA: - return dvd_read_bca(cdi, s); + return dvd_read_bca(cdi, s, cgc); case DVD_STRUCT_MANUFACT: - return dvd_read_manufact(cdi, s); + return dvd_read_manufact(cdi, s, cgc); default: cdinfo(CD_WARNING, ": Invalid DVD structure read requested (%d)\n", @@ -3027,7 +3041,8 @@ static noinline int mmc_ioctl_cdrom_pause_resume(struct cdrom_device_info *cdi, } static noinline int mmc_ioctl_dvd_read_struct(struct cdrom_device_info *cdi, - void __user *arg) + void __user *arg, + struct packet_command *cgc) { int ret; dvd_struct *s; @@ -3046,7 +3061,7 @@ static noinline int mmc_ioctl_dvd_read_struct(struct cdrom_device_info *cdi, return -EFAULT; } - ret = dvd_read_struct(cdi, s); + ret = dvd_read_struct(cdi, s, cgc); if (ret) goto out; @@ -3132,7 +3147,7 @@ static int mmc_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, case CDROMRESUME: return mmc_ioctl_cdrom_pause_resume(cdi, &cgc, cmd); case DVD_READ_STRUCT: - return mmc_ioctl_dvd_read_struct(cdi, userptr); + return mmc_ioctl_dvd_read_struct(cdi, userptr, &cgc); case DVD_AUTH: return mmc_ioctl_dvd_auth(cdi, userptr); case CDROM_NEXT_WRITABLE: -- cgit v1.2.3 From 8a3173de4ab4cdacc43675dc5c077f9a5bf17f5f Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Thu, 20 Nov 2008 09:46:09 +0100 Subject: cciss: switch to using hlist for command list management This both cleans up the code and also helps detect the spurious case of a command attempted being removed from a queue it doesn't belong to. Acked-by: Mike Miller Signed-off-by: Jens Axboe --- drivers/block/cciss.c | 72 ++++++++++++++++++++--------------------------- drivers/block/cciss.h | 4 +-- drivers/block/cciss_cmd.h | 3 +- 3 files changed, 33 insertions(+), 46 deletions(-) (limited to 'drivers') diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index 9f7c543cc04b..4e07b5ca847c 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -215,31 +215,17 @@ static struct block_device_operations cciss_fops = { /* * Enqueuing and dequeuing functions for cmdlists. */ -static inline void addQ(CommandList_struct **Qptr, CommandList_struct *c) +static inline void addQ(struct hlist_head *list, CommandList_struct *c) { - if (*Qptr == NULL) { - *Qptr = c; - c->next = c->prev = c; - } else { - c->prev = (*Qptr)->prev; - c->next = (*Qptr); - (*Qptr)->prev->next = c; - (*Qptr)->prev = c; - } + hlist_add_head(&c->list, list); } -static inline CommandList_struct *removeQ(CommandList_struct **Qptr, - CommandList_struct *c) +static inline void removeQ(CommandList_struct *c) { - if (c && c->next != c) { - if (*Qptr == c) - *Qptr = c->next; - c->prev->next = c->next; - c->next->prev = c->prev; - } else { - *Qptr = NULL; - } - return c; + if (WARN_ON(hlist_unhashed(&c->list))) + return; + + hlist_del_init(&c->list); } #include "cciss_scsi.c" /* For SCSI tape support */ @@ -506,6 +492,7 @@ static CommandList_struct *cmd_alloc(ctlr_info_t *h, int get_from_pool) c->cmdindex = i; } + INIT_HLIST_NODE(&c->list); c->busaddr = (__u32) cmd_dma_handle; temp64.val = (__u64) err_dma_handle; c->ErrDesc.Addr.lower = temp64.val32.lower; @@ -2548,7 +2535,8 @@ static void start_io(ctlr_info_t *h) { CommandList_struct *c; - while ((c = h->reqQ) != NULL) { + while (!hlist_empty(&h->reqQ)) { + c = hlist_entry(h->reqQ.first, CommandList_struct, list); /* can't do anything if fifo is full */ if ((h->access.fifo_full(h))) { printk(KERN_WARNING "cciss: fifo full\n"); @@ -2556,14 +2544,14 @@ static void start_io(ctlr_info_t *h) } /* Get the first entry from the Request Q */ - removeQ(&(h->reqQ), c); + removeQ(c); h->Qdepth--; /* Tell the controller execute command */ h->access.submit_command(h, c); /* Put job onto the completed Q */ - addQ(&(h->cmpQ), c); + addQ(&h->cmpQ, c); } } @@ -2576,7 +2564,7 @@ static inline void resend_cciss_cmd(ctlr_info_t *h, CommandList_struct *c) memset(c->err_info, 0, sizeof(ErrorInfo_struct)); /* add it to software queue and then send it to the controller */ - addQ(&(h->reqQ), c); + addQ(&h->reqQ, c); h->Qdepth++; if (h->Qdepth > h->maxQsinceinit) h->maxQsinceinit = h->Qdepth; @@ -2897,7 +2885,7 @@ static void do_cciss_request(struct request_queue *q) spin_lock_irq(q->queue_lock); - addQ(&(h->reqQ), c); + addQ(&h->reqQ, c); h->Qdepth++; if (h->Qdepth > h->maxQsinceinit) h->maxQsinceinit = h->Qdepth; @@ -2985,16 +2973,12 @@ static irqreturn_t do_cciss_intr(int irq, void *dev_id) a = c->busaddr; } else { + struct hlist_node *tmp; + a &= ~3; - if ((c = h->cmpQ) == NULL) { - printk(KERN_WARNING - "cciss: Completion of %08x ignored\n", - a1); - continue; - } - while (c->busaddr != a) { - c = c->next; - if (c == h->cmpQ) + c = NULL; + hlist_for_each_entry(c, tmp, &h->cmpQ, list) { + if (c->busaddr == a) break; } } @@ -3002,8 +2986,8 @@ static irqreturn_t do_cciss_intr(int irq, void *dev_id) * If we've found the command, take it off the * completion Q and free it */ - if (c->busaddr == a) { - removeQ(&h->cmpQ, c); + if (c && c->busaddr == a) { + removeQ(c); if (c->cmd_type == CMD_RWREQ) { complete_command(h, c, 0); } else if (c->cmd_type == CMD_IOCTL_PEND) { @@ -3423,6 +3407,8 @@ static int __devinit cciss_init_one(struct pci_dev *pdev, return -1; hba[i]->busy_initializing = 1; + INIT_HLIST_HEAD(&hba[i]->cmpQ); + INIT_HLIST_HEAD(&hba[i]->reqQ); if (cciss_pci_init(hba[i], pdev) != 0) goto clean1; @@ -3730,15 +3716,17 @@ static void fail_all_cmds(unsigned long ctlr) pci_disable_device(h->pdev); /* Make sure it is really dead. */ /* move everything off the request queue onto the completed queue */ - while ((c = h->reqQ) != NULL) { - removeQ(&(h->reqQ), c); + while (!hlist_empty(&h->reqQ)) { + c = hlist_entry(h->reqQ.first, CommandList_struct, list); + removeQ(c); h->Qdepth--; - addQ(&(h->cmpQ), c); + addQ(&h->cmpQ, c); } /* Now, fail everything on the completed queue with a HW error */ - while ((c = h->cmpQ) != NULL) { - removeQ(&h->cmpQ, c); + while (!hlist_empty(&h->cmpQ)) { + c = hlist_entry(h->cmpQ.first, CommandList_struct, list); + removeQ(c); c->err_info->CommandStatus = CMD_HARDWARE_ERR; if (c->cmd_type == CMD_RWREQ) { complete_command(h, c, 0); diff --git a/drivers/block/cciss.h b/drivers/block/cciss.h index 24a7efa993ab..15e2b84734e3 100644 --- a/drivers/block/cciss.h +++ b/drivers/block/cciss.h @@ -89,8 +89,8 @@ struct ctlr_info struct access_method access; /* queue and queue Info */ - CommandList_struct *reqQ; - CommandList_struct *cmpQ; + struct hlist_head reqQ; + struct hlist_head cmpQ; unsigned int Qdepth; unsigned int maxQsinceinit; unsigned int maxSG; diff --git a/drivers/block/cciss_cmd.h b/drivers/block/cciss_cmd.h index 43bf5593b59b..24e22dea1a99 100644 --- a/drivers/block/cciss_cmd.h +++ b/drivers/block/cciss_cmd.h @@ -265,8 +265,7 @@ typedef struct _CommandList_struct { int ctlr; int cmd_type; long cmdindex; - struct _CommandList_struct *prev; - struct _CommandList_struct *next; + struct hlist_node list; struct request * rq; struct completion *waiting; int retry_count; -- cgit v1.2.3 From bb799ca0202a360fa74d5f17039b9100caebdde7 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Wed, 10 Dec 2008 15:35:05 +0100 Subject: bio: allow individual slabs in the bio_set Instead of having a global bio slab cache, add a reference to one in each bio_set that is created. This allows for personalized slabs in each bio_set, so that they can have bios of different sizes. This means we can personalize the bios we return. File systems may want to embed the bio inside another structure, to avoid allocation more items (and stuffing them in ->bi_private) after the get a bio. Or we may want to embed a number of bio_vecs directly at the end of a bio, to avoid doing two allocations to return a bio. This is now possible. Signed-off-by: Jens Axboe --- drivers/md/dm-crypt.c | 2 +- drivers/md/dm-io.c | 2 +- drivers/md/dm.c | 2 +- fs/bio-integrity.c | 2 +- fs/bio.c | 191 ++++++++++++++++++++++++++++++++++++++++++-------- include/linux/bio.h | 6 +- 6 files changed, 170 insertions(+), 35 deletions(-) (limited to 'drivers') diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c index ce26c84af064..3326750ec02c 100644 --- a/drivers/md/dm-crypt.c +++ b/drivers/md/dm-crypt.c @@ -1060,7 +1060,7 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv) goto bad_page_pool; } - cc->bs = bioset_create(MIN_IOS, MIN_IOS); + cc->bs = bioset_create(MIN_IOS, 0); if (!cc->bs) { ti->error = "Cannot allocate crypt bioset"; goto bad_bs; diff --git a/drivers/md/dm-io.c b/drivers/md/dm-io.c index 2fd6d4450637..a34338567a2a 100644 --- a/drivers/md/dm-io.c +++ b/drivers/md/dm-io.c @@ -56,7 +56,7 @@ struct dm_io_client *dm_io_client_create(unsigned num_pages) if (!client->pool) goto bad; - client->bios = bioset_create(16, 16); + client->bios = bioset_create(16, 0); if (!client->bios) goto bad; diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 343094c3feeb..421c9f02d8ca 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -1093,7 +1093,7 @@ static struct mapped_device *alloc_dev(int minor) if (!md->tio_pool) goto bad_tio_pool; - md->bs = bioset_create(16, 16); + md->bs = bioset_create(16, 0); if (!md->bs) goto bad_no_bioset; diff --git a/fs/bio-integrity.c b/fs/bio-integrity.c index 19caf7c962ac..77ebc3c263d6 100644 --- a/fs/bio-integrity.c +++ b/fs/bio-integrity.c @@ -111,7 +111,7 @@ void bio_integrity_free(struct bio *bio, struct bio_set *bs) && bip->bip_buf != NULL) kfree(bip->bip_buf); - mempool_free(bip->bip_vec, bs->bvec_pools[bip->bip_pool]); + bvec_free_bs(bs, bip->bip_vec, bip->bip_pool); mempool_free(bip, bs->bio_integrity_pool); bio->bi_integrity = NULL; diff --git a/fs/bio.c b/fs/bio.c index 1ab8986b0411..0146f80789e9 100644 --- a/fs/bio.c +++ b/fs/bio.c @@ -31,8 +31,6 @@ DEFINE_TRACE(block_split); -static struct kmem_cache *bio_slab __read_mostly; - static mempool_t *bio_split_pool __read_mostly; /* @@ -40,9 +38,8 @@ static mempool_t *bio_split_pool __read_mostly; * break badly! cannot be bigger than what you can fit into an * unsigned short */ - #define BV(x) { .nr_vecs = x, .name = "biovec-"__stringify(x) } -static struct biovec_slab bvec_slabs[BIOVEC_NR_POOLS] __read_mostly = { +struct biovec_slab bvec_slabs[BIOVEC_NR_POOLS] __read_mostly = { BV(1), BV(4), BV(16), BV(64), BV(128), BV(BIO_MAX_PAGES), }; #undef BV @@ -53,11 +50,119 @@ static struct biovec_slab bvec_slabs[BIOVEC_NR_POOLS] __read_mostly = { */ struct bio_set *fs_bio_set; +/* + * Our slab pool management + */ +struct bio_slab { + struct kmem_cache *slab; + unsigned int slab_ref; + unsigned int slab_size; + char name[8]; +}; +static DEFINE_MUTEX(bio_slab_lock); +static struct bio_slab *bio_slabs; +static unsigned int bio_slab_nr, bio_slab_max; + +static struct kmem_cache *bio_find_or_create_slab(unsigned int extra_size) +{ + unsigned int sz = sizeof(struct bio) + extra_size; + struct kmem_cache *slab = NULL; + struct bio_slab *bslab; + unsigned int i, entry = -1; + + mutex_lock(&bio_slab_lock); + + i = 0; + while (i < bio_slab_nr) { + struct bio_slab *bslab = &bio_slabs[i]; + + if (!bslab->slab && entry == -1) + entry = i; + else if (bslab->slab_size == sz) { + slab = bslab->slab; + bslab->slab_ref++; + break; + } + i++; + } + + if (slab) + goto out_unlock; + + if (bio_slab_nr == bio_slab_max && entry == -1) { + bio_slab_max <<= 1; + bio_slabs = krealloc(bio_slabs, + bio_slab_max * sizeof(struct bio_slab), + GFP_KERNEL); + if (!bio_slabs) + goto out_unlock; + } + if (entry == -1) + entry = bio_slab_nr++; + + bslab = &bio_slabs[entry]; + + snprintf(bslab->name, sizeof(bslab->name), "bio-%d", entry); + slab = kmem_cache_create(bslab->name, sz, 0, SLAB_HWCACHE_ALIGN, NULL); + if (!slab) + goto out_unlock; + + printk("bio: create slab <%s> at %d\n", bslab->name, entry); + bslab->slab = slab; + bslab->slab_ref = 1; + bslab->slab_size = sz; +out_unlock: + mutex_unlock(&bio_slab_lock); + return slab; +} + +static void bio_put_slab(struct bio_set *bs) +{ + struct bio_slab *bslab = NULL; + unsigned int i; + + mutex_lock(&bio_slab_lock); + + for (i = 0; i < bio_slab_nr; i++) { + if (bs->bio_slab == bio_slabs[i].slab) { + bslab = &bio_slabs[i]; + break; + } + } + + if (WARN(!bslab, KERN_ERR "bio: unable to find slab!\n")) + goto out; + + WARN_ON(!bslab->slab_ref); + + if (--bslab->slab_ref) + goto out; + + kmem_cache_destroy(bslab->slab); + bslab->slab = NULL; + +out: + mutex_unlock(&bio_slab_lock); +} + unsigned int bvec_nr_vecs(unsigned short idx) { return bvec_slabs[idx].nr_vecs; } +void bvec_free_bs(struct bio_set *bs, struct bio_vec *bv, unsigned int idx) +{ + BIO_BUG_ON(idx >= BIOVEC_NR_POOLS); + + if (idx == BIOVEC_MAX_IDX) + mempool_free(bv, bs->bvec_pool); + else { + struct biovec_slab *bvs = bvec_slabs + idx; + + kmem_cache_free(bvs->slab, bv); + } +} + struct bio_vec *bvec_alloc_bs(gfp_t gfp_mask, int nr, unsigned long *idx, struct bio_set *bs) { @@ -134,24 +239,22 @@ fallback: void bio_free(struct bio *bio, struct bio_set *bs) { - if (bio->bi_io_vec) { - const int pool_idx = BIO_POOL_IDX(bio); + void *p; - BIO_BUG_ON(pool_idx >= BIOVEC_NR_POOLS); - - if (pool_idx == BIOVEC_MAX_IDX) - mempool_free(bio->bi_io_vec, bs->bvec_pool); - else { - struct biovec_slab *bvs = bvec_slabs + pool_idx; - - kmem_cache_free(bvs->slab, bio->bi_io_vec); - } - } + if (bio->bi_io_vec) + bvec_free_bs(bs, bio->bi_io_vec, BIO_POOL_IDX(bio)); if (bio_integrity(bio)) bio_integrity_free(bio, bs); - mempool_free(bio, bs->bio_pool); + /* + * If we have front padding, adjust the bio pointer before freeing + */ + p = bio; + if (bs->front_pad) + p -= bs->front_pad; + + mempool_free(p, bs->bio_pool); } /* @@ -188,16 +291,20 @@ void bio_init(struct bio *bio) * for a &struct bio to become free. If a %NULL @bs is passed in, we will * fall back to just using @kmalloc to allocate the required memory. * - * allocate bio and iovecs from the memory pools specified by the - * bio_set structure, or @kmalloc if none given. + * Note that the caller must set ->bi_destructor on succesful return + * of a bio, to do the appropriate freeing of the bio once the reference + * count drops to zero. **/ struct bio *bio_alloc_bioset(gfp_t gfp_mask, int nr_iovecs, struct bio_set *bs) { - struct bio *bio; + struct bio *bio = NULL; + + if (bs) { + void *p = mempool_alloc(bs->bio_pool, gfp_mask); - if (bs) - bio = mempool_alloc(bs->bio_pool, gfp_mask); - else + if (p) + bio = p + bs->front_pad; + } else bio = kmalloc(sizeof(*bio), gfp_mask); if (likely(bio)) { @@ -1398,11 +1505,25 @@ void bioset_free(struct bio_set *bs) bioset_integrity_free(bs); biovec_free_pools(bs); + bio_put_slab(bs); kfree(bs); } -struct bio_set *bioset_create(int bio_pool_size, int bvec_pool_size) +/** + * bioset_create - Create a bio_set + * @pool_size: Number of bio and bio_vecs to cache in the mempool + * @front_pad: Number of bytes to allocate in front of the returned bio + * + * Description: + * Set up a bio_set to be used with @bio_alloc_bioset. Allows the caller + * to ask for a number of bytes to be allocated in front of the bio. + * Front pad allocation is useful for embedding the bio inside + * another structure, to avoid allocating extra data to go with the bio. + * Note that the bio must be embedded at the END of that structure always, + * or things will break badly. + */ +struct bio_set *bioset_create(unsigned int pool_size, unsigned int front_pad) { struct bio_set *bs; @@ -1410,16 +1531,22 @@ struct bio_set *bioset_create(int bio_pool_size, int bvec_pool_size) if (!bs) return NULL; - bs->bio_slab = bio_slab; + bs->front_pad = front_pad; - bs->bio_pool = mempool_create_slab_pool(bio_pool_size, bs->bio_slab); + bs->bio_slab = bio_find_or_create_slab(front_pad); + if (!bs->bio_slab) { + kfree(bs); + return NULL; + } + + bs->bio_pool = mempool_create_slab_pool(pool_size, bs->bio_slab); if (!bs->bio_pool) goto bad; - if (bioset_integrity_create(bs, bio_pool_size)) + if (bioset_integrity_create(bs, pool_size)) goto bad; - if (!biovec_create_pools(bs, bvec_pool_size)) + if (!biovec_create_pools(bs, pool_size)) return bs; bad: @@ -1443,12 +1570,16 @@ static void __init biovec_init_slabs(void) static int __init init_bio(void) { - bio_slab = KMEM_CACHE(bio, SLAB_HWCACHE_ALIGN|SLAB_PANIC); + bio_slab_max = 2; + bio_slab_nr = 0; + bio_slabs = kzalloc(bio_slab_max * sizeof(struct bio_slab), GFP_KERNEL); + if (!bio_slabs) + panic("bio: can't allocate bios\n"); bio_integrity_init_slab(); biovec_init_slabs(); - fs_bio_set = bioset_create(BIO_POOL_SIZE, 2); + fs_bio_set = bioset_create(BIO_POOL_SIZE, 0); if (!fs_bio_set) panic("bio: can't allocate bios\n"); diff --git a/include/linux/bio.h b/include/linux/bio.h index 9340098d75dc..4b80d3537f97 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -334,7 +334,7 @@ struct bio_pair { extern struct bio_pair *bio_split(struct bio *bi, int first_sectors); extern void bio_pair_release(struct bio_pair *dbio); -extern struct bio_set *bioset_create(int, int); +extern struct bio_set *bioset_create(unsigned int, unsigned int); extern void bioset_free(struct bio_set *); extern struct bio *bio_alloc(gfp_t, int); @@ -379,6 +379,7 @@ extern struct bio *bio_copy_user_iov(struct request_queue *, extern int bio_uncopy_user(struct bio *); void zero_fill_bio(struct bio *bio); extern struct bio_vec *bvec_alloc_bs(gfp_t, int, unsigned long *, struct bio_set *); +extern void bvec_free_bs(struct bio_set *, struct bio_vec *, unsigned int); extern unsigned int bvec_nr_vecs(unsigned short idx); /* @@ -401,6 +402,8 @@ static inline void bio_set_completion_cpu(struct bio *bio, unsigned int cpu) struct bio_set { struct kmem_cache *bio_slab; + unsigned int front_pad; + mempool_t *bio_pool; #if defined(CONFIG_BLK_DEV_INTEGRITY) mempool_t *bio_integrity_pool; @@ -415,6 +418,7 @@ struct biovec_slab { }; extern struct bio_set *fs_bio_set; +extern struct biovec_slab bvec_slabs[BIOVEC_NR_POOLS] __read_mostly; /* * a small number of entries is fine, not going to be performance critical. -- cgit v1.2.3 From b374d18a4bfce705e4a99ae9f501b53e86ecb283 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Fri, 31 Oct 2008 10:05:07 +0100 Subject: block: get rid of elevator_t typedef Just use struct elevator_queue everywhere instead. Signed-off-by: Jens Axboe --- block/as-iosched.c | 8 +++---- block/cfq-iosched.c | 6 +++--- block/deadline-iosched.c | 6 +++--- block/elevator.c | 55 +++++++++++++++++++++++++----------------------- block/noop-iosched.c | 2 +- drivers/block/nbd.c | 2 +- include/linux/blkdev.h | 3 +-- include/linux/elevator.h | 8 +++---- 8 files changed, 46 insertions(+), 44 deletions(-) (limited to 'drivers') diff --git a/block/as-iosched.c b/block/as-iosched.c index 802b5d0d8536..631f6f44460a 100644 --- a/block/as-iosched.c +++ b/block/as-iosched.c @@ -1339,7 +1339,7 @@ static int as_may_queue(struct request_queue *q, int rw) return ret; } -static void as_exit_queue(elevator_t *e) +static void as_exit_queue(struct elevator_queue *e) { struct as_data *ad = e->elevator_data; @@ -1409,7 +1409,7 @@ as_var_store(unsigned long *var, const char *page, size_t count) return count; } -static ssize_t est_time_show(elevator_t *e, char *page) +static ssize_t est_time_show(struct elevator_queue *e, char *page) { struct as_data *ad = e->elevator_data; int pos = 0; @@ -1427,7 +1427,7 @@ static ssize_t est_time_show(elevator_t *e, char *page) } #define SHOW_FUNCTION(__FUNC, __VAR) \ -static ssize_t __FUNC(elevator_t *e, char *page) \ +static ssize_t __FUNC(struct elevator_queue *e, char *page) \ { \ struct as_data *ad = e->elevator_data; \ return as_var_show(jiffies_to_msecs((__VAR)), (page)); \ @@ -1440,7 +1440,7 @@ SHOW_FUNCTION(as_write_batch_expire_show, ad->batch_expire[REQ_ASYNC]); #undef SHOW_FUNCTION #define STORE_FUNCTION(__FUNC, __PTR, MIN, MAX) \ -static ssize_t __FUNC(elevator_t *e, const char *page, size_t count) \ +static ssize_t __FUNC(struct elevator_queue *e, const char *page, size_t count) \ { \ struct as_data *ad = e->elevator_data; \ int ret = as_var_store(__PTR, (page), count); \ diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index a2bfec7d6b36..adaf93a9d19d 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -2178,7 +2178,7 @@ static void cfq_put_async_queues(struct cfq_data *cfqd) cfq_put_queue(cfqd->async_idle_cfqq); } -static void cfq_exit_queue(elevator_t *e) +static void cfq_exit_queue(struct elevator_queue *e) { struct cfq_data *cfqd = e->elevator_data; struct request_queue *q = cfqd->queue; @@ -2288,7 +2288,7 @@ cfq_var_store(unsigned int *var, const char *page, size_t count) } #define SHOW_FUNCTION(__FUNC, __VAR, __CONV) \ -static ssize_t __FUNC(elevator_t *e, char *page) \ +static ssize_t __FUNC(struct elevator_queue *e, char *page) \ { \ struct cfq_data *cfqd = e->elevator_data; \ unsigned int __data = __VAR; \ @@ -2308,7 +2308,7 @@ SHOW_FUNCTION(cfq_slice_async_rq_show, cfqd->cfq_slice_async_rq, 0); #undef SHOW_FUNCTION #define STORE_FUNCTION(__FUNC, __PTR, MIN, MAX, __CONV) \ -static ssize_t __FUNC(elevator_t *e, const char *page, size_t count) \ +static ssize_t __FUNC(struct elevator_queue *e, const char *page, size_t count) \ { \ struct cfq_data *cfqd = e->elevator_data; \ unsigned int __data; \ diff --git a/block/deadline-iosched.c b/block/deadline-iosched.c index fd311179f44c..c4d991d4adef 100644 --- a/block/deadline-iosched.c +++ b/block/deadline-iosched.c @@ -334,7 +334,7 @@ static int deadline_queue_empty(struct request_queue *q) && list_empty(&dd->fifo_list[READ]); } -static void deadline_exit_queue(elevator_t *e) +static void deadline_exit_queue(struct elevator_queue *e) { struct deadline_data *dd = e->elevator_data; @@ -387,7 +387,7 @@ deadline_var_store(int *var, const char *page, size_t count) } #define SHOW_FUNCTION(__FUNC, __VAR, __CONV) \ -static ssize_t __FUNC(elevator_t *e, char *page) \ +static ssize_t __FUNC(struct elevator_queue *e, char *page) \ { \ struct deadline_data *dd = e->elevator_data; \ int __data = __VAR; \ @@ -403,7 +403,7 @@ SHOW_FUNCTION(deadline_fifo_batch_show, dd->fifo_batch, 0); #undef SHOW_FUNCTION #define STORE_FUNCTION(__FUNC, __PTR, MIN, MAX, __CONV) \ -static ssize_t __FUNC(elevator_t *e, const char *page, size_t count) \ +static ssize_t __FUNC(struct elevator_queue *e, const char *page, size_t count) \ { \ struct deadline_data *dd = e->elevator_data; \ int __data; \ diff --git a/block/elevator.c b/block/elevator.c index ff60177a3bab..98259eda0ef6 100644 --- a/block/elevator.c +++ b/block/elevator.c @@ -65,7 +65,7 @@ DEFINE_TRACE(block_rq_issue); static int elv_iosched_allow_merge(struct request *rq, struct bio *bio) { struct request_queue *q = rq->q; - elevator_t *e = q->elevator; + struct elevator_queue *e = q->elevator; if (e->ops->elevator_allow_merge_fn) return e->ops->elevator_allow_merge_fn(q, rq, bio); @@ -208,13 +208,13 @@ __setup("elevator=", elevator_setup); static struct kobj_type elv_ktype; -static elevator_t *elevator_alloc(struct request_queue *q, +static struct elevator_queue *elevator_alloc(struct request_queue *q, struct elevator_type *e) { - elevator_t *eq; + struct elevator_queue *eq; int i; - eq = kmalloc_node(sizeof(elevator_t), GFP_KERNEL | __GFP_ZERO, q->node); + eq = kmalloc_node(sizeof(*eq), GFP_KERNEL | __GFP_ZERO, q->node); if (unlikely(!eq)) goto err; @@ -240,8 +240,9 @@ err: static void elevator_release(struct kobject *kobj) { - elevator_t *e = container_of(kobj, elevator_t, kobj); + struct elevator_queue *e; + e = container_of(kobj, struct elevator_queue, kobj); elevator_put(e->elevator_type); kfree(e->hash); kfree(e); @@ -297,7 +298,7 @@ int elevator_init(struct request_queue *q, char *name) } EXPORT_SYMBOL(elevator_init); -void elevator_exit(elevator_t *e) +void elevator_exit(struct elevator_queue *e) { mutex_lock(&e->sysfs_lock); if (e->ops->elevator_exit_fn) @@ -311,7 +312,7 @@ EXPORT_SYMBOL(elevator_exit); static void elv_activate_rq(struct request_queue *q, struct request *rq) { - elevator_t *e = q->elevator; + struct elevator_queue *e = q->elevator; if (e->ops->elevator_activate_req_fn) e->ops->elevator_activate_req_fn(q, rq); @@ -319,7 +320,7 @@ static void elv_activate_rq(struct request_queue *q, struct request *rq) static void elv_deactivate_rq(struct request_queue *q, struct request *rq) { - elevator_t *e = q->elevator; + struct elevator_queue *e = q->elevator; if (e->ops->elevator_deactivate_req_fn) e->ops->elevator_deactivate_req_fn(q, rq); @@ -338,7 +339,7 @@ static void elv_rqhash_del(struct request_queue *q, struct request *rq) static void elv_rqhash_add(struct request_queue *q, struct request *rq) { - elevator_t *e = q->elevator; + struct elevator_queue *e = q->elevator; BUG_ON(ELV_ON_HASH(rq)); hlist_add_head(&rq->hash, &e->hash[ELV_HASH_FN(rq_hash_key(rq))]); @@ -352,7 +353,7 @@ static void elv_rqhash_reposition(struct request_queue *q, struct request *rq) static struct request *elv_rqhash_find(struct request_queue *q, sector_t offset) { - elevator_t *e = q->elevator; + struct elevator_queue *e = q->elevator; struct hlist_head *hash_list = &e->hash[ELV_HASH_FN(offset)]; struct hlist_node *entry, *next; struct request *rq; @@ -494,7 +495,7 @@ EXPORT_SYMBOL(elv_dispatch_add_tail); int elv_merge(struct request_queue *q, struct request **req, struct bio *bio) { - elevator_t *e = q->elevator; + struct elevator_queue *e = q->elevator; struct request *__rq; int ret; @@ -529,7 +530,7 @@ int elv_merge(struct request_queue *q, struct request **req, struct bio *bio) void elv_merged_request(struct request_queue *q, struct request *rq, int type) { - elevator_t *e = q->elevator; + struct elevator_queue *e = q->elevator; if (e->ops->elevator_merged_fn) e->ops->elevator_merged_fn(q, rq, type); @@ -543,7 +544,7 @@ void elv_merged_request(struct request_queue *q, struct request *rq, int type) void elv_merge_requests(struct request_queue *q, struct request *rq, struct request *next) { - elevator_t *e = q->elevator; + struct elevator_queue *e = q->elevator; if (e->ops->elevator_merge_req_fn) e->ops->elevator_merge_req_fn(q, rq, next); @@ -846,7 +847,7 @@ void elv_dequeue_request(struct request_queue *q, struct request *rq) int elv_queue_empty(struct request_queue *q) { - elevator_t *e = q->elevator; + struct elevator_queue *e = q->elevator; if (!list_empty(&q->queue_head)) return 0; @@ -860,7 +861,7 @@ EXPORT_SYMBOL(elv_queue_empty); struct request *elv_latter_request(struct request_queue *q, struct request *rq) { - elevator_t *e = q->elevator; + struct elevator_queue *e = q->elevator; if (e->ops->elevator_latter_req_fn) return e->ops->elevator_latter_req_fn(q, rq); @@ -869,7 +870,7 @@ struct request *elv_latter_request(struct request_queue *q, struct request *rq) struct request *elv_former_request(struct request_queue *q, struct request *rq) { - elevator_t *e = q->elevator; + struct elevator_queue *e = q->elevator; if (e->ops->elevator_former_req_fn) return e->ops->elevator_former_req_fn(q, rq); @@ -878,7 +879,7 @@ struct request *elv_former_request(struct request_queue *q, struct request *rq) int elv_set_request(struct request_queue *q, struct request *rq, gfp_t gfp_mask) { - elevator_t *e = q->elevator; + struct elevator_queue *e = q->elevator; if (e->ops->elevator_set_req_fn) return e->ops->elevator_set_req_fn(q, rq, gfp_mask); @@ -889,7 +890,7 @@ int elv_set_request(struct request_queue *q, struct request *rq, gfp_t gfp_mask) void elv_put_request(struct request_queue *q, struct request *rq) { - elevator_t *e = q->elevator; + struct elevator_queue *e = q->elevator; if (e->ops->elevator_put_req_fn) e->ops->elevator_put_req_fn(rq); @@ -897,7 +898,7 @@ void elv_put_request(struct request_queue *q, struct request *rq) int elv_may_queue(struct request_queue *q, int rw) { - elevator_t *e = q->elevator; + struct elevator_queue *e = q->elevator; if (e->ops->elevator_may_queue_fn) return e->ops->elevator_may_queue_fn(q, rw); @@ -920,7 +921,7 @@ EXPORT_SYMBOL(elv_abort_queue); void elv_completed_request(struct request_queue *q, struct request *rq) { - elevator_t *e = q->elevator; + struct elevator_queue *e = q->elevator; /* * request is released from the driver, io must be done @@ -955,13 +956,14 @@ void elv_completed_request(struct request_queue *q, struct request *rq) static ssize_t elv_attr_show(struct kobject *kobj, struct attribute *attr, char *page) { - elevator_t *e = container_of(kobj, elevator_t, kobj); struct elv_fs_entry *entry = to_elv(attr); + struct elevator_queue *e; ssize_t error; if (!entry->show) return -EIO; + e = container_of(kobj, struct elevator_queue, kobj); mutex_lock(&e->sysfs_lock); error = e->ops ? entry->show(e, page) : -ENOENT; mutex_unlock(&e->sysfs_lock); @@ -972,13 +974,14 @@ static ssize_t elv_attr_store(struct kobject *kobj, struct attribute *attr, const char *page, size_t length) { - elevator_t *e = container_of(kobj, elevator_t, kobj); struct elv_fs_entry *entry = to_elv(attr); + struct elevator_queue *e; ssize_t error; if (!entry->store) return -EIO; + e = container_of(kobj, struct elevator_queue, kobj); mutex_lock(&e->sysfs_lock); error = e->ops ? entry->store(e, page, length) : -ENOENT; mutex_unlock(&e->sysfs_lock); @@ -997,7 +1000,7 @@ static struct kobj_type elv_ktype = { int elv_register_queue(struct request_queue *q) { - elevator_t *e = q->elevator; + struct elevator_queue *e = q->elevator; int error; error = kobject_add(&e->kobj, &q->kobj, "%s", "iosched"); @@ -1015,7 +1018,7 @@ int elv_register_queue(struct request_queue *q) return error; } -static void __elv_unregister_queue(elevator_t *e) +static void __elv_unregister_queue(struct elevator_queue *e) { kobject_uevent(&e->kobj, KOBJ_REMOVE); kobject_del(&e->kobj); @@ -1078,7 +1081,7 @@ EXPORT_SYMBOL_GPL(elv_unregister); */ static int elevator_switch(struct request_queue *q, struct elevator_type *new_e) { - elevator_t *old_elevator, *e; + struct elevator_queue *old_elevator, *e; void *data; /* @@ -1184,7 +1187,7 @@ ssize_t elv_iosched_store(struct request_queue *q, const char *name, ssize_t elv_iosched_show(struct request_queue *q, char *name) { - elevator_t *e = q->elevator; + struct elevator_queue *e = q->elevator; struct elevator_type *elv = e->elevator_type; struct elevator_type *__e; int len = 0; diff --git a/block/noop-iosched.c b/block/noop-iosched.c index c23e02969650..3a0d369d08c7 100644 --- a/block/noop-iosched.c +++ b/block/noop-iosched.c @@ -76,7 +76,7 @@ static void *noop_init_queue(struct request_queue *q) return nd; } -static void noop_exit_queue(elevator_t *e) +static void noop_exit_queue(struct elevator_queue *e) { struct noop_data *nd = e->elevator_data; diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index d3a91cacee8c..0766ce6187a9 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -722,7 +722,7 @@ static int __init nbd_init(void) for (i = 0; i < nbds_max; i++) { struct gendisk *disk = alloc_disk(1 << part_shift); - elevator_t *old_e; + struct elevator_queue *old_e; if (!disk) goto out; nbd_dev[i].disk = disk; diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 41bbadfd17f6..7035cec583b6 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -26,7 +26,6 @@ struct scsi_ioctl_command; struct request_queue; struct elevator_queue; -typedef struct elevator_queue elevator_t; struct request_pm_state; struct blk_trace; struct request; @@ -313,7 +312,7 @@ struct request_queue */ struct list_head queue_head; struct request *last_merge; - elevator_t *elevator; + struct elevator_queue *elevator; /* * the queue request freelist, one for reads and one for writes diff --git a/include/linux/elevator.h b/include/linux/elevator.h index 92f6f634e3e6..7a204256b155 100644 --- a/include/linux/elevator.h +++ b/include/linux/elevator.h @@ -28,7 +28,7 @@ typedef void (elevator_activate_req_fn) (struct request_queue *, struct request typedef void (elevator_deactivate_req_fn) (struct request_queue *, struct request *); typedef void *(elevator_init_fn) (struct request_queue *); -typedef void (elevator_exit_fn) (elevator_t *); +typedef void (elevator_exit_fn) (struct elevator_queue *); struct elevator_ops { @@ -62,8 +62,8 @@ struct elevator_ops struct elv_fs_entry { struct attribute attr; - ssize_t (*show)(elevator_t *, char *); - ssize_t (*store)(elevator_t *, const char *, size_t); + ssize_t (*show)(struct elevator_queue *, char *); + ssize_t (*store)(struct elevator_queue *, const char *, size_t); }; /* @@ -130,7 +130,7 @@ extern ssize_t elv_iosched_show(struct request_queue *, char *); extern ssize_t elv_iosched_store(struct request_queue *, const char *, size_t); extern int elevator_init(struct request_queue *, char *); -extern void elevator_exit(elevator_t *); +extern void elevator_exit(struct elevator_queue *); extern int elv_rq_merge_ok(struct request *, struct bio *); /* -- cgit v1.2.3 From 31dcfab0ae6eb0340d456931df4d478e17efc65d Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Fri, 31 Oct 2008 10:06:37 +0100 Subject: nbd: tell the block layer that it is not a rotational device Then we can get rid of that manual elevator type fiddling. Signed-off-by: Jens Axboe --- drivers/block/nbd.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index 0766ce6187a9..7bcc1d8bc967 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -722,7 +722,6 @@ static int __init nbd_init(void) for (i = 0; i < nbds_max; i++) { struct gendisk *disk = alloc_disk(1 << part_shift); - struct elevator_queue *old_e; if (!disk) goto out; nbd_dev[i].disk = disk; @@ -736,11 +735,10 @@ static int __init nbd_init(void) put_disk(disk); goto out; } - old_e = disk->queue->elevator; - if (elevator_init(disk->queue, "deadline") == 0 || - elevator_init(disk->queue, "noop") == 0) { - elevator_exit(old_e); - } + /* + * Tell the block layer that we are not a rotational device + */ + queue_flag_set_unlocked(QUEUE_FLAG_NONROT, disk->queue); } if (register_blkdev(NBD_MAJOR, "nbd")) { -- cgit v1.2.3 From 14f2793958bd7878db2dc0309ed4525d6b7f1b72 Mon Sep 17 00:00:00 2001 From: Milan Broz Date: Fri, 12 Dec 2008 14:48:27 +0100 Subject: loop: Flush possible running bios when loop device is released. When there are still queued bios and reference count drops to zero, loop device must flush all queued bios. Otherwise it can lead to situation that caller closes the device, but some bios are still running and endio() function call later OOpses when uses unallocated mempool. This happens for example when running dm-crypt over loop, here is typical oops backtrace: Oops: 0000 [#1] PREEMPT SMP EIP is at mempool_free+0x12/0x6b ... crypt_dec_pending+0x50/0x54 [dm_crypt] crypt_endio+0x9f/0xa7 [dm_crypt] crypt_endio+0x0/0xa7 [dm_crypt] bio_endio+0x2b/0x2e loop_thread+0x37a/0x3b1 do_lo_send_aops+0x0/0x165 autoremove_wake_function+0x0/0x33 loop_thread+0x0/0x3b1 kthread+0x3b/0x61 kthread+0x0/0x61 kernel_thread_helper+0x7/0x10 (But crash is reproducible with different dm targets running over loop device too.) Patch fixes it by flushing the bios in release call, reusing the flush mechanism for switching backing store. Signed-off-by: Milan Broz Signed-off-by: Jens Axboe --- drivers/block/loop.c | 38 +++++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/block/loop.c b/drivers/block/loop.c index fb06ed659212..66cfff79938e 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -623,6 +623,18 @@ static int loop_switch(struct loop_device *lo, struct file *file) return 0; } +/* + * Helper to flush the IOs in loop, but keeping loop thread running + */ +static int loop_flush(struct loop_device *lo) +{ + /* loop not yet configured, no running thread, nothing to flush */ + if (!lo->lo_thread) + return 0; + + return loop_switch(lo, NULL); +} + /* * Do the actual switch; called from the BIO completion routine */ @@ -630,14 +642,20 @@ static void do_loop_switch(struct loop_device *lo, struct switch_request *p) { struct file *file = p->file; struct file *old_file = lo->lo_backing_file; - struct address_space *mapping = file->f_mapping; + struct address_space *mapping; + + /* if no new file, only flush of queued bios requested */ + if (!file) + goto out; + mapping = file->f_mapping; mapping_set_gfp_mask(old_file->f_mapping, lo->old_gfp_mask); lo->lo_backing_file = file; lo->lo_blocksize = S_ISBLK(mapping->host->i_mode) ? mapping->host->i_bdev->bd_block_size : PAGE_SIZE; lo->old_gfp_mask = mapping_gfp_mask(mapping); mapping_set_gfp_mask(mapping, lo->old_gfp_mask & ~(__GFP_IO|__GFP_FS)); +out: complete(&p->wait); } @@ -1345,11 +1363,25 @@ static int lo_release(struct gendisk *disk, fmode_t mode) struct loop_device *lo = disk->private_data; mutex_lock(&lo->lo_ctl_mutex); - --lo->lo_refcnt; - if ((lo->lo_flags & LO_FLAGS_AUTOCLEAR) && !lo->lo_refcnt) + if (--lo->lo_refcnt) + goto out; + + if (lo->lo_flags & LO_FLAGS_AUTOCLEAR) { + /* + * In autoclear mode, stop the loop thread + * and remove configuration after last close. + */ loop_clr_fd(lo, NULL); + } else { + /* + * Otherwise keep thread (if running) and config, + * but flush possible ongoing bios in thread. + */ + loop_flush(lo); + } +out: mutex_unlock(&lo->lo_ctl_mutex); return 0; -- cgit v1.2.3 From 8ae30b895805a6e2bb725b1d78b12daabd7eadfe Mon Sep 17 00:00:00 2001 From: Milan Broz Date: Fri, 12 Dec 2008 14:50:49 +0100 Subject: loop: Do not call loop_unplug for not configured loop device. In loop_unplug() function is expected that mapping is set and lo->lo_backing_file is not NULL. Unfortunately loop_set_fd() set the request queue unplug function, but loop_clr_fd() doesn't clear that. Loop device allows open of non-configured loop in some situations. If the unplug on request queue is called, loop module oopses because of missing lo_backing_file. Simple reproducer: losetup /dev/loop0 /xxx losetup -d /dev/loop0 dmsetup create x --table "0 1 linear /dev/loop0 0" EIP is at loop_unplug+0x1d/0x3b ... Call Trace: blk_unplug+0x57/0x5e dm_table_unplug_all+0x34/0x77 [dm_mod] destroy_inode+0x27/0x38 generic_delete_inode+0xd5/0xd9 iput+0x4b/0x4e dm_resume+0xca/0xfe [dm_mod] dev_suspend+0x143/0x165 [dm_mod] dm_ctl_ioctl+0x18e/0x1cf [dm_mod] dev_suspend+0x0/0x165 [dm_mod] dm_ctl_ioctl+0x0/0x1cf [dm_mod] vfs_ioctl+0x22/0x69 do_vfs_ioctl+0x39d/0x3c7 trace_hardirqs_on+0xb/0xd remove_vma+0x50/0x56 do_munmap+0x21c/0x237 sys_ioctl+0x2c/0x45 sysenter_do_call+0x12/0x31 Several reports here http://www.kerneloops.org/search.php?search=loop_unplug Fix it by simply clear unplug function together with removing of backing file. Signed-off-by: Milan Broz Signed-off-by: Jens Axboe --- drivers/block/loop.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/block/loop.c b/drivers/block/loop.c index 66cfff79938e..edbaac6c0573 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -919,6 +919,7 @@ static int loop_clr_fd(struct loop_device *lo, struct block_device *bdev) kthread_stop(lo->lo_thread); + lo->lo_queue->unplug_fn = NULL; lo->lo_backing_file = NULL; loop_release_xfer(lo); -- cgit v1.2.3 From a0ea8622918019ed76c0b85f5d0247809ba05a7c Mon Sep 17 00:00:00 2001 From: "Stephen M. Cameron" Date: Thu, 18 Dec 2008 14:55:51 +0100 Subject: cciss: simplify parameters to deregister_disk function Simplify parameters to deregister_disk function. Signed-off-by: Stephen M. Cameron Signed-off-by: Jens Axboe --- drivers/block/cciss.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index 4e07b5ca847c..01e69383d9c0 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -164,7 +164,7 @@ static int cciss_getgeo(struct block_device *bdev, struct hd_geometry *geo); static int cciss_revalidate(struct gendisk *disk); static int rebuild_lun_table(ctlr_info_t *h, int first_time); -static int deregister_disk(struct gendisk *disk, drive_info_struct *drv, +static int deregister_disk(ctlr_info_t *h, int drv_index, int clear_all); static void cciss_read_capacity(int ctlr, int logvol, int withirq, @@ -1479,8 +1479,7 @@ static void cciss_update_drive_info(int ctlr, int drv_index, int first_time) * which keeps the interrupt handler from starting * the queue. */ - ret = deregister_disk(h->gendisk[drv_index], - &h->drv[drv_index], 0); + ret = deregister_disk(h, drv_index, 0); h->drv[drv_index].busy_configuring = 0; } @@ -1698,8 +1697,7 @@ static int rebuild_lun_table(ctlr_info_t *h, int first_time) spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags); h->drv[i].busy_configuring = 1; spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags); - return_code = deregister_disk(h->gendisk[i], - &h->drv[i], 1); + return_code = deregister_disk(h, i, 1); h->drv[i].busy_configuring = 0; } } @@ -1769,15 +1767,19 @@ mem_msg: * the highest_lun should be left unchanged and the LunID * should not be cleared. */ -static int deregister_disk(struct gendisk *disk, drive_info_struct *drv, +static int deregister_disk(ctlr_info_t *h, int drv_index, int clear_all) { int i; - ctlr_info_t *h = get_host(disk); + struct gendisk *disk; + drive_info_struct *drv; if (!capable(CAP_SYS_RAWIO)) return -EPERM; + drv = &h->drv[drv_index]; + disk = h->gendisk[drv_index]; + /* make sure logical volume is NOT is use */ if (clear_all || (h->gendisk[0] == disk)) { if (drv->usage_count > 1) -- cgit v1.2.3