aboutsummaryrefslogtreecommitdiff
path: root/drivers/scsi/sg.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/scsi/sg.c')
-rw-r--r--drivers/scsi/sg.c271
1 files changed, 121 insertions, 150 deletions
diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c
index 85d38940a6c..7238b2dfc49 100644
--- a/drivers/scsi/sg.c
+++ b/drivers/scsi/sg.c
@@ -43,6 +43,7 @@ static int sg_version_num = 30534; /* 2 digits for each component */
#include <linux/poll.h>
#include <linux/moduleparam.h>
#include <linux/cdev.h>
+#include <linux/idr.h>
#include <linux/seq_file.h>
#include <linux/blkdev.h>
#include <linux/delay.h>
@@ -99,12 +100,11 @@ static int scatter_elem_sz_prev = SG_SCATTER_SZ;
#define SG_SECTOR_SZ 512
#define SG_SECTOR_MSK (SG_SECTOR_SZ - 1)
-#define SG_DEV_ARR_LUMP 32 /* amount to over allocate sg_dev_arr by */
-
static int sg_add(struct class_device *, struct class_interface *);
static void sg_remove(struct class_device *, struct class_interface *);
-static DEFINE_RWLOCK(sg_dev_arr_lock); /* Also used to lock
+static DEFINE_IDR(sg_index_idr);
+static DEFINE_RWLOCK(sg_index_lock); /* Also used to lock
file descriptor list for device */
static struct class_interface sg_interface = {
@@ -114,7 +114,7 @@ static struct class_interface sg_interface = {
typedef struct sg_scatter_hold { /* holding area for scsi scatter gather info */
unsigned short k_use_sg; /* Count of kernel scatter-gather pieces */
- unsigned short sglist_len; /* size of malloc'd scatter-gather list ++ */
+ unsigned sglist_len; /* size of malloc'd scatter-gather list ++ */
unsigned bufflen; /* Size of (aggregate) data buffer */
unsigned b_malloc_len; /* actual len malloc'ed in buffer */
struct scatterlist *buffer;/* scatter list */
@@ -162,6 +162,7 @@ typedef struct sg_device { /* holds the state of each scsi generic device */
struct scsi_device *device;
wait_queue_head_t o_excl_wait; /* queue open() when O_EXCL in use */
int sg_tablesize; /* adapter's max scatter-gather table size */
+ u32 index; /* device index number */
Sg_fd *headfp; /* first open fd belonging to this device */
volatile char detached; /* 0->attached, 1->detached pending removal */
volatile char exclude; /* opened for exclusive access */
@@ -209,10 +210,6 @@ static Sg_device *sg_get_dev(int dev);
static int sg_last_dev(void);
#endif
-static Sg_device **sg_dev_arr = NULL;
-static int sg_dev_max;
-static int sg_nr_dev;
-
#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)
@@ -1168,7 +1165,7 @@ sg_vma_nopage(struct vm_area_struct *vma, unsigned long addr, int *type)
sg = rsv_schp->buffer;
sa = vma->vm_start;
for (k = 0; (k < rsv_schp->k_use_sg) && (sa < vma->vm_end);
- ++k, ++sg) {
+ ++k, sg = sg_next(sg)) {
len = vma->vm_end - sa;
len = (len < sg->length) ? len : sg->length;
if (offset < len) {
@@ -1212,7 +1209,7 @@ sg_mmap(struct file *filp, struct vm_area_struct *vma)
sa = vma->vm_start;
sg = rsv_schp->buffer;
for (k = 0; (k < rsv_schp->k_use_sg) && (sa < vma->vm_end);
- ++k, ++sg) {
+ ++k, sg = sg_next(sg)) {
len = vma->vm_end - sa;
len = (len < sg->length) ? len : sg->length;
sa += len;
@@ -1331,40 +1328,35 @@ static struct class *sg_sysfs_class;
static int sg_sysfs_valid = 0;
-static int sg_alloc(struct gendisk *disk, struct scsi_device *scsidp)
+static Sg_device *sg_alloc(struct gendisk *disk, struct scsi_device *scsidp)
{
struct request_queue *q = scsidp->request_queue;
Sg_device *sdp;
unsigned long iflags;
- void *old_sg_dev_arr = NULL;
- int k, error;
+ int error;
+ u32 k;
sdp = kzalloc(sizeof(Sg_device), GFP_KERNEL);
if (!sdp) {
printk(KERN_WARNING "kmalloc Sg_device failure\n");
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
+ }
+ error = -ENOMEM;
+ if (!idr_pre_get(&sg_index_idr, GFP_KERNEL)) {
+ printk(KERN_WARNING "idr expansion Sg_device failure\n");
+ goto out;
}
- write_lock_irqsave(&sg_dev_arr_lock, iflags);
- if (unlikely(sg_nr_dev >= sg_dev_max)) { /* try to resize */
- Sg_device **tmp_da;
- int tmp_dev_max = sg_nr_dev + SG_DEV_ARR_LUMP;
- write_unlock_irqrestore(&sg_dev_arr_lock, iflags);
-
- tmp_da = kzalloc(tmp_dev_max * sizeof(Sg_device *), GFP_KERNEL);
- if (unlikely(!tmp_da))
- goto expand_failed;
+ write_lock_irqsave(&sg_index_lock, iflags);
+ error = idr_get_new(&sg_index_idr, sdp, &k);
+ write_unlock_irqrestore(&sg_index_lock, iflags);
- write_lock_irqsave(&sg_dev_arr_lock, iflags);
- memcpy(tmp_da, sg_dev_arr, sg_dev_max * sizeof(Sg_device *));
- old_sg_dev_arr = sg_dev_arr;
- sg_dev_arr = tmp_da;
- sg_dev_max = tmp_dev_max;
+ if (error) {
+ printk(KERN_WARNING "idr allocation Sg_device failure: %d\n",
+ error);
+ goto out;
}
- for (k = 0; k < sg_dev_max; k++)
- if (!sg_dev_arr[k])
- break;
if (unlikely(k >= SG_MAX_DEVS))
goto overflow;
@@ -1375,25 +1367,17 @@ static int sg_alloc(struct gendisk *disk, struct scsi_device *scsidp)
sdp->device = scsidp;
init_waitqueue_head(&sdp->o_excl_wait);
sdp->sg_tablesize = min(q->max_hw_segments, q->max_phys_segments);
+ sdp->index = k;
- sg_nr_dev++;
- sg_dev_arr[k] = sdp;
- write_unlock_irqrestore(&sg_dev_arr_lock, iflags);
- error = k;
-
+ error = 0;
out:
- if (error < 0)
+ if (error) {
kfree(sdp);
- kfree(old_sg_dev_arr);
- return error;
-
- expand_failed:
- printk(KERN_WARNING "sg_alloc: device array cannot be resized\n");
- error = -ENOMEM;
- goto out;
+ return ERR_PTR(error);
+ }
+ return sdp;
overflow:
- write_unlock_irqrestore(&sg_dev_arr_lock, iflags);
sdev_printk(KERN_WARNING, scsidp,
"Unable to attach sg device type=%d, minor "
"number exceeds %d\n", scsidp->type, SG_MAX_DEVS - 1);
@@ -1408,7 +1392,7 @@ sg_add(struct class_device *cl_dev, struct class_interface *cl_intf)
struct gendisk *disk;
Sg_device *sdp = NULL;
struct cdev * cdev = NULL;
- int error, k;
+ int error;
unsigned long iflags;
disk = alloc_disk(1);
@@ -1427,15 +1411,15 @@ sg_add(struct class_device *cl_dev, struct class_interface *cl_intf)
cdev->owner = THIS_MODULE;
cdev->ops = &sg_fops;
- error = sg_alloc(disk, scsidp);
- if (error < 0) {
+ sdp = sg_alloc(disk, scsidp);
+ if (IS_ERR(sdp)) {
printk(KERN_WARNING "sg_alloc failed\n");
+ error = PTR_ERR(sdp);
goto out;
}
- k = error;
- sdp = sg_dev_arr[k];
- error = cdev_add(cdev, MKDEV(SCSI_GENERIC_MAJOR, k), 1);
+ class_set_devdata(cl_dev, sdp);
+ error = cdev_add(cdev, MKDEV(SCSI_GENERIC_MAJOR, sdp->index), 1);
if (error)
goto cdev_add_err;
@@ -1444,8 +1428,8 @@ sg_add(struct class_device *cl_dev, struct class_interface *cl_intf)
struct class_device * sg_class_member;
sg_class_member = class_device_create(sg_sysfs_class, NULL,
- MKDEV(SCSI_GENERIC_MAJOR, k),
- cl_dev->dev, "%s",
+ MKDEV(SCSI_GENERIC_MAJOR, sdp->index),
+ cl_dev->dev, "%s",
disk->disk_name);
if (IS_ERR(sg_class_member))
printk(KERN_WARNING "sg_add: "
@@ -1455,21 +1439,21 @@ sg_add(struct class_device *cl_dev, struct class_interface *cl_intf)
&sg_class_member->kobj, "generic");
if (error)
printk(KERN_ERR "sg_add: unable to make symlink "
- "'generic' back to sg%d\n", k);
+ "'generic' back to sg%d\n", sdp->index);
} else
- printk(KERN_WARNING "sg_add: sg_sys INvalid\n");
+ printk(KERN_WARNING "sg_add: sg_sys Invalid\n");
sdev_printk(KERN_NOTICE, scsidp,
- "Attached scsi generic sg%d type %d\n", k,scsidp->type);
+ "Attached scsi generic sg%d type %d\n", sdp->index,
+ scsidp->type);
return 0;
cdev_add_err:
- write_lock_irqsave(&sg_dev_arr_lock, iflags);
- kfree(sg_dev_arr[k]);
- sg_dev_arr[k] = NULL;
- sg_nr_dev--;
- write_unlock_irqrestore(&sg_dev_arr_lock, iflags);
+ write_lock_irqsave(&sg_index_lock, iflags);
+ idr_remove(&sg_index_idr, sdp->index);
+ write_unlock_irqrestore(&sg_index_lock, iflags);
+ kfree(sdp);
out:
put_disk(disk);
@@ -1482,64 +1466,56 @@ static void
sg_remove(struct class_device *cl_dev, struct class_interface *cl_intf)
{
struct scsi_device *scsidp = to_scsi_device(cl_dev->dev);
- Sg_device *sdp = NULL;
+ Sg_device *sdp = class_get_devdata(cl_dev);
unsigned long iflags;
Sg_fd *sfp;
Sg_fd *tsfp;
Sg_request *srp;
Sg_request *tsrp;
- int k, delay;
+ int delay;
- if (NULL == sg_dev_arr)
+ if (!sdp)
return;
+
delay = 0;
- write_lock_irqsave(&sg_dev_arr_lock, iflags);
- for (k = 0; k < sg_dev_max; k++) {
- sdp = sg_dev_arr[k];
- if ((NULL == sdp) || (sdp->device != scsidp))
- continue; /* dirty but lowers nesting */
- if (sdp->headfp) {
- 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 == sg_srp_done(srp, sfp)))
- sg_finish_rem_req(srp);
- }
- if (sfp->closed) {
- scsi_device_put(sdp->device);
- __sg_remove_sfp(sdp, sfp);
- } else {
- delay = 1;
- wake_up_interruptible(&sfp->read_wait);
- kill_fasync(&sfp->async_qp, SIGPOLL,
- POLL_HUP);
- }
+ write_lock_irqsave(&sg_index_lock, iflags);
+ if (sdp->headfp) {
+ 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 == sg_srp_done(srp, sfp)))
+ sg_finish_rem_req(srp);
}
- SCSI_LOG_TIMEOUT(3, printk("sg_remove: dev=%d, dirty\n", k));
- if (NULL == sdp->headfp) {
- sg_dev_arr[k] = NULL;
+ if (sfp->closed) {
+ scsi_device_put(sdp->device);
+ __sg_remove_sfp(sdp, sfp);
+ } else {
+ delay = 1;
+ wake_up_interruptible(&sfp->read_wait);
+ kill_fasync(&sfp->async_qp, SIGPOLL,
+ POLL_HUP);
}
- } else { /* nothing active, simple case */
- SCSI_LOG_TIMEOUT(3, printk("sg_remove: dev=%d\n", k));
- sg_dev_arr[k] = NULL;
}
- sg_nr_dev--;
- break;
- }
- write_unlock_irqrestore(&sg_dev_arr_lock, iflags);
-
- if (sdp) {
- sysfs_remove_link(&scsidp->sdev_gendev.kobj, "generic");
- class_device_destroy(sg_sysfs_class, MKDEV(SCSI_GENERIC_MAJOR, k));
- cdev_del(sdp->cdev);
- sdp->cdev = NULL;
- put_disk(sdp->disk);
- sdp->disk = NULL;
- if (NULL == sdp->headfp)
- kfree((char *) sdp);
- }
+ SCSI_LOG_TIMEOUT(3, printk("sg_remove: dev=%d, dirty\n", sdp->index));
+ if (NULL == sdp->headfp) {
+ idr_remove(&sg_index_idr, sdp->index);
+ }
+ } else { /* nothing active, simple case */
+ SCSI_LOG_TIMEOUT(3, printk("sg_remove: dev=%d\n", sdp->index));
+ idr_remove(&sg_index_idr, sdp->index);
+ }
+ write_unlock_irqrestore(&sg_index_lock, iflags);
+
+ sysfs_remove_link(&scsidp->sdev_gendev.kobj, "generic");
+ class_device_destroy(sg_sysfs_class, MKDEV(SCSI_GENERIC_MAJOR, sdp->index));
+ cdev_del(sdp->cdev);
+ sdp->cdev = NULL;
+ put_disk(sdp->disk);
+ sdp->disk = NULL;
+ if (NULL == sdp->headfp)
+ kfree(sdp);
if (delay)
msleep(10); /* dirty detach so delay device destruction */
@@ -1609,9 +1585,7 @@ exit_sg(void)
sg_sysfs_valid = 0;
unregister_chrdev_region(MKDEV(SCSI_GENERIC_MAJOR, 0),
SG_MAX_DEVS);
- kfree((char *)sg_dev_arr);
- sg_dev_arr = NULL;
- sg_dev_max = 0;
+ idr_destroy(&sg_index_idr);
}
static int
@@ -1866,7 +1840,7 @@ sg_build_indirect(Sg_scatter_hold * schp, Sg_fd * sfp, int buff_size)
}
for (k = 0, sg = schp->buffer, rem_sz = blk_size;
(rem_sz > 0) && (k < mx_sc_elems);
- ++k, rem_sz -= ret_sz, ++sg) {
+ ++k, rem_sz -= ret_sz, sg = sg_next(sg)) {
num = (rem_sz > scatter_elem_sz_prev) ?
scatter_elem_sz_prev : rem_sz;
@@ -1939,7 +1913,7 @@ sg_write_xfer(Sg_request * srp)
if (res)
return res;
- for (; p; ++sg, ksglen = sg->length,
+ for (; p; sg = sg_next(sg), ksglen = sg->length,
p = page_address(sg->page)) {
if (usglen <= 0)
break;
@@ -2018,7 +1992,7 @@ sg_remove_scat(Sg_scatter_hold * schp)
int k;
for (k = 0; (k < schp->k_use_sg) && sg->page;
- ++k, ++sg) {
+ ++k, sg = sg_next(sg)) {
SCSI_LOG_TIMEOUT(5, printk(
"sg_remove_scat: k=%d, pg=0x%p, len=%d\n",
k, sg->page, sg->length));
@@ -2071,7 +2045,7 @@ sg_read_xfer(Sg_request * srp)
if (res)
return res;
- for (; p; ++sg, ksglen = sg->length,
+ for (; p; sg = sg_next(sg), ksglen = sg->length,
p = page_address(sg->page)) {
if (usglen <= 0)
break;
@@ -2118,7 +2092,7 @@ sg_read_oxfer(Sg_request * srp, char __user *outp, int num_read_xfer)
if ((!outp) || (num_read_xfer <= 0))
return 0;
- for (k = 0; (k < schp->k_use_sg) && sg->page; ++k, ++sg) {
+ for (k = 0; (k < schp->k_use_sg) && sg->page; ++k, sg = sg_next(sg)) {
num = sg->length;
if (num > num_read_xfer) {
if (__copy_to_user(outp, page_address(sg->page),
@@ -2168,7 +2142,7 @@ sg_link_reserve(Sg_fd * sfp, Sg_request * srp, int size)
SCSI_LOG_TIMEOUT(4, printk("sg_link_reserve: size=%d\n", size));
rem = size;
- for (k = 0; k < rsv_schp->k_use_sg; ++k, ++sg) {
+ for (k = 0; k < rsv_schp->k_use_sg; ++k, sg = sg_next(sg)) {
num = sg->length;
if (rem <= num) {
sfp->save_scat_len = num;
@@ -2331,10 +2305,10 @@ sg_get_nth_sfp(Sg_device * sdp, int nth)
unsigned long iflags;
int k;
- read_lock_irqsave(&sg_dev_arr_lock, iflags);
+ read_lock_irqsave(&sg_index_lock, iflags);
for (k = 0, resp = sdp->headfp; resp && (k < nth);
++k, resp = resp->nextfp) ;
- read_unlock_irqrestore(&sg_dev_arr_lock, iflags);
+ read_unlock_irqrestore(&sg_index_lock, iflags);
return resp;
}
#endif
@@ -2361,7 +2335,7 @@ sg_add_sfp(Sg_device * sdp, int dev)
sfp->cmd_q = SG_DEF_COMMAND_Q;
sfp->keep_orphan = SG_DEF_KEEP_ORPHAN;
sfp->parentdp = sdp;
- write_lock_irqsave(&sg_dev_arr_lock, iflags);
+ write_lock_irqsave(&sg_index_lock, iflags);
if (!sdp->headfp)
sdp->headfp = sfp;
else { /* add to tail of existing list */
@@ -2370,7 +2344,7 @@ sg_add_sfp(Sg_device * sdp, int dev)
pfp = pfp->nextfp;
pfp->nextfp = sfp;
}
- write_unlock_irqrestore(&sg_dev_arr_lock, iflags);
+ write_unlock_irqrestore(&sg_index_lock, iflags);
SCSI_LOG_TIMEOUT(3, printk("sg_add_sfp: sfp=0x%p\n", sfp));
if (unlikely(sg_big_buff != def_reserved_size))
sg_big_buff = def_reserved_size;
@@ -2431,22 +2405,14 @@ sg_remove_sfp(Sg_device * sdp, Sg_fd * sfp)
if (0 == dirty) {
unsigned long iflags;
- write_lock_irqsave(&sg_dev_arr_lock, iflags);
+ write_lock_irqsave(&sg_index_lock, iflags);
__sg_remove_sfp(sdp, sfp);
if (sdp->detached && (NULL == sdp->headfp)) {
- int k, maxd;
-
- maxd = sg_dev_max;
- for (k = 0; k < maxd; ++k) {
- if (sdp == sg_dev_arr[k])
- break;
- }
- if (k < maxd)
- sg_dev_arr[k] = NULL;
- kfree((char *) sdp);
+ idr_remove(&sg_index_idr, sdp->index);
+ kfree(sdp);
res = 1;
}
- write_unlock_irqrestore(&sg_dev_arr_lock, iflags);
+ write_unlock_irqrestore(&sg_index_lock, iflags);
} else {
/* MOD_INC's to inhibit unloading sg and associated adapter driver */
/* only bump the access_count if we actually succeeded in
@@ -2546,16 +2512,25 @@ sg_allow_access(unsigned char opcode, char dev_type)
#ifdef CONFIG_SCSI_PROC_FS
static int
+sg_idr_max_id(int id, void *p, void *data)
+{
+ int *k = data;
+
+ if (*k < id)
+ *k = id;
+
+ return 0;
+}
+
+static int
sg_last_dev(void)
{
- int k;
+ int k = 0;
unsigned long iflags;
- read_lock_irqsave(&sg_dev_arr_lock, iflags);
- for (k = sg_dev_max - 1; k >= 0; --k)
- if (sg_dev_arr[k] && sg_dev_arr[k]->device)
- break;
- read_unlock_irqrestore(&sg_dev_arr_lock, iflags);
+ read_lock_irqsave(&sg_index_lock, iflags);
+ idr_for_each(&sg_index_idr, sg_idr_max_id, &k);
+ read_unlock_irqrestore(&sg_index_lock, iflags);
return k + 1; /* origin 1 */
}
#endif
@@ -2563,15 +2538,13 @@ sg_last_dev(void)
static Sg_device *
sg_get_dev(int dev)
{
- Sg_device *sdp = NULL;
+ Sg_device *sdp;
unsigned long iflags;
- if (sg_dev_arr && (dev >= 0)) {
- read_lock_irqsave(&sg_dev_arr_lock, iflags);
- if (dev < sg_dev_max)
- sdp = sg_dev_arr[dev];
- read_unlock_irqrestore(&sg_dev_arr_lock, iflags);
- }
+ read_lock_irqsave(&sg_index_lock, iflags);
+ sdp = idr_find(&sg_index_idr, dev);
+ read_unlock_irqrestore(&sg_index_lock, iflags);
+
return sdp;
}
@@ -2805,8 +2778,6 @@ static void * dev_seq_start(struct seq_file *s, loff_t *pos)
if (! it)
return NULL;
- if (NULL == sg_dev_arr)
- return NULL;
it->index = *pos;
it->max = sg_last_dev();
if (it->index >= it->max)
@@ -2942,8 +2913,8 @@ static int sg_proc_seq_show_debug(struct seq_file *s, void *v)
Sg_device *sdp;
if (it && (0 == it->index)) {
- seq_printf(s, "dev_max(currently)=%d max_active_device=%d "
- "(origin 1)\n", sg_dev_max, (int)it->max);
+ seq_printf(s, "max_active_device=%d(origin 1)\n",
+ (int)it->max);
seq_printf(s, " def_reserved_size=%d\n", sg_big_buff);
}
sdp = it ? sg_get_dev(it->index) : NULL;