diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/sg.c linux.ac/drivers/scsi/sg.c --- linux.vanilla/drivers/scsi/sg.c Mon Jan 15 21:08:15 2001 +++ linux.ac/drivers/scsi/sg.c Mon Jun 25 09:50:33 2001 @@ -7,7 +7,7 @@ * Original driver (sg.c): * Copyright (C) 1992 Lawrence Foard * Version 2 and 3 extensions to driver: - * Copyright (C) 1998 - 2000 Douglas Gilbert + * Copyright (C) 1998 - 2001 Douglas Gilbert * * Modified 19-JAN-1998 Richard Gooch Devfs support * @@ -19,9 +19,9 @@ */ #include #ifdef CONFIG_PROC_FS - static char * sg_version_str = "Version: 3.1.17 (20001002)"; + static char sg_version_str[] = "Version: 3.1.19 (20010623)"; #endif - static int sg_version_num = 30117; /* 2 digits for each component */ + static int sg_version_num = 30119; /* 2 digits for each component */ /* * D. P. Gilbert (dgilbert@xxxxxxxxxxxx, dougg@xxxxxxxxxxxxx), notes: * - scsi logging is available via SCSI_LOG_TIMEOUT macros. First @@ -76,8 +76,9 @@ #include #endif /* LINUX_VERSION_CODE */ -/* #define SG_ALLOW_DIO */ -#ifdef SG_ALLOW_DIO +#define SG_ALLOW_DIO_DEF 0 +#define SG_ALLOW_DIO_CODE /* compile out be commenting this define */ +#ifdef SG_ALLOW_DIO_CODE #include #endif @@ -89,6 +90,7 @@ readable via /proc/sys/kernel/sg-big-buff if the sg driver is built into the kernel (i.e. it is not a module).] */ static int def_reserved_size = -1; /* picks up init parameter */ +static int sg_allow_dio = SG_ALLOW_DIO_DEF; #define SG_SECTOR_SZ 512 #define SG_SECTOR_MSK (SG_SECTOR_SZ - 1) @@ -112,7 +114,7 @@ static int sg_detect(Scsi_Device *); static void sg_detach(Scsi_Device *); -static Scsi_Request * dummy_cmdp = 0; /* only used for sizeof */ +static Scsi_Request * dummy_cmdp; /* only used for sizeof */ static rwlock_t sg_dev_arr_lock = RW_LOCK_UNLOCKED; /* Also used to lock file descriptor list for device */ @@ -157,7 +159,7 @@ char res_used; /* 1 -> using reserve buffer, 0 -> not ... */ char orphan; /* 1 -> drop on sight, 0 -> normal */ char sg_io_owned; /* 1 -> packet belongs to SG_IO */ - char done; /* 0->before bh, 1->before read, 2->read */ + volatile char done; /* 0->before bh, 1->before read, 2->read */ } Sg_request; /* 168 bytes long on i386 */ typedef struct sg_fd /* holds the state of a file descriptor */ @@ -174,12 +176,12 @@ Sg_request req_arr[SG_MAX_QUEUE]; /* used as singly-linked list */ char low_dma; /* as in parent but possibly overridden to 1 */ char force_packid; /* 1 -> pack_id input to read(), 0 -> ignored */ - char closed; /* 1 -> fd closed but request(s) outstanding */ + volatile char closed; /* 1 -> fd closed but request(s) outstanding */ char fd_mem_src; /* heap whereabouts of this Sg_fd object */ char cmd_q; /* 1 -> allow command queuing, 0 -> don't */ char next_cmd_len; /* 0 -> automatic (def), >0 -> use on next write() */ char keep_orphan; /* 0 -> drop orphan (def), 1 -> keep for read() */ -} Sg_fd; /* 2768 bytes long on i386 */ +} Sg_fd; /* 2760 bytes long on i386 */ typedef struct sg_device /* holds the state of each scsi generic device */ { @@ -189,10 +191,10 @@ Sg_fd * headfp; /* first open fd belonging to this device */ devfs_handle_t de; kdev_t i_rdev; /* holds device major+minor number */ - char exclude; /* opened for exclusive access */ + volatile char detached; /* 0->attached, 1->detached pending removal */ + volatile char exclude; /* opened for exclusive access */ char sgdebug; /* 0->off, 1->sense, 9->dump dev, 10-> all devs */ - char detached; /* 0->attached, 1->detached pending removal */ -} Sg_device; /* 44 bytes long on i386 */ +} Sg_device; /* 36 bytes long on i386 */ static int sg_fasync(int fd, struct file * filp, int mode); @@ -225,13 +227,12 @@ static void sg_low_free(char * buff, int size, int mem_src); static Sg_fd * sg_add_sfp(Sg_device * sdp, int dev); static int sg_remove_sfp(Sg_device * sdp, Sg_fd * sfp); +static void __sg_remove_sfp(Sg_device * sdp, Sg_fd * sfp); static Sg_request * sg_get_rq_mark(Sg_fd * sfp, int pack_id); static Sg_request * sg_add_request(Sg_fd * sfp); static int sg_remove_request(Sg_fd * sfp, Sg_request * srp); static int sg_res_in_use(Sg_fd * sfp); -static int sg_dio_in_use(Sg_fd * sfp); static void sg_clr_srpnt(Scsi_Request * SRpnt); -static void sg_shorten_timeout(Scsi_Request * srpnt); static int sg_ms_to_jif(unsigned int msecs); static unsigned sg_jif_to_ms(int jifs); static int sg_allow_access(unsigned char opcode, char dev_type); @@ -244,10 +245,10 @@ static Sg_device ** sg_dev_arr = NULL; -static const int size_sg_header = sizeof(struct sg_header); -static const int size_sg_io_hdr = sizeof(sg_io_hdr_t); -static const int size_sg_iovec = sizeof(sg_iovec_t); -static const int size_sg_req_info = sizeof(sg_req_info_t); +#define SZ_SG_HEADER sizeof(struct sg_header) +#define SZ_SG_IO_HDR sizeof(sg_io_hdr_t) +#define SZ_SG_IOVEC sizeof(sg_iovec_t) +#define SZ_SG_REQ_INFO sizeof(sg_req_info_t) static int sg_open(struct inode * inode, struct file * filp) @@ -257,43 +258,56 @@ Sg_device * sdp; Sg_fd * sfp; int res; + int retval = -EBUSY; + SCSI_LOG_TIMEOUT(3, printk("sg_open: dev=%d, flags=0x%x\n", dev, flags)); sdp = sg_get_dev(dev); - if ((! sdp) || (! sdp->device) || (! sdp->device->host)) - return -ENXIO; - if (sdp->i_rdev != inode->i_rdev) - printk("sg_open: inode maj=%d, min=%d sdp maj=%d, min=%d\n", - MAJOR(inode->i_rdev), MINOR(inode->i_rdev), - MAJOR(sdp->i_rdev), MINOR(sdp->i_rdev)); - /* If we are in the middle of error recovery, don't let anyone - * else try and use this device. Also, if error recovery fails, it - * may try and take the device offline, in which case all further - * access to the device is prohibited. */ - if (! ((flags & O_NONBLOCK) || - scsi_block_when_processing_errors(sdp->device))) + if ((! sdp) || (! sdp->device)) return -ENXIO; + if (sdp->detached) + return -ENODEV; - SCSI_LOG_TIMEOUT(3, printk("sg_open: dev=%d, flags=0x%x\n", dev, flags)); + /* This driver's module count bumped by fops_get in */ + /* Prevent the device driver from vanishing while we sleep */ + if (sdp->device->host->hostt->module) + __MOD_INC_USE_COUNT(sdp->device->host->hostt->module); + + if (! ((flags & O_NONBLOCK) || + scsi_block_when_processing_errors(sdp->device))) { + retval = -ENXIO; + /* we are in error recovery for this device */ + goto error_out; + } if (flags & O_EXCL) { - if (O_RDONLY == (flags & O_ACCMODE)) - return -EACCES; /* Can't lock it with read only access */ - if (sdp->headfp && (flags & O_NONBLOCK)) - return -EBUSY; - res = 0; /* following is a macro that beats race condition */ + if (O_RDONLY == (flags & O_ACCMODE)) { + retval = -EACCES; /* Can't lock it with read only access */ + goto error_out; + } + if (sdp->headfp && (flags & O_NONBLOCK)) + goto error_out; + res = 0; __wait_event_interruptible(sdp->o_excl_wait, ((sdp->headfp || sdp->exclude) ? 0 : (sdp->exclude = 1)), res); - if (res) - return res; /* -ERESTARTSYS because signal hit process */ + if (res) { + retval = res; /* -ERESTARTSYS because signal hit process */ + goto error_out; + } } else if (sdp->exclude) { /* some other fd has an exclusive lock on dev */ if (flags & O_NONBLOCK) - return -EBUSY; - res = 0; /* following is a macro that beats race condition */ + goto error_out; + res = 0; __wait_event_interruptible(sdp->o_excl_wait, (! sdp->exclude), res); - if (res) - return res; /* -ERESTARTSYS because signal hit process */ + if (res) { + retval = res; /* -ERESTARTSYS because signal hit process */ + goto error_out; + } + } + if (sdp->detached) { + retval = -ENODEV; + goto error_out; } if (! sdp->headfp) { /* no existing opens on this device */ sdp->sgdebug = 0; @@ -303,12 +317,15 @@ filp->private_data = sfp; else { if (flags & O_EXCL) sdp->exclude = 0; /* undo if error */ - return -ENOMEM; + retval = -ENOMEM; + goto error_out; } - - if (sdp->device->host->hostt->module) - __MOD_INC_USE_COUNT(sdp->device->host->hostt->module); return 0; + +error_out: + if ((! sdp->detached) && sdp->device->host->hostt->module) + __MOD_DEC_USE_COUNT(sdp->device->host->hostt->module); + return retval; } /* Following function was formerly called 'sg_close' */ @@ -324,14 +341,12 @@ } SCSI_LOG_TIMEOUT(3, printk("sg_release: dev=%d\n", MINOR(sdp->i_rdev))); sg_fasync(-1, filp, 0); /* remove filp from async notification list */ - sg_remove_sfp(sdp, sfp); - if (! sdp->headfp) - filp->private_data = NULL; - - if (sdp->device->host->hostt->module) - __MOD_DEC_USE_COUNT(sdp->device->host->hostt->module); - sdp->exclude = 0; - wake_up_interruptible(&sdp->o_excl_wait); + if (0 == sg_remove_sfp(sdp, sfp)) { /* Returns 1 when sdp gone */ + if ((! sdp->detached) && sdp->device->host->hostt->module) + __MOD_DEC_USE_COUNT(sdp->device->host->hostt->module); + sdp->exclude = 0; + wake_up_interruptible(&sdp->o_excl_wait); + } unlock_kernel(); return 0; } @@ -356,11 +371,11 @@ ; /* FIXME: Hmm. Seek to the right place, or fail? */ if ((k = verify_area(VERIFY_WRITE, buf, count))) return k; - if (sfp->force_packid && (count >= size_sg_header)) { - __copy_from_user(&old_hdr, buf, size_sg_header); + if (sfp->force_packid && (count >= SZ_SG_HEADER)) { + __copy_from_user(&old_hdr, buf, SZ_SG_HEADER); if (old_hdr.reply_len < 0) { - if (count >= size_sg_io_hdr) { - __copy_from_user(&new_hdr, buf, size_sg_io_hdr); + if (count >= SZ_SG_IO_HDR) { + __copy_from_user(&new_hdr, buf, SZ_SG_IO_HDR); req_pack_id = new_hdr.pack_id; } } @@ -369,25 +384,26 @@ } srp = sg_get_rq_mark(sfp, req_pack_id); if (! srp) { /* now wait on packet to arrive */ + if (sdp->detached) + return -ENODEV; if (filp->f_flags & O_NONBLOCK) return -EAGAIN; while (1) { - int dio = sg_dio_in_use(sfp); res = 0; /* following is a macro that beats race condition */ - __wait_event_interruptible(sfp->read_wait, - (srp = sg_get_rq_mark(sfp, req_pack_id)), - res); + __wait_event_interruptible(sfp->read_wait, (sdp->detached || + (srp = sg_get_rq_mark(sfp, req_pack_id))), res); + if (sdp->detached) + return -ENODEV; if (0 == res) break; - else if (! dio) /* only let signal out if no dio */ - return res; /* -ERESTARTSYS because signal hit process */ + return res; /* -ERESTARTSYS because signal hit process */ } } if (srp->header.interface_id != '\0') return sg_new_read(sfp, buf, count, srp); hp = &srp->header; - memset(&old_hdr, 0, size_sg_header); + memset(&old_hdr, 0, SZ_SG_HEADER); old_hdr.reply_len = (int)hp->timeout; old_hdr.pack_len = old_hdr.reply_len; /* very old, strange behaviour */ old_hdr.pack_id = hp->pack_id; @@ -430,13 +446,13 @@ } /* Now copy the result back to the user buffer. */ - if (count >= size_sg_header) { - __copy_to_user(buf, &old_hdr, size_sg_header); - buf += size_sg_header; + if (count >= SZ_SG_HEADER) { + __copy_to_user(buf, &old_hdr, SZ_SG_HEADER); + buf += SZ_SG_HEADER; if (count > old_hdr.reply_len) count = old_hdr.reply_len; - if (count > size_sg_header) - sg_read_oxfer(srp, buf, count - size_sg_header); + if (count > SZ_SG_HEADER) + sg_read_oxfer(srp, buf, count - SZ_SG_HEADER); } else count = (old_hdr.result == 0) ? 0 : -EIO; @@ -447,11 +463,14 @@ static ssize_t sg_new_read(Sg_fd * sfp, char * buf, size_t count, Sg_request * srp) { - sg_io_hdr_t * hp = &srp->header; - int k, len; + sg_io_hdr_t * hp = &srp->header; + int err = 0; + int len; - if (count < size_sg_io_hdr) - return -EINVAL; + if (count < SZ_SG_IO_HDR) { + err = -EINVAL; + goto err_out; + } hp->sb_len_wr = 0; if ((hp->mx_sb_len > 0) && hp->sbp) { if ((CHECK_CONDITION & hp->masked_status) || @@ -460,20 +479,19 @@ sb_len = (hp->mx_sb_len > sb_len) ? sb_len : hp->mx_sb_len; len = 8 + (int)srp->sense_b[7]; /* Additional sense length field */ len = (len > sb_len) ? sb_len : len; - if ((k = verify_area(VERIFY_WRITE, hp->sbp, len))) - return k; + if ((err = verify_area(VERIFY_WRITE, hp->sbp, len))) + goto err_out; __copy_to_user(hp->sbp, srp->sense_b, len); hp->sb_len_wr = len; } } if (hp->masked_status || hp->host_status || hp->driver_status) hp->info |= SG_INFO_CHECK; - copy_to_user(buf, hp, size_sg_io_hdr); - - k = sg_read_xfer(srp); - if (k) return k; /* probably -EFAULT, bad addr in dxferp or iovec list */ + copy_to_user(buf, hp, SZ_SG_IO_HDR); + err = sg_read_xfer(srp); +err_out: sg_finish_rem_req(srp); - return count; + return (0 == err) ? count : err; } @@ -494,7 +512,8 @@ return -ENXIO; SCSI_LOG_TIMEOUT(3, printk("sg_write: dev=%d, count=%d\n", MINOR(sdp->i_rdev), (int)count)); - + if (sdp->detached) + return -ENODEV; if (! ((filp->f_flags & O_NONBLOCK) || scsi_block_when_processing_errors(sdp->device))) return -ENXIO; @@ -503,20 +522,20 @@ if ((k = verify_area(VERIFY_READ, buf, count))) return k; /* protects following copy_from_user()s + get_user()s */ - if (count < size_sg_header) + if (count < SZ_SG_HEADER) return -EIO; - __copy_from_user(&old_hdr, buf, size_sg_header); + __copy_from_user(&old_hdr, buf, SZ_SG_HEADER); blocking = !(filp->f_flags & O_NONBLOCK); if (old_hdr.reply_len < 0) return sg_new_write(sfp, buf, count, blocking, 0, NULL); - if (count < (size_sg_header + 6)) + if (count < (SZ_SG_HEADER + 6)) return -EIO; /* The minimum scsi command length is 6 bytes. */ if (! (srp = sg_add_request(sfp))) { SCSI_LOG_TIMEOUT(1, printk("sg_write: queue full\n")); return -EDOM; } - buf += size_sg_header; + buf += SZ_SG_HEADER; __get_user(opcode, buf); if (sfp->next_cmd_len > 0) { if (sfp->next_cmd_len > MAX_COMMAND_SIZE) { @@ -539,8 +558,8 @@ input_size = count - cmd_size; mxsize = (input_size > old_hdr.reply_len) ? input_size : old_hdr.reply_len; - mxsize -= size_sg_header; - input_size -= size_sg_header; + mxsize -= SZ_SG_HEADER; + input_size -= SZ_SG_HEADER; if (input_size < 0) { sg_remove_request(sfp, srp); return -EIO; /* User did not pass enough bytes for this command. */ @@ -550,16 +569,12 @@ hp->cmd_len = (unsigned char)cmd_size; hp->iovec_count = 0; hp->mx_sb_len = 0; -#if 0 - hp->dxfer_direction = SG_DXFER_UNKNOWN; -#else if (input_size > 0) - hp->dxfer_direction = ((old_hdr.reply_len - size_sg_header) > 0) ? + hp->dxfer_direction = ((old_hdr.reply_len - SZ_SG_HEADER) > 0) ? SG_DXFER_TO_FROM_DEV : SG_DXFER_TO_DEV; else hp->dxfer_direction = (mxsize > 0) ? SG_DXFER_FROM_DEV : SG_DXFER_NONE; -#endif hp->dxfer_len = mxsize; hp->dxferp = (unsigned char *)buf + cmd_size; hp->sbp = NULL; @@ -581,7 +596,7 @@ unsigned char cmnd[sizeof(dummy_cmdp->sr_cmnd)]; int timeout; - if (count < size_sg_io_hdr) + if (count < SZ_SG_IO_HDR) return -EINVAL; if ((k = verify_area(VERIFY_READ, buf, count))) return k; /* protects following copy_from_user()s + get_user()s */ @@ -592,7 +607,7 @@ return -EDOM; } hp = &srp->header; - __copy_from_user(hp, buf, size_sg_io_hdr); + __copy_from_user(hp, buf, SZ_SG_IO_HDR); if (hp->interface_id != 'S') { sg_remove_request(sfp, srp); return -ENOSYS; @@ -648,7 +663,10 @@ sg_finish_rem_req(srp); return k; } -/* SCSI_LOG_TIMEOUT(7, printk("sg_write: allocating device\n")); */ + if (sdp->detached) { + sg_finish_rem_req(srp); + return -ENODEV; + } SRpnt = scsi_allocate_request(sdp->device); if(SRpnt == NULL) { SCSI_LOG_TIMEOUT(1, printk("sg_write: no mem\n")); @@ -656,17 +674,15 @@ return -ENOMEM; } -/* SCSI_LOG_TIMEOUT(7, printk("sg_write: device allocated\n")); */ srp->my_cmdp = SRpnt; SRpnt->sr_request.rq_dev = sdp->i_rdev; SRpnt->sr_request.rq_status = RQ_ACTIVE; SRpnt->sr_sense_buffer[0] = 0; SRpnt->sr_cmd_len = hp->cmd_len; -/* Set the LUN field in the command structure, overriding user input */ - if (! (hp->flags & SG_FLAG_LUN_INHIBIT)) - cmnd[1] = (cmnd[1] & 0x1f) | (sdp->device->lun << 5); - -/* SCSI_LOG_TIMEOUT(7, printk("sg_write: do cmd\n")); */ + if (! (hp->flags & SG_FLAG_LUN_INHIBIT)) { + if (sdp->device->scsi_level <= SCSI_2) + cmnd[1] = (cmnd[1] & 0x1f) | (sdp->device->lun << 5); + } SRpnt->sr_use_sg = srp->data.k_use_sg; SRpnt->sr_sglist_len = srp->data.sglist_len; SRpnt->sr_bufflen = srp->data.bufflen; @@ -719,30 +735,31 @@ { int blocking = 1; /* ignore O_NONBLOCK flag */ + if (sdp->detached) + return -ENODEV; if(! scsi_block_when_processing_errors(sdp->device) ) return -ENXIO; - result = verify_area(VERIFY_WRITE, (void *)arg, size_sg_io_hdr); + result = verify_area(VERIFY_WRITE, (void *)arg, SZ_SG_IO_HDR); if (result) return result; - result = sg_new_write(sfp, (const char *)arg, size_sg_io_hdr, + result = sg_new_write(sfp, (const char *)arg, SZ_SG_IO_HDR, blocking, read_only, &srp); if (result < 0) return result; srp->sg_io_owned = 1; while (1) { - int dio = sg_dio_in_use(sfp); result = 0; /* following macro to beat race condition */ __wait_event_interruptible(sfp->read_wait, - (sfp->closed || srp->done), result); + (sdp->detached || sfp->closed || srp->done), result); + if (sdp->detached) + return -ENODEV; if (sfp->closed) return 0; /* request packet dropped already */ if (0 == result) break; - else if (! dio) { /* only let signal out if no dio */ - srp->orphan = 1; - return result; /* -ERESTARTSYS because signal hit process */ - } + srp->orphan = 1; + return result; /* -ERESTARTSYS because signal hit process */ } srp->done = 2; - result = sg_new_read(sfp, (char *)arg, size_sg_io_hdr, srp); + result = sg_new_read(sfp, (char *)arg, SZ_SG_IO_HDR, srp); return (result < 0) ? result : 0; } case SG_SET_TIMEOUT: @@ -765,8 +782,11 @@ sg_build_reserve(sfp, val); } } - else + else { + if (sdp->detached) + return -ENODEV; sfp->low_dma = sdp->device->host->unchecked_isa_dma; + } return 0; case SG_GET_LOW_DMA: return put_user((int)sfp->low_dma, (int *)arg); @@ -775,6 +795,9 @@ if (result) return result; else { sg_scsi_id_t * sg_idp = (sg_scsi_id_t *)arg; + + if (sdp->detached) + return -ENODEV; __put_user((int)sdp->device->host->host_no, &sg_idp->host_no); __put_user((int)sdp->device->channel, &sg_idp->channel); __put_user((int)sdp->device->id, &sg_idp->scsi_id); @@ -853,7 +876,7 @@ return put_user(sg_version_num, (int *)arg); case SG_GET_REQUEST_TABLE: result = verify_area(VERIFY_WRITE, (void *) arg, - size_sg_req_info * SG_MAX_QUEUE); + SZ_SG_REQ_INFO * SG_MAX_QUEUE); if (result) return result; else { sg_req_info_t rinfo[SG_MAX_QUEUE]; @@ -861,7 +884,7 @@ read_lock_irqsave(&sfp->rq_list_lock, iflags); for (srp = sfp->headrp, val = 0; val < SG_MAX_QUEUE; ++val, srp = srp ? srp->nextrp : srp) { - memset(&rinfo[val], 0, size_sg_req_info); + memset(&rinfo[val], 0, SZ_SG_REQ_INFO); if (srp) { rinfo[val].req_state = srp->done + 1; rinfo[val].problem = srp->header.masked_status & @@ -876,12 +899,16 @@ } } read_unlock_irqrestore(&sfp->rq_list_lock, iflags); - __copy_to_user((void *)arg, rinfo, size_sg_req_info * SG_MAX_QUEUE); + __copy_to_user((void *)arg, rinfo, SZ_SG_REQ_INFO * SG_MAX_QUEUE); return 0; } case SG_EMULATED_HOST: + if (sdp->detached) + return -ENODEV; return put_user(sdp->device->host->hostt->emulated, (int *)arg); case SG_SCSI_RESET: + if (sdp->detached) + return -ENODEV; if (filp->f_flags & O_NONBLOCK) { if (sdp->device->host->in_recovery) return -EBUSY; @@ -907,13 +934,16 @@ default: return -EINVAL; } - if(! capable(CAP_SYS_ADMIN)) return -EACCES; + if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) + return -EACCES; return (scsi_reset_provider(sdp->device, val) == SUCCESS) ? 0 : -EIO; #else SCSI_LOG_TIMEOUT(1, printk("sg_ioctl: SG_RESET_SCSI not supported\n")); result = -EINVAL; #endif case SCSI_IOCTL_SEND_COMMAND: + if (sdp->detached) + return -ENODEV; if (read_only) { unsigned char opcode = WRITE_6; Scsi_Ioctl_Command * siocp = (void *)arg; @@ -932,6 +962,8 @@ case SCSI_IOCTL_GET_BUS_NUMBER: case SCSI_IOCTL_PROBE_HOST: case SG_GET_TRANSFORM: + if (sdp->detached) + return -ENODEV; return scsi_ioctl(sdp->device, cmd_in, (void *)arg); default: if (read_only) @@ -949,7 +981,8 @@ int count = 0; unsigned long iflags; - if ((! (sfp = (Sg_fd *)filp->private_data)) || (! (sdp = sfp->parentdp))) + if ((! (sfp = (Sg_fd *)filp->private_data)) || (! (sdp = sfp->parentdp)) + || sfp->closed) return POLLERR; poll_wait(filp, &sfp->read_wait, wait); read_lock_irqsave(&sfp->rq_list_lock, iflags); @@ -960,7 +993,10 @@ ++count; } read_unlock_irqrestore(&sfp->rq_list_lock, iflags); - if (! sfp->cmd_q) { + + if (sdp->detached) + res |= POLLHUP; + else if (! sfp->cmd_q) { if (0 == count) res |= POLLOUT | POLLWRNORM; } @@ -1001,9 +1037,9 @@ if (dev < sg_template.dev_max) sdp = sg_dev_arr[dev]; } - if (NULL == sdp) { + if ((NULL == sdp) || sdp->detached) { read_unlock(&sg_dev_arr_lock); - SCSI_LOG_TIMEOUT(1, printk("sg...bh: bad args dev=%d\n", dev)); + SCSI_LOG_TIMEOUT(1, printk("sg...bh: dev=%d gone\n", dev)); scsi_release_request(SRpnt); SRpnt = NULL; return; @@ -1020,8 +1056,8 @@ break; sfp = sfp->nextfp; } - read_unlock(&sg_dev_arr_lock); if (! srp) { + read_unlock(&sg_dev_arr_lock); SCSI_LOG_TIMEOUT(1, printk("sg...bh: req missing, dev=%d\n", dev)); scsi_release_request(SRpnt); SRpnt = NULL; @@ -1035,6 +1071,7 @@ sg_clr_srpnt(SRpnt); srp->my_cmdp = NULL; srp->done = 1; + read_unlock(&sg_dev_arr_lock); SCSI_LOG_TIMEOUT(4, printk("sg...bh: dev=%d, pack_id=%d, res=0x%x\n", dev, srp->header.pack_id, (int)SRpnt->sr_result)); @@ -1071,7 +1108,6 @@ if (sfp->closed) { /* whoops this fd already released, cleanup */ SCSI_LOG_TIMEOUT(1, printk("sg...bh: already closed, freeing ...\n")); - /* should check if module is unloaded <<<<<<< */ sg_finish_rem_req(srp); srp = NULL; if (NULL == sfp->headrp) { @@ -1080,6 +1116,10 @@ sg_remove_sfp(sdp, sfp); sfp = NULL; } + if (sg_template.module) + __MOD_DEC_USE_COUNT(sg_template.module); + if (sdp->device->host->hostt->module) + __MOD_DEC_USE_COUNT(sdp->device->host->hostt->module); } else if (srp && srp->orphan) { if (sfp->keep_orphan) @@ -1110,19 +1150,6 @@ static int sg_detect(Scsi_Device * scsidp) { - switch (scsidp->type) { - case TYPE_DISK: - case TYPE_MOD: - case TYPE_ROM: - case TYPE_WORM: - case TYPE_TAPE: break; - default: - printk("Detected scsi generic sg%d at scsi%d," - " channel %d, id %d, lun %d, type %d\n", - sg_template.dev_noticed, - scsidp->host->host_no, scsidp->channel, - scsidp->id, scsidp->lun, scsidp->type); - } sg_template.dev_noticed++; return 1; } @@ -1241,51 +1268,73 @@ sg_template.nr_dev++; sg_dev_arr[k] = sdp; write_unlock_irqrestore(&sg_dev_arr_lock, iflags); + switch (scsidp->type) { + case TYPE_DISK: + case TYPE_MOD: + case TYPE_ROM: + case TYPE_WORM: + case TYPE_TAPE: break; + default: + printk("Attached scsi generic sg%d at scsi%d, channel %d, id %d," + " lun %d, type %d\n", k, scsidp->host->host_no, + scsidp->channel, scsidp->id, scsidp->lun, scsidp->type); + } return 0; } /* Called at 'finish' of init process, after all attaches */ static void sg_finish(void) -{ - SCSI_LOG_TIMEOUT(3, printk("sg_finish: dma_free_sectors=%u\n", - scsi_dma_free_sectors)); -} +{ } static void sg_detach(Scsi_Device * scsidp) { Sg_device * sdp; unsigned long iflags; Sg_fd * sfp; + Sg_fd * tsfp; Sg_request * srp; - int k; + Sg_request * tsrp; + int k, delay; if (NULL == sg_dev_arr) return; + delay = 0; write_lock_irqsave(&sg_dev_arr_lock, iflags); for (k = 0; k < sg_template.dev_max; k++) { sdp = sg_dev_arr[k]; if ((NULL == sdp) || (sdp->device != scsidp)) continue; /* dirty but lowers nesting */ if (sdp->headfp) { - for (sfp = sdp->headfp; sfp; sfp = sfp->nextfp) { - /* no lock on request list here */ - for (srp = sfp->headrp; srp; srp = srp->nextrp) { - if (! srp->done) { - write_unlock_irqrestore(&sg_dev_arr_lock, iflags); - sg_shorten_timeout(srp->my_cmdp); - write_lock_irqsave(&sg_dev_arr_lock, iflags); - } - } + sdp->detached = 1; + for (sfp = sdp->headfp; sfp; sfp = tsfp) { + tsfp = sfp->nextfp; + for (srp = sfp->headrp; srp; srp = tsrp) { + tsrp = srp->nextrp; + if (sfp->closed || (0 == srp->done)) + sg_finish_rem_req(srp); + } + if (sfp->closed) { + if (sg_template.module) + __MOD_DEC_USE_COUNT(sg_template.module); + if (sdp->device->host->hostt->module) + __MOD_DEC_USE_COUNT(sdp->device->host->hostt->module); + __sg_remove_sfp(sdp, sfp); + } + else { + delay = 1; + wake_up_interruptible(&sfp->read_wait); + kill_fasync(&sfp->async_qp, SIGPOLL, POLL_HUP); + } } - write_unlock_irqrestore(&sg_dev_arr_lock, iflags); - SCSI_LOG_TIMEOUT(3, printk("sg_detach: dev=%d, dirty, sleep(3)\n", k)); - scsi_sleep(3); /* sleep 3 jiffies, hoping for timeout to go off */ + SCSI_LOG_TIMEOUT(3, printk("sg_detach: dev=%d, dirty\n", k)); devfs_unregister (sdp->de); sdp->de = NULL; - sdp->detached = 1; - write_lock_irqsave(&sg_dev_arr_lock, iflags); + if (NULL == sdp->headfp) { + kfree((char *)sdp); + sg_dev_arr[k] = NULL; + } } - else { + else { /* nothing active, simple case */ SCSI_LOG_TIMEOUT(3, printk("sg_detach: dev=%d\n", k)); devfs_unregister (sdp->de); kfree((char *)sdp); @@ -1293,13 +1342,12 @@ } scsidp->attached--; sg_template.nr_dev--; -/* avoid associated device /dev/sg? being incremented - * each time module is inserted/removed , */ - sg_template.dev_noticed--; + sg_template.dev_noticed--; /* from */ break; } write_unlock_irqrestore(&sg_dev_arr_lock, iflags); - return; + if (delay) + scsi_sleep(2); /* dirty detach so delay device destruction */ } MODULE_AUTHOR("Douglas Gilbert"); @@ -1322,8 +1370,6 @@ scsi_unregister_module(MODULE_SCSI_DEV, &sg_template); devfs_unregister_chrdev(SCSI_GENERIC_MAJOR, "sg"); if(sg_dev_arr != NULL) { -/* Really worrying situation of writes still pending and get here */ -/* Strategy: shorten timeout on release + wait on detach ... */ kfree((char *)sg_dev_arr); sg_dev_arr = NULL; } @@ -1331,29 +1377,6 @@ } -#if 0 -extern void scsi_times_out (Scsi_Cmnd * SCpnt); -extern void scsi_old_times_out (Scsi_Cmnd * SCpnt); -#endif - -/* Can't see clean way to abort a command so shorten timeout to 1 jiffy */ -static void sg_shorten_timeout(Scsi_Request * srpnt) -{ -#if 0 /* scsi_syms.c is very miserly about exported functions */ - scsi_delete_timer(scpnt); - if (! scpnt) - return; - scpnt->timeout_per_command = 1; /* try 1 jiffy (perhaps 0 jiffies) */ - if (scpnt->host->hostt->use_new_eh_code) - scsi_add_timer(scpnt, scpnt->timeout_per_command, scsi_times_out); - else - scsi_add_timer(scpnt, scpnt->timeout_per_command, - scsi_old_times_out); -#else - scsi_sleep(HZ); /* just sleep 1 second and hope ... */ -#endif -} - static int sg_start_req(Sg_request * srp) { int res; @@ -1367,9 +1390,8 @@ SCSI_LOG_TIMEOUT(4, printk("sg_start_req: dxfer_len=%d\n", dxfer_len)); if ((dxfer_len <= 0) || (dxfer_dir == SG_DXFER_NONE)) return 0; - if ((hp->flags & SG_FLAG_DIRECT_IO) && - (dxfer_dir != SG_DXFER_UNKNOWN) && - (0 == hp->iovec_count) && + if (sg_allow_dio && (hp->flags & SG_FLAG_DIRECT_IO) && + (dxfer_dir != SG_DXFER_UNKNOWN) && (0 == hp->iovec_count) && (! sfp->parentdp->device->host->unchecked_isa_dma)) { res = sg_build_dir(srp, sfp, dxfer_len); if (res <= 0) /* -ve -> error, 0 -> done, 1 -> try indirect */ @@ -1427,7 +1449,7 @@ static void sg_unmap_and(Sg_scatter_hold * schp, int free_also) { -#ifdef SG_ALLOW_DIO +#ifdef SG_ALLOW_DIO_CODE if (schp && schp->kiobp) { if (schp->mapped) { unmap_kiobuf(schp->kiobp); @@ -1443,7 +1465,7 @@ static int sg_build_dir(Sg_request * srp, Sg_fd * sfp, int dxfer_len) { -#ifdef SG_ALLOW_DIO +#ifdef SG_ALLOW_DIO_CODE int res, k, split, offset, num, mx_sc_elems, rem_sz; struct kiobuf * kp; char * mem_src_arr; @@ -1519,7 +1541,7 @@ return 0; #else return 1; -#endif /* SG_ALLOW_DIO */ +#endif /* SG_ALLOW_DIO_CODE */ } static int sg_build_indi(Sg_scatter_hold * schp, Sg_fd * sfp, int buff_size) @@ -1629,7 +1651,7 @@ if (iovec_count) { onum = iovec_count; if ((k = verify_area(VERIFY_READ, hp->dxferp, - size_sg_iovec * onum))) + SZ_SG_IOVEC * onum))) return k; } else @@ -1707,8 +1729,8 @@ } else { __copy_from_user(&u_iovec, - (unsigned char *)hp->dxferp + (ind * size_sg_iovec), - size_sg_iovec); + (unsigned char *)hp->dxferp + (ind * SZ_SG_IOVEC), + SZ_SG_IOVEC); p = (unsigned char *)u_iovec.iov_base; count = (int)u_iovec.iov_len; } @@ -1778,7 +1800,7 @@ if (iovec_count) { onum = iovec_count; if ((k = verify_area(VERIFY_READ, hp->dxferp, - size_sg_iovec * onum))) + SZ_SG_IOVEC * onum))) return k; } else @@ -2124,6 +2146,34 @@ return sfp; } +static void __sg_remove_sfp(Sg_device * sdp, Sg_fd * sfp) +{ + Sg_fd * fp; + Sg_fd * prev_fp; + + prev_fp = sdp->headfp; + if (sfp == prev_fp) + sdp->headfp = prev_fp->nextfp; + else { + while ((fp = prev_fp->nextfp)) { + if (sfp == fp) { + prev_fp->nextfp = fp->nextfp; + break; + } + prev_fp = fp; + } + } + if (sfp->reserve.bufflen > 0) { + SCSI_LOG_TIMEOUT(6, printk("__sg_remove_sfp: bufflen=%d, k_use_sg=%d\n", + (int)sfp->reserve.bufflen, (int)sfp->reserve.k_use_sg)); + sg_remove_scat(&sfp->reserve); + } + sfp->parentdp = NULL; + SCSI_LOG_TIMEOUT(6, printk("__sg_remove_sfp: sfp=0x%p\n", sfp)); + sg_low_free((char *)sfp, sizeof(Sg_fd), sfp->fd_mem_src); +} + +/* Returns 0 in normal case, 1 when detached and sdp object removed */ static int sg_remove_sfp(Sg_device * sdp, Sg_fd * sfp) { Sg_request * srp; @@ -2131,44 +2181,18 @@ int dirty = 0; int res = 0; - srp = sfp->headrp; - if (srp) { - while (srp) { - tsrp = srp->nextrp; - if (srp->done) - sg_finish_rem_req(srp); - else - ++dirty; - srp = tsrp; - } + for (srp = sfp->headrp; srp; srp = tsrp) { + tsrp = srp->nextrp; + if (srp->done) + sg_finish_rem_req(srp); + else + ++dirty; } if (0 == dirty) { - Sg_fd * fp; - Sg_fd * prev_fp; unsigned long iflags; write_lock_irqsave(&sg_dev_arr_lock, iflags); - prev_fp = sdp->headfp; - if (sfp == prev_fp) - sdp->headfp = prev_fp->nextfp; - else { - while ((fp = prev_fp->nextfp)) { - if (sfp == fp) { - prev_fp->nextfp = fp->nextfp; - break; - } - prev_fp = fp; - } - } - write_unlock_irqrestore(&sg_dev_arr_lock, iflags); - if (sfp->reserve.bufflen > 0) { -SCSI_LOG_TIMEOUT(6, printk("sg_remove_sfp: bufflen=%d, k_use_sg=%d\n", - (int)sfp->reserve.bufflen, (int)sfp->reserve.k_use_sg)); - sg_remove_scat(&sfp->reserve); - } - sfp->parentdp = NULL; - SCSI_LOG_TIMEOUT(6, printk("sg_remove_sfp: sfp=0x%p\n", sfp)); - sg_low_free((char *)sfp, sizeof(Sg_fd), sfp->fd_mem_src); + __sg_remove_sfp(sdp, sfp); if (sdp->detached && (NULL == sdp->headfp)) { int k, maxd; @@ -2180,11 +2204,17 @@ if (k < maxd) sg_dev_arr[k] = NULL; kfree((char *)sdp); + res = 1; } - res = 1; + write_unlock_irqrestore(&sg_dev_arr_lock, iflags); } else { sfp->closed = 1; /* flag dirty state on this fd */ + /* MOD_INC's to inhibit unloading sg and associated adapter driver */ + if (sg_template.module) + __MOD_INC_USE_COUNT(sg_template.module); + if (sdp->device->host->hostt->module) + __MOD_INC_USE_COUNT(sdp->device->host->hostt->module); SCSI_LOG_TIMEOUT(1, printk( "sg_remove_sfp: worrisome, %d writes pending\n", dirty)); } @@ -2203,31 +2233,15 @@ return srp ? 1 : 0; } -static int sg_dio_in_use(Sg_fd * sfp) -{ - const Sg_request * srp; - unsigned long iflags; - - read_lock_irqsave(&sfp->rq_list_lock, iflags); - for (srp = sfp->headrp; srp; srp = srp->nextrp) - if ((! srp->done) && srp->data.kiobp) break; - read_unlock_irqrestore(&sfp->rq_list_lock, iflags); - return srp ? 1 : 0; -} - /* If retSzp==NULL want exact size or fail */ -/* sg_low_malloc() should always be called from a process context allowing - GFP_KERNEL to be used instead of GFP_ATOMIC */ static char * sg_low_malloc(int rqSz, int lowDma, int mem_src, int * retSzp) { char * resp = NULL; - int page_mask = lowDma ? (GFP_KERNEL | GFP_DMA) : GFP_KERNEL; + int page_mask = lowDma ? (GFP_ATOMIC | GFP_DMA) : GFP_ATOMIC; if (rqSz <= 0) return resp; if (SG_HEAP_KMAL == mem_src) { - page_mask = lowDma ? (GFP_ATOMIC | GFP_DMA) : GFP_ATOMIC; - /* Seen kmalloc(..,GFP_KERNEL) hang for 40 secs! */ resp = kmalloc(rqSz, page_mask); if (resp && retSzp) *retSzp = rqSz; return resp; @@ -2355,7 +2369,7 @@ case SG_USER_MEM: break; /* nothing to do */ default: - printk("sg_low_free: bad mem_src=%d, buff=0x%p, rqSz=%df\n", + printk("sg_low_free: bad mem_src=%d, buff=0x%p, rqSz=%d\n", mem_src, buff, size); break; } @@ -2451,11 +2465,17 @@ static struct proc_dir_entry * sg_proc_sgp = NULL; -static const char * sg_proc_sg_dirname = "sg"; -static const char * sg_proc_leaf_names[] = {"def_reserved_size", "debug", - "devices", "device_hdr", "device_strs", - "hosts", "host_hdr", "host_strs", "version"}; +static char sg_proc_sg_dirname[] = "sg"; +static const char * sg_proc_leaf_names[] = {"allow_dio", "def_reserved_size", + "debug", "devices", "device_hdr", "device_strs", + "hosts", "host_hdr", "host_strs", "version"}; +static int sg_proc_adio_read(char * buffer, char ** start, off_t offset, + int size, int * eof, void * data); +static int sg_proc_adio_info(char * buffer, int * len, off_t * begin, + off_t offset, int size); +static int sg_proc_adio_write(struct file * filp, const char * buffer, + unsigned long count, void * data); static int sg_proc_dressz_read(char * buffer, char ** start, off_t offset, int size, int * eof, void * data); static int sg_proc_dressz_info(char * buffer, int * len, off_t * begin, @@ -2495,12 +2515,12 @@ static int sg_proc_version_info(char * buffer, int * len, off_t * begin, off_t offset, int size); static read_proc_t * sg_proc_leaf_reads[] = { - sg_proc_dressz_read, sg_proc_debug_read, + sg_proc_adio_read, sg_proc_dressz_read, sg_proc_debug_read, sg_proc_dev_read, sg_proc_devhdr_read, sg_proc_devstrs_read, sg_proc_host_read, sg_proc_hosthdr_read, sg_proc_hoststrs_read, sg_proc_version_read}; static write_proc_t * sg_proc_leaf_writes[] = { - sg_proc_dressz_write, 0, 0, 0, 0, 0, 0, 0, 0}; + sg_proc_adio_write, sg_proc_dressz_write, 0, 0, 0, 0, 0, 0, 0, 0}; #define PRINT_PROC(fmt,args...) \ do { \ @@ -2562,6 +2582,32 @@ remove_proc_entry(sg_proc_sg_dirname, proc_scsi); } +static int sg_proc_adio_read(char * buffer, char ** start, off_t offset, + int size, int * eof, void * data) +{ SG_PROC_READ_FN(sg_proc_adio_info); } + +static int sg_proc_adio_info(char * buffer, int * len, off_t * begin, + off_t offset, int size) +{ + PRINT_PROC("%d\n", sg_allow_dio); + return 1; +} + +static int sg_proc_adio_write(struct file * filp, const char * buffer, + unsigned long count, void * data) +{ + int num; + char buff[11]; + + if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) + return -EACCES; + num = (count < 10) ? count : 10; + copy_from_user(buff, buffer, num); + buff[num] = '\0'; + sg_allow_dio = simple_strtoul(buff, 0, 10) ? 1 : 0; + return count; +} + static int sg_proc_dressz_read(char * buffer, char ** start, off_t offset, int size, int * eof, void * data) { SG_PROC_READ_FN(sg_proc_dressz_info); } @@ -2580,7 +2626,7 @@ unsigned long k = ULONG_MAX; char buff[11]; - if (! capable(CAP_SYS_ADMIN)) + if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) return -EACCES; num = (count < 10) ? count : 10; copy_from_user(buff, buffer, num); @@ -2620,8 +2666,9 @@ Sg_request * srp; struct scsi_device * scsidp; int dev, k, m, blen, usg; - - if (! (scsidp = sdp->device)) { + + scsidp = sdp->device; + if (NULL == scsidp) { PRINT_PROC("device %d detached ??\n", j); continue; } @@ -2629,10 +2676,14 @@ if (sg_get_nth_sfp(sdp, 0)) { PRINT_PROC(" >>> device=sg%d ", dev); - PRINT_PROC("scsi%d chan=%d id=%d lun=%d em=%d sg_tablesize=%d" - " excl=%d\n", scsidp->host->host_no, scsidp->channel, - scsidp->id, scsidp->lun, scsidp->host->hostt->emulated, - sdp->sg_tablesize, sdp->exclude); + if (sdp->detached) + PRINT_PROC("detached pending close "); + else + PRINT_PROC("scsi%d chan=%d id=%d lun=%d em=%d", + scsidp->host->host_no, scsidp->channel, + scsidp->id, scsidp->lun, scsidp->host->hostt->emulated); + PRINT_PROC(" sg_tablesize=%d excl=%d\n", sdp->sg_tablesize, + sdp->exclude); } for (k = 0; (fp = sg_get_nth_sfp(sdp, k)); ++k) { PRINT_PROC(" FD(%d): timeout=%dms bufflen=%d " @@ -2683,13 +2734,14 @@ max_dev = sg_last_dev(); for (j = 0; j < max_dev; ++j) { sdp = sg_get_dev(j); - if (sdp && (scsidp = sdp->device)) - PRINT_PROC("%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n", + if (sdp && (scsidp = sdp->device) && (! sdp->detached)) + PRINT_PROC("%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n", scsidp->host->host_no, scsidp->channel, scsidp->id, scsidp->lun, (int)scsidp->type, (int)scsidp->access_count, - (int)scsidp->queue_depth, (int)scsidp->device_busy); + (int)scsidp->queue_depth, (int)scsidp->device_busy, + (int)scsidp->online); else - PRINT_PROC("-1\t-1\t-1\t-1\t-1\t-1\t-1\t-1\n"); + PRINT_PROC("-1\t-1\t-1\t-1\t-1\t-1\t-1\t-1\t-1\n"); } return 1; } @@ -2701,7 +2753,7 @@ static int sg_proc_devhdr_info(char * buffer, int * len, off_t * begin, off_t offset, int size) { - PRINT_PROC("host\tchan\tid\tlun\ttype\tbopens\tqdepth\tbusy\n"); + PRINT_PROC("host\tchan\tid\tlun\ttype\tbopens\tqdepth\tbusy\tonline\n"); return 1; } @@ -2719,7 +2771,7 @@ max_dev = sg_last_dev(); for (j = 0; j < max_dev; ++j) { sdp = sg_get_dev(j); - if (sdp && (scsidp = sdp->device)) + if (sdp && (scsidp = sdp->device) && (! sdp->detached)) PRINT_PROC("%8.8s\t%16.16s\t%4.4s\n", scsidp->vendor, scsidp->model, scsidp->rev); else