Commit 3147c531 authored by Marcin Slusarz's avatar Marcin Slusarz Committed by Jens Axboe

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: default avatarMarcin Slusarz <marcin.slusarz@gmail.com>
Cc: Bartlomiej Zolnierkiewicz <bzolnier@gmail.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Jens Axboe <jens.axboe@oracle.com>
Signed-off-by: default avatarJens Axboe <jens.axboe@oracle.com>
parent 2b91bafc
...@@ -2787,271 +2787,359 @@ static int cdrom_switch_blocksize(struct cdrom_device_info *cdi, int size) ...@@ -2787,271 +2787,359 @@ static int cdrom_switch_blocksize(struct cdrom_device_info *cdi, int size)
return cdo->generic_packet(cdi, &cgc); return cdo->generic_packet(cdi, &cgc);
} }
static int mmc_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, static noinline int mmc_ioctl_cdrom_read_data(struct cdrom_device_info *cdi,
unsigned long arg) void __user *arg,
{ struct packet_command *cgc,
struct cdrom_device_ops *cdo = cdi->ops; int cmd)
struct packet_command cgc; {
struct request_sense sense; struct request_sense sense;
unsigned char buffer[32]; struct cdrom_msf msf;
int ret = 0; int blocksize = 0, format = 0, lba;
int ret;
memset(&cgc, 0, sizeof(cgc));
/* build a unified command and queue it through
cdo->generic_packet() */
switch (cmd) { switch (cmd) {
case CDROMREADRAW: case CDROMREADRAW:
blocksize = CD_FRAMESIZE_RAW;
break;
case CDROMREADMODE1: case CDROMREADMODE1:
case CDROMREADMODE2: { blocksize = CD_FRAMESIZE;
struct cdrom_msf msf; format = 2;
int blocksize = 0, format = 0, lba; break;
case CDROMREADMODE2:
switch (cmd) { blocksize = CD_FRAMESIZE_RAW0;
case CDROMREADRAW: break;
blocksize = CD_FRAMESIZE_RAW; }
break; IOCTL_IN(arg, struct cdrom_msf, msf);
case CDROMREADMODE1: lba = msf_to_lba(msf.cdmsf_min0, msf.cdmsf_sec0, msf.cdmsf_frame0);
blocksize = CD_FRAMESIZE; /* FIXME: we need upper bound checking, too!! */
format = 2; if (lba < 0)
break; return -EINVAL;
case CDROMREADMODE2:
blocksize = CD_FRAMESIZE_RAW0; cgc->buffer = kmalloc(blocksize, GFP_KERNEL);
break; if (cgc->buffer == NULL)
} return -ENOMEM;
IOCTL_IN(arg, struct cdrom_msf, msf);
lba = msf_to_lba(msf.cdmsf_min0,msf.cdmsf_sec0,msf.cdmsf_frame0); memset(&sense, 0, sizeof(sense));
/* FIXME: we need upper bound checking, too!! */ cgc->sense = &sense;
if (lba < 0) cgc->data_direction = CGC_DATA_READ;
return -EINVAL; ret = cdrom_read_block(cdi, cgc, lba, 1, format, blocksize);
cgc.buffer = kmalloc(blocksize, GFP_KERNEL); if (ret && sense.sense_key == 0x05 &&
if (cgc.buffer == NULL) sense.asc == 0x20 &&
return -ENOMEM; sense.ascq == 0x00) {
memset(&sense, 0, sizeof(sense)); /*
cgc.sense = &sense; * SCSI-II devices are not required to support
cgc.data_direction = CGC_DATA_READ; * READ_CD, so let's try switching block size
ret = cdrom_read_block(cdi, &cgc, lba, 1, format, blocksize); */
if (ret && sense.sense_key==0x05 && sense.asc==0x20 && sense.ascq==0x00) { /* FIXME: switch back again... */
/* ret = cdrom_switch_blocksize(cdi, blocksize);
* SCSI-II devices are not required to support if (ret)
* READ_CD, so let's try switching block size goto out;
*/ cgc->sense = NULL;
/* FIXME: switch back again... */ ret = cdrom_read_cd(cdi, cgc, lba, blocksize, 1);
if ((ret = cdrom_switch_blocksize(cdi, blocksize))) { ret |= cdrom_switch_blocksize(cdi, blocksize);
kfree(cgc.buffer); }
return ret; if (!ret && copy_to_user(arg, cgc->buffer, blocksize))
} ret = -EFAULT;
cgc.sense = NULL; out:
ret = cdrom_read_cd(cdi, &cgc, lba, blocksize, 1); kfree(cgc->buffer);
ret |= cdrom_switch_blocksize(cdi, blocksize); return ret;
} }
if (!ret && copy_to_user((char __user *)arg, cgc.buffer, blocksize))
ret = -EFAULT; static noinline int mmc_ioctl_cdrom_read_audio(struct cdrom_device_info *cdi,
kfree(cgc.buffer); 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; return ret;
} back = q.cdsc_format; /* local copy */
case CDROMREADAUDIO: { sanitize_format(&q.cdsc_absaddr, &back, requested);
struct cdrom_read_audio ra; sanitize_format(&q.cdsc_reladdr, &q.cdsc_format, requested);
int lba; IOCTL_OUT(arg, struct cdrom_subchnl, q);
/* cdinfo(CD_DO_IOCTL, "CDROMSUBCHNL successful\n"); */
IOCTL_IN(arg, struct cdrom_read_audio, ra); return 0;
}
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!! */ static noinline int mmc_ioctl_cdrom_play_msf(struct cdrom_device_info *cdi,
if (lba < 0 || ra.nframes <= 0 || ra.nframes > CD_FRAMES) void __user *arg,
return -EINVAL; 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); static noinline int mmc_ioctl_cdrom_play_blk(struct cdrom_device_info *cdi,
} void __user *arg,
case CDROMSUBCHNL: { struct packet_command *cgc)
struct cdrom_subchnl q; {
u_char requested, back; struct cdrom_device_ops *cdo = cdi->ops;
IOCTL_IN(arg, struct cdrom_subchnl, q); struct cdrom_blk blk;
requested = q.cdsc_format; cdinfo(CD_DO_IOCTL, "entering CDROMPLAYBLK\n");
if (!((requested == CDROM_MSF) || IOCTL_IN(arg, struct cdrom_blk, blk);
(requested == CDROM_LBA))) cgc->cmd[0] = GPCMD_PLAY_AUDIO_10;
return -EINVAL; cgc->cmd[2] = (blk.from >> 24) & 0xff;
q.cdsc_format = CDROM_MSF; cgc->cmd[3] = (blk.from >> 16) & 0xff;
if ((ret = cdrom_read_subchannel(cdi, &q, 0))) cgc->cmd[4] = (blk.from >> 8) & 0xff;
return ret; cgc->cmd[5] = blk.from & 0xff;
back = q.cdsc_format; /* local copy */ cgc->cmd[7] = (blk.len >> 8) & 0xff;
sanitize_format(&q.cdsc_absaddr, &back, requested); cgc->cmd[8] = blk.len & 0xff;
sanitize_format(&q.cdsc_reladdr, &q.cdsc_format, requested); cgc->data_direction = CGC_DATA_NONE;
IOCTL_OUT(arg, struct cdrom_subchnl, q); return cdo->generic_packet(cdi, cgc);
/* 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;
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->buffer = buffer;
cgc.buflen = 24; cgc->buflen = 24;
if ((ret = cdrom_mode_sense(cdi, &cgc, GPMODE_AUDIO_CTL_PAGE, 0))) ret = cdrom_mode_sense(cdi, cgc, GPMODE_AUDIO_CTL_PAGE, 0);
return ret; if (ret)
return ret;
/* originally the code depended on buffer[1] to determine /* originally the code depended on buffer[1] to determine
how much data is available for transfer. buffer[1] is how much data is available for transfer. buffer[1] is
unfortunately ambigious and the only reliable way seem unfortunately ambigious and the only reliable way seem
to be to simply skip over the block descriptor... */ to be to simply skip over the block descriptor... */
offset = 8 + be16_to_cpu(*(__be16 *)(buffer+6)); offset = 8 + be16_to_cpu(*(__be16 *)(buffer + 6));
if (offset + 16 > sizeof(buffer)) if (offset + 16 > sizeof(buffer))
return -E2BIG; return -E2BIG;
if (offset + 16 > cgc.buflen) { if (offset + 16 > cgc->buflen) {
cgc.buflen = offset+16; cgc->buflen = offset + 16;
ret = cdrom_mode_sense(cdi, &cgc, ret = cdrom_mode_sense(cdi, cgc,
GPMODE_AUDIO_CTL_PAGE, 0); GPMODE_AUDIO_CTL_PAGE, 0);
if (ret) if (ret)
return ret; return ret;
} }
/* sanity check */ /* sanity check */
if ((buffer[offset] & 0x3f) != GPMODE_AUDIO_CTL_PAGE || if ((buffer[offset] & 0x3f) != GPMODE_AUDIO_CTL_PAGE ||
buffer[offset+1] < 14) buffer[offset + 1] < 14)
return -EINVAL; return -EINVAL;
/* now we have the current volume settings. if it was only /* now we have the current volume settings. if it was only
a CDROMVOLREAD, return these values */ a CDROMVOLREAD, return these values */
if (cmd == CDROMVOLREAD) { if (cmd == CDROMVOLREAD) {
volctrl.channel0 = buffer[offset+9]; volctrl.channel0 = buffer[offset+9];
volctrl.channel1 = buffer[offset+11]; volctrl.channel1 = buffer[offset+11];
volctrl.channel2 = buffer[offset+13]; volctrl.channel2 = buffer[offset+13];
volctrl.channel3 = buffer[offset+15]; volctrl.channel3 = buffer[offset+15];
IOCTL_OUT(arg, struct cdrom_volctrl, volctrl); IOCTL_OUT(arg, struct cdrom_volctrl, volctrl);
return 0; return 0;
} }
/* get the volume mask */ /* get the volume mask */
cgc.buffer = mask; cgc->buffer = mask;
if ((ret = cdrom_mode_sense(cdi, &cgc, ret = cdrom_mode_sense(cdi, cgc, GPMODE_AUDIO_CTL_PAGE, 1);
GPMODE_AUDIO_CTL_PAGE, 1))) if (ret)
return ret; return ret;
buffer[offset+9] = volctrl.channel0 & mask[offset+9]; buffer[offset + 9] = volctrl.channel0 & mask[offset + 9];
buffer[offset+11] = volctrl.channel1 & mask[offset+11]; buffer[offset + 11] = volctrl.channel1 & mask[offset + 11];
buffer[offset+13] = volctrl.channel2 & mask[offset+13]; buffer[offset + 13] = volctrl.channel2 & mask[offset + 13];
buffer[offset+15] = volctrl.channel3 & mask[offset+15]; buffer[offset + 15] = volctrl.channel3 & mask[offset + 15];
/* set volume */ /* set volume */
cgc.buffer = buffer + offset - 8; cgc->buffer = buffer + offset - 8;
memset(cgc.buffer, 0, 8); memset(cgc->buffer, 0, 8);
return cdrom_mode_select(cdi, &cgc); return cdrom_mode_select(cdi, cgc);
} }
case CDROMSTART: static noinline int mmc_ioctl_cdrom_start_stop(struct cdrom_device_info *cdi,
case CDROMSTOP: { struct packet_command *cgc,
cdinfo(CD_DO_IOCTL, "entering CDROMSTART/CDROMSTOP\n"); int cmd)
cgc.cmd[0] = GPCMD_START_STOP_UNIT; {
cgc.cmd[1] = 1; struct cdrom_device_ops *cdo = cdi->ops;
cgc.cmd[4] = (cmd == CDROMSTART) ? 1 : 0; cdinfo(CD_DO_IOCTL, "entering CDROMSTART/CDROMSTOP\n");
cgc.data_direction = CGC_DATA_NONE; cgc->cmd[0] = GPCMD_START_STOP_UNIT;
return cdo->generic_packet(cdi, &cgc); 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: static noinline int mmc_ioctl_cdrom_pause_resume(struct cdrom_device_info *cdi,
case CDROMRESUME: { struct packet_command *cgc,
cdinfo(CD_DO_IOCTL, "entering CDROMPAUSE/CDROMRESUME\n"); int cmd)
cgc.cmd[0] = GPCMD_PAUSE_RESUME; {
cgc.cmd[8] = (cmd == CDROMRESUME) ? 1 : 0; struct cdrom_device_ops *cdo = cdi->ops;
cgc.data_direction = CGC_DATA_NONE; cdinfo(CD_DO_IOCTL, "entering CDROMPAUSE/CDROMRESUME\n");
return cdo->generic_packet(cdi, &cgc); 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: { static noinline int mmc_ioctl_dvd_read_struct(struct cdrom_device_info *cdi,
dvd_struct *s; void __user *arg)
int size = sizeof(dvd_struct); {
if (!CDROM_CAN(CDC_DVD)) int ret;
return -ENOSYS; dvd_struct *s;
if ((s = kmalloc(size, GFP_KERNEL)) == NULL) int size = sizeof(dvd_struct);
return -ENOMEM;
cdinfo(CD_DO_IOCTL, "entering DVD_READ_STRUCT\n"); if (!CDROM_CAN(CDC_DVD))
if (copy_from_user(s, (dvd_struct __user *)arg, size)) { return -ENOSYS;
kfree(s);
return -EFAULT; s = kmalloc(size, GFP_KERNEL);
} if (!s)
if ((ret = dvd_read_struct(cdi, s))) { return -ENOMEM;
kfree(s);
return ret; cdinfo(CD_DO_IOCTL, "entering DVD_READ_STRUCT\n");
} if (copy_from_user(s, arg, size)) {
if (copy_to_user((dvd_struct __user *)arg, s, size))
ret = -EFAULT;
kfree(s); 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; return ret;
} IOCTL_OUT(arg, dvd_authinfo, ai);
return 0;
}
case DVD_AUTH: { static noinline int mmc_ioctl_cdrom_next_writable(struct cdrom_device_info *cdi,
dvd_authinfo ai; void __user *arg)
if (!CDROM_CAN(CDC_DVD)) {
return -ENOSYS; int ret;
cdinfo(CD_DO_IOCTL, "entering DVD_AUTH\n"); long next = 0;
IOCTL_IN(arg, dvd_authinfo, ai); cdinfo(CD_DO_IOCTL, "entering CDROM_NEXT_WRITABLE\n");
if ((ret = dvd_do_auth (cdi, &ai))) ret = cdrom_get_next_writable(cdi, &next);
return ret; if (ret)
IOCTL_OUT(arg, dvd_authinfo, ai); return ret;
return 0; IOCTL_OUT(arg, long, next);
} return 0;
}
case CDROM_NEXT_WRITABLE: { static noinline int mmc_ioctl_cdrom_last_written(struct cdrom_device_info *cdi,
long next = 0; void __user *arg)
cdinfo(CD_DO_IOCTL, "entering CDROM_NEXT_WRITABLE\n"); {
if ((ret = cdrom_get_next_writable(cdi, &next))) int ret;
return ret; long last = 0;
IOCTL_OUT(arg, long, next); cdinfo(CD_DO_IOCTL, "entering CDROM_LAST_WRITTEN\n");
return 0; ret = cdrom_get_last_written(cdi, &last);
} if (ret)
case CDROM_LAST_WRITTEN: { return ret;
long last = 0; IOCTL_OUT(arg, long, last);
cdinfo(CD_DO_IOCTL, "entering CDROM_LAST_WRITTEN\n"); return 0;
if ((ret = cdrom_get_last_written(cdi, &last))) }
return ret;
IOCTL_OUT(arg, long, last); static int mmc_ioctl(struct cdrom_device_info *cdi, unsigned int cmd,
return 0; unsigned long arg)
} {
} /* switch */ 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; return -ENOTTY;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment