|
@@ -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 = {
|
|
@@ -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)
|
|
@@ -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
|
|
@@ -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
|
|
@@ -2545,17 +2511,26 @@ 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;
|