X-Git-Url: http://git.rot13.org/?a=blobdiff_plain;f=drivers%2Fscsi%2Fst.c;h=6b85f84c839756680e8373ad45532ad946f8fce8;hb=8c65b4a60450590e79a28e9717ceffa9e4debb3f;hp=0a7839db57529c700e71dd1699eb45f273d44ccb;hpb=3cb904caa97f5c193df21b8cdf780a826564baff;p=powerpc.git diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c index 0a7839db57..6b85f84c83 100644 --- a/drivers/scsi/st.c +++ b/drivers/scsi/st.c @@ -17,7 +17,7 @@ Last modified: 18-JAN-1998 Richard Gooch Devfs support */ -static char *verstr = "20050501"; +static char *verstr = "20050830"; #include @@ -219,6 +219,12 @@ static int switch_partition(struct scsi_tape *); static int st_int_ioctl(struct scsi_tape *, unsigned int, unsigned long); +static void scsi_tape_release(struct kref *); + +#define to_scsi_tape(obj) container_of(obj, struct scsi_tape, kref) + +static DECLARE_MUTEX(st_ref_sem); + #include "osst_detect.h" #ifndef SIGS_FROM_OSST @@ -230,6 +236,46 @@ static int st_int_ioctl(struct scsi_tape *, unsigned int, unsigned long); {"OnStream", "FW-", "", "osst"} #endif +static struct scsi_tape *scsi_tape_get(int dev) +{ + struct scsi_tape *STp = NULL; + + down(&st_ref_sem); + write_lock(&st_dev_arr_lock); + + if (dev < st_dev_max && scsi_tapes != NULL) + STp = scsi_tapes[dev]; + if (!STp) goto out; + + kref_get(&STp->kref); + + if (!STp->device) + goto out_put; + + if (scsi_device_get(STp->device)) + goto out_put; + + goto out; + +out_put: + kref_put(&STp->kref, scsi_tape_release); + STp = NULL; +out: + write_unlock(&st_dev_arr_lock); + up(&st_ref_sem); + return STp; +} + +static void scsi_tape_put(struct scsi_tape *STp) +{ + struct scsi_device *sdev = STp->device; + + down(&st_ref_sem); + kref_put(&STp->kref, scsi_tape_release); + scsi_device_put(sdev); + up(&st_ref_sem); +} + struct st_reject_data { char *vendor; char *model; @@ -311,7 +357,7 @@ static int st_chk_result(struct scsi_tape *STp, struct scsi_request * SRpnt) return 0; cmdstatp = &STp->buffer->cmdstat; - st_analyze_sense(STp->buffer->last_SRpnt, cmdstatp); + st_analyze_sense(SRpnt, cmdstatp); if (cmdstatp->have_sense) scode = STp->buffer->cmdstat.sense_hdr.sense_key; @@ -399,10 +445,10 @@ static void st_sleep_done(struct scsi_cmnd * SCpnt) (STp->buffer)->cmdstat.midlevel_result = SCpnt->result; SCpnt->request->rq_status = RQ_SCSI_DONE; - (STp->buffer)->last_SRpnt = SCpnt->sc_request; DEB( STp->write_pending = 0; ) - complete(SCpnt->request->waiting); + if (SCpnt->request->waiting) + complete(SCpnt->request->waiting); } /* Do the scsi command. Waits until command performed if do_wait is true. @@ -412,8 +458,20 @@ static struct scsi_request * st_do_scsi(struct scsi_request * SRpnt, struct scsi_tape * STp, unsigned char *cmd, int bytes, int direction, int timeout, int retries, int do_wait) { + struct completion *waiting; unsigned char *bp; + /* if async, make sure there's no command outstanding */ + if (!do_wait && ((STp->buffer)->last_SRpnt)) { + printk(KERN_ERR "%s: Async command already active.\n", + tape_name(STp)); + if (signal_pending(current)) + (STp->buffer)->syscall_result = (-EINTR); + else + (STp->buffer)->syscall_result = (-EBUSY); + return NULL; + } + if (SRpnt == NULL) { SRpnt = scsi_allocate_request(STp->device, GFP_ATOMIC); if (SRpnt == NULL) { @@ -427,7 +485,13 @@ st_do_scsi(struct scsi_request * SRpnt, struct scsi_tape * STp, unsigned char *c } } - init_completion(&STp->wait); + /* If async IO, set last_SRpnt. This ptr tells write_behind_check + which IO is outstanding. It's nulled out when the IO completes. */ + if (!do_wait) + (STp->buffer)->last_SRpnt = SRpnt; + + waiting = &STp->wait; + init_completion(waiting); SRpnt->sr_use_sg = STp->buffer->do_dio || (bytes > (STp->buffer)->frp[0].length); if (SRpnt->sr_use_sg) { if (!STp->buffer->do_dio) @@ -438,17 +502,20 @@ st_do_scsi(struct scsi_request * SRpnt, struct scsi_tape * STp, unsigned char *c bp = (STp->buffer)->b_data; SRpnt->sr_data_direction = direction; SRpnt->sr_cmd_len = 0; - SRpnt->sr_request->waiting = &(STp->wait); + SRpnt->sr_request->waiting = waiting; SRpnt->sr_request->rq_status = RQ_SCSI_BUSY; SRpnt->sr_request->rq_disk = STp->disk; + SRpnt->sr_request->end_io = blk_end_sync_rq; STp->buffer->cmdstat.have_sense = 0; scsi_do_req(SRpnt, (void *) cmd, bp, bytes, st_sleep_done, timeout, retries); if (do_wait) { - wait_for_completion(SRpnt->sr_request->waiting); + wait_for_completion(waiting); SRpnt->sr_request->waiting = NULL; + if (SRpnt->sr_request->rq_status != RQ_SCSI_DONE) + SRpnt->sr_result |= (DRIVER_ERROR << 24); (STp->buffer)->syscall_result = st_chk_result(STp, SRpnt); } return SRpnt; @@ -465,6 +532,7 @@ static int write_behind_check(struct scsi_tape * STp) struct st_buffer *STbuffer; struct st_partstat *STps; struct st_cmdstatus *cmdstatp; + struct scsi_request *SRpnt; STbuffer = STp->buffer; if (!STbuffer->writing) @@ -478,10 +546,14 @@ static int write_behind_check(struct scsi_tape * STp) ) /* end DEB */ wait_for_completion(&(STp->wait)); - (STp->buffer)->last_SRpnt->sr_request->waiting = NULL; + SRpnt = STbuffer->last_SRpnt; + STbuffer->last_SRpnt = NULL; + SRpnt->sr_request->waiting = NULL; + if (SRpnt->sr_request->rq_status != RQ_SCSI_DONE) + SRpnt->sr_result |= (DRIVER_ERROR << 24); - (STp->buffer)->syscall_result = st_chk_result(STp, (STp->buffer)->last_SRpnt); - scsi_release_request((STp->buffer)->last_SRpnt); + (STp->buffer)->syscall_result = st_chk_result(STp, SRpnt); + scsi_release_request(SRpnt); STbuffer->buffer_bytes -= STbuffer->writing; STps = &(STp->ps[STp->partition]); @@ -1055,25 +1127,20 @@ static int st_open(struct inode *inode, struct file *filp) */ filp->f_mode &= ~(FMODE_PREAD | FMODE_PWRITE); + if (!(STp = scsi_tape_get(dev))) + return -ENXIO; + write_lock(&st_dev_arr_lock); - if (dev >= st_dev_max || scsi_tapes == NULL || - ((STp = scsi_tapes[dev]) == NULL)) { - write_unlock(&st_dev_arr_lock); - return (-ENXIO); - } filp->private_data = STp; name = tape_name(STp); if (STp->in_use) { write_unlock(&st_dev_arr_lock); + scsi_tape_put(STp); DEB( printk(ST_DEB_MSG "%s: Device already in use.\n", name); ) return (-EBUSY); } - if(scsi_device_get(STp->device)) { - write_unlock(&st_dev_arr_lock); - return (-ENXIO); - } STp->in_use = 1; write_unlock(&st_dev_arr_lock); STp->rew_at_close = STp->autorew_dev = (iminor(inode) & 0x80) == 0; @@ -1118,7 +1185,7 @@ static int st_open(struct inode *inode, struct file *filp) err_out: normalize_buffer(STp->buffer); STp->in_use = 0; - scsi_device_put(STp->device); + scsi_tape_put(STp); return retval; } @@ -1250,7 +1317,7 @@ static int st_release(struct inode *inode, struct file *filp) write_lock(&st_dev_arr_lock); STp->in_use = 0; write_unlock(&st_dev_arr_lock); - scsi_device_put(STp->device); + scsi_tape_put(STp); return result; } @@ -3510,7 +3577,8 @@ static long st_compat_ioctl(struct file *file, unsigned int cmd, unsigned long a static struct st_buffer * new_tape_buffer(int from_initialization, int need_dma, int max_sg) { - int i, priority, got = 0, segs = 0; + int i, got = 0, segs = 0; + gfp_t priority; struct st_buffer *tb; if (from_initialization) @@ -3543,7 +3611,8 @@ static struct st_buffer * /* Try to allocate enough space in the tape buffer */ static int enlarge_buffer(struct st_buffer * STbuffer, int new_size, int need_dma) { - int segs, nbr, max_segs, b_size, priority, order, got; + int segs, nbr, max_segs, b_size, order, got; + gfp_t priority; if (new_size <= STbuffer->buffer_size) return 1; @@ -3818,9 +3887,7 @@ static int st_probe(struct device *dev) if (SDp->type != TYPE_TAPE) return -ENODEV; if ((stp = st_incompatible(SDp))) { - printk(KERN_INFO - "st: Found incompatible tape at scsi%d, channel %d, id %d, lun %d\n", - SDp->host->host_no, SDp->channel, SDp->id, SDp->lun); + sdev_printk(KERN_INFO, SDp, "Found incompatible tape\n"); printk(KERN_INFO "st: The suggested driver is %s.\n", stp); return -ENODEV; } @@ -3887,6 +3954,7 @@ static int st_probe(struct device *dev) goto out_put_disk; } memset(tpnt, 0, sizeof(struct scsi_tape)); + kref_init(&tpnt->kref); tpnt->disk = disk; sprintf(disk->disk_name, "st%d", i); disk->private_data = &tpnt->driver; @@ -3902,6 +3970,7 @@ static int st_probe(struct device *dev) tpnt->tape_type = MT_ISSCSI2; tpnt->buffer = buffer; + tpnt->buffer->last_SRpnt = NULL; tpnt->inited = 0; tpnt->dirty = 0; @@ -4006,9 +4075,8 @@ static int st_probe(struct device *dev) } disk->number = devfs_register_tape(SDp->devfs_name); - printk(KERN_WARNING - "Attached scsi tape %s at scsi%d, channel %d, id %d, lun %d\n", - tape_name(tpnt), SDp->host->host_no, SDp->channel, SDp->id, SDp->lun); + sdev_printk(KERN_WARNING, SDp, + "Attached scsi tape %s", tape_name(tpnt)); printk(KERN_WARNING "%s: try direct i/o: %s (alignment %d B), max page reachable by HBA %lu\n", tape_name(tpnt), tpnt->try_dio ? "yes" : "no", queue_dma_alignment(SDp->request_queue) + 1, tpnt->max_pfn); @@ -4076,15 +4144,10 @@ static int st_remove(struct device *dev) tpnt->modes[mode].cdevs[j] = NULL; } } - tpnt->device = NULL; - if (tpnt->buffer) { - tpnt->buffer->orig_frp_segs = 0; - normalize_buffer(tpnt->buffer); - kfree(tpnt->buffer); - } - put_disk(tpnt->disk); - kfree(tpnt); + down(&st_ref_sem); + kref_put(&tpnt->kref, scsi_tape_release); + up(&st_ref_sem); return 0; } } @@ -4093,6 +4156,34 @@ static int st_remove(struct device *dev) return 0; } +/** + * scsi_tape_release - Called to free the Scsi_Tape structure + * @kref: pointer to embedded kref + * + * st_ref_sem must be held entering this routine. Because it is + * called on last put, you should always use the scsi_tape_get() + * scsi_tape_put() helpers which manipulate the semaphore directly + * and never do a direct kref_put(). + **/ +static void scsi_tape_release(struct kref *kref) +{ + struct scsi_tape *tpnt = to_scsi_tape(kref); + struct gendisk *disk = tpnt->disk; + + tpnt->device = NULL; + + if (tpnt->buffer) { + tpnt->buffer->orig_frp_segs = 0; + normalize_buffer(tpnt->buffer); + kfree(tpnt->buffer); + } + + disk->private_data = NULL; + put_disk(disk); + kfree(tpnt); + return; +} + static void st_intr(struct scsi_cmnd *SCpnt) { scsi_io_completion(SCpnt, (SCpnt->result ? 0: SCpnt->bufflen), 1); @@ -4114,6 +4205,7 @@ static int st_init_command(struct scsi_cmnd *SCpnt) return 0; memcpy(SCpnt->cmnd, rq->cmd, sizeof(SCpnt->cmnd)); + SCpnt->cmd_len = rq->cmd_len; if (rq_data_dir(rq) == WRITE) SCpnt->sc_data_direction = DMA_TO_DEVICE; @@ -4282,7 +4374,7 @@ static void do_create_class_files(struct scsi_tape *STp, int dev_num, int mode) snprintf(name, 10, "%s%s%s", rew ? "n" : "", STp->disk->disk_name, st_formats[i]); st_class_member = - class_device_create(st_sysfs_class, + class_device_create(st_sysfs_class, NULL, MKDEV(SCSI_TAPE_MAJOR, TAPE_MINOR(dev_num, mode, rew)), &STp->device->sdev_gendev, "%s", name); @@ -4348,12 +4440,12 @@ static int st_map_user_pages(struct scatterlist *sgl, const unsigned int max_pag static int sgl_map_user_pages(struct scatterlist *sgl, const unsigned int max_pages, unsigned long uaddr, size_t count, int rw) { + unsigned long end = (uaddr + count + PAGE_SIZE - 1) >> PAGE_SHIFT; + unsigned long start = uaddr >> PAGE_SHIFT; + const int nr_pages = end - start; int res, i, j; - unsigned int nr_pages; struct page **pages; - nr_pages = ((uaddr & ~PAGE_MASK) + count + ~PAGE_MASK) >> PAGE_SHIFT; - /* User attempted Overflow! */ if ((uaddr + count) < uaddr) return -EINVAL; @@ -4431,12 +4523,16 @@ static int sgl_unmap_user_pages(struct scatterlist *sgl, const unsigned int nr_p int i; for (i=0; i < nr_pages; i++) { - if (dirtied && !PageReserved(sgl[i].page)) - SetPageDirty(sgl[i].page); + struct page *page = sgl[i].page; + + /* XXX: just for debug. Remove when PageReserved is removed */ + BUG_ON(PageReserved(page)); + if (dirtied) + SetPageDirty(page); /* FIXME: cache flush missing for rw==READ * FIXME: call the correct reference counting function */ - page_cache_release(sgl[i].page); + page_cache_release(page); } return 0;