Factor scsi_dh_data allocation out from scsi_dh_attach to the optional scsi_dh_alloc_data interface. scsi_dh_attach will still allocate the the appropriate scsi_dh_data if a NULL scsi_dh_data struct is passed to it. In that case, all scsi_dh will now use GFP_NOIO to allocate scsi_dh_data. This change will allow DM multipath to preallocate the scsi_dh_data during the multipath table load but then defer the scsi_dh_attach until the multipath table resume (memory allocation is not allowed during DM table resume). Signed-off-by: Mike Snitzer <snitzer@xxxxxxxxxx> --- drivers/md/dm-mpath.c | 4 +- drivers/scsi/device_handler/scsi_dh.c | 42 +++++++++++++++++++++----- drivers/scsi/device_handler/scsi_dh_alua.c | 18 ++++++++--- drivers/scsi/device_handler/scsi_dh_emc.c | 18 ++++++++--- drivers/scsi/device_handler/scsi_dh_hp_sw.c | 18 ++++++++--- drivers/scsi/device_handler/scsi_dh_rdac.c | 18 ++++++++--- include/scsi/scsi_device.h | 3 +- include/scsi/scsi_dh.h | 4 ++- 8 files changed, 93 insertions(+), 32 deletions(-) diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c index 51bb816..37d9ead 100644 --- a/drivers/md/dm-mpath.c +++ b/drivers/md/dm-mpath.c @@ -616,14 +616,14 @@ static struct pgpath *parse_path(struct dm_arg_set *as, struct path_selector *ps * Increments scsi_dh reference, even when using an * already-attached handler. */ - r = scsi_dh_attach(q, m->hw_handler_name); + r = scsi_dh_attach(q, m->hw_handler_name, NULL); if (r == -EBUSY) { /* * Already attached to different hw_handler: * try to reattach with correct one. */ scsi_dh_detach(q); - r = scsi_dh_attach(q, m->hw_handler_name); + r = scsi_dh_attach(q, m->hw_handler_name, NULL); } if (r < 0) { diff --git a/drivers/scsi/device_handler/scsi_dh.c b/drivers/scsi/device_handler/scsi_dh.c index 33e422e..034b394 100644 --- a/drivers/scsi/device_handler/scsi_dh.c +++ b/drivers/scsi/device_handler/scsi_dh.c @@ -94,19 +94,24 @@ device_handler_match(struct scsi_device_handler *scsi_dh, * scsi_dh_handler_attach - Attach a device handler to a device * @sdev - SCSI device the device handler should attach to * @scsi_dh - The device handler to attach + * @scsi_dh_data - if not NULL it is either assigned to sdev->scsi_dh_data + * on attach or free'd if the scsi_dh is already attached. */ static int scsi_dh_handler_attach(struct scsi_device *sdev, - struct scsi_device_handler *scsi_dh) + struct scsi_device_handler *scsi_dh, + struct scsi_dh_data *scsi_dh_data) { int err = 0; if (sdev->scsi_dh_data) { if (sdev->scsi_dh_data->scsi_dh != scsi_dh) err = -EBUSY; - else + else { kref_get(&sdev->scsi_dh_data->kref); + kfree(scsi_dh_data); + } } else if (scsi_dh->attach) { - err = scsi_dh->attach(sdev); + err = scsi_dh->attach(sdev, scsi_dh_data); if (!err) { kref_init(&sdev->scsi_dh_data->kref); sdev->scsi_dh_data->sdev = sdev; @@ -166,7 +171,7 @@ store_dh_state(struct device *dev, struct device_attribute *attr, */ if (!(scsi_dh = get_device_handler(buf))) return err; - err = scsi_dh_handler_attach(sdev, scsi_dh); + err = scsi_dh_handler_attach(sdev, scsi_dh, NULL); } else { scsi_dh = sdev->scsi_dh_data->scsi_dh; if (!strncmp(buf, "detach", 6)) { @@ -262,7 +267,7 @@ static int scsi_dh_notifier(struct notifier_block *nb, /* don't care about err */ devinfo = device_handler_match(NULL, sdev); if (devinfo) - err = scsi_dh_handler_attach(sdev, devinfo); + err = scsi_dh_handler_attach(sdev, devinfo, NULL); } else if (action == BUS_NOTIFY_DEL_DEVICE) { device_remove_file(dev, &scsi_dh_state_attr); scsi_dh_handler_detach(sdev, NULL); @@ -287,7 +292,7 @@ static int scsi_dh_notifier_add(struct device *dev, void *data) sdev = to_scsi_device(dev); if (device_handler_match(scsi_dh, sdev)) - scsi_dh_handler_attach(sdev, scsi_dh); + scsi_dh_handler_attach(sdev, scsi_dh, NULL); put_device(dev); @@ -466,13 +471,34 @@ int scsi_dh_handler_exist(const char *name) } EXPORT_SYMBOL_GPL(scsi_dh_handler_exist); +struct scsi_dh_data *scsi_dh_alloc_data(const char *name, gfp_t flags) +{ + struct scsi_device_handler *scsi_dh; + struct scsi_dh_data *scsi_dh_data; + size_t scsi_dh_data_size = sizeof(sizeof(*scsi_dh_data)); + + scsi_dh = get_device_handler(name); + if (!scsi_dh) + return ERR_PTR(-EINVAL); + + if (scsi_dh->get_dh_data_size) + scsi_dh_data_size += scsi_dh->get_dh_data_size(); + scsi_dh_data = kzalloc(scsi_dh_data_size, flags); + if (!scsi_dh_data) + return ERR_PTR(-ENOMEM); + + return scsi_dh_data; +} +EXPORT_SYMBOL_GPL(scsi_dh_alloc_data); + /* * scsi_dh_attach - Attach device handler * @q - Request queue that is associated with the scsi_device * the handler should be attached to * @name - name of the handler to attach */ -int scsi_dh_attach(struct request_queue *q, const char *name) +int scsi_dh_attach(struct request_queue *q, const char *name, + struct scsi_dh_data *scsi_dh_data) { unsigned long flags; struct scsi_device *sdev; @@ -490,7 +516,7 @@ int scsi_dh_attach(struct request_queue *q, const char *name) spin_unlock_irqrestore(q->queue_lock, flags); if (!err) { - err = scsi_dh_handler_attach(sdev, scsi_dh); + err = scsi_dh_handler_attach(sdev, scsi_dh, scsi_dh_data); put_device(&sdev->sdev_gendev); } return err; diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c index 6f4d8e6..2334db1 100644 --- a/drivers/scsi/device_handler/scsi_dh_alua.c +++ b/drivers/scsi/device_handler/scsi_dh_alua.c @@ -798,7 +798,13 @@ static bool alua_match(struct scsi_device *sdev) return (scsi_device_tpgs(sdev) != 0); } -static int alua_bus_attach(struct scsi_device *sdev); +static size_t alua_dh_data_size(void) +{ + return sizeof(struct alua_dh_data); +} + +static int alua_bus_attach(struct scsi_device *sdev, + struct scsi_dh_data *scsi_dh_data); static void alua_bus_detach(struct scsi_device *sdev); static struct scsi_device_handler alua_dh = { @@ -811,21 +817,23 @@ static struct scsi_device_handler alua_dh = { .activate = alua_activate, .set_params = alua_set_params, .match = alua_match, + .get_dh_data_size = alua_dh_data_size, }; /* * alua_bus_attach - Attach device handler * @sdev: device to be attached to */ -static int alua_bus_attach(struct scsi_device *sdev) +static int alua_bus_attach(struct scsi_device *sdev, + struct scsi_dh_data *scsi_dh_data) { - struct scsi_dh_data *scsi_dh_data; struct alua_dh_data *h; unsigned long flags; int err = SCSI_DH_OK; - scsi_dh_data = kzalloc(sizeof(*scsi_dh_data) - + sizeof(*h) , GFP_KERNEL); + if (!scsi_dh_data) + scsi_dh_data = kzalloc(sizeof(*scsi_dh_data) + + sizeof(*h) , GFP_NOIO); if (!scsi_dh_data) { sdev_printk(KERN_ERR, sdev, "%s: Attach failed\n", ALUA_DH_NAME); diff --git a/drivers/scsi/device_handler/scsi_dh_emc.c b/drivers/scsi/device_handler/scsi_dh_emc.c index e1c8be0..fa0553a 100644 --- a/drivers/scsi/device_handler/scsi_dh_emc.c +++ b/drivers/scsi/device_handler/scsi_dh_emc.c @@ -647,7 +647,13 @@ static bool clariion_match(struct scsi_device *sdev) return false; } -static int clariion_bus_attach(struct scsi_device *sdev); +static size_t clariion_dh_data_size(void) +{ + return sizeof(struct clariion_dh_data); +} + +static int clariion_bus_attach(struct scsi_device *sdev, + struct scsi_dh_data *scsi_dh_data); static void clariion_bus_detach(struct scsi_device *sdev); static struct scsi_device_handler clariion_dh = { @@ -661,17 +667,19 @@ static struct scsi_device_handler clariion_dh = { .prep_fn = clariion_prep_fn, .set_params = clariion_set_params, .match = clariion_match, + .get_dh_data_size = clariion_dh_data_size, }; -static int clariion_bus_attach(struct scsi_device *sdev) +static int clariion_bus_attach(struct scsi_device *sdev, + struct scsi_dh_data *scsi_dh_data) { - struct scsi_dh_data *scsi_dh_data; struct clariion_dh_data *h; unsigned long flags; int err; - scsi_dh_data = kzalloc(sizeof(*scsi_dh_data) - + sizeof(*h) , GFP_KERNEL); + if (!scsi_dh_data) + scsi_dh_data = kzalloc(sizeof(*scsi_dh_data) + + sizeof(*h) , GFP_NOIO); if (!scsi_dh_data) { sdev_printk(KERN_ERR, sdev, "%s: Attach failed\n", CLARIION_NAME); diff --git a/drivers/scsi/device_handler/scsi_dh_hp_sw.c b/drivers/scsi/device_handler/scsi_dh_hp_sw.c index 084062b..c0a13e6 100644 --- a/drivers/scsi/device_handler/scsi_dh_hp_sw.c +++ b/drivers/scsi/device_handler/scsi_dh_hp_sw.c @@ -338,7 +338,13 @@ static bool hp_sw_match(struct scsi_device *sdev) return false; } -static int hp_sw_bus_attach(struct scsi_device *sdev); +static size_t hp_sw_dh_data_size(void) +{ + return sizeof(struct hp_sw_dh_data); +} + +static int hp_sw_bus_attach(struct scsi_device *sdev, + struct scsi_dh_data *scsi_dh_data); static void hp_sw_bus_detach(struct scsi_device *sdev); static struct scsi_device_handler hp_sw_dh = { @@ -350,17 +356,19 @@ static struct scsi_device_handler hp_sw_dh = { .activate = hp_sw_activate, .prep_fn = hp_sw_prep_fn, .match = hp_sw_match, + .get_dh_data_size = hp_sw_dh_data_size, }; -static int hp_sw_bus_attach(struct scsi_device *sdev) +static int hp_sw_bus_attach(struct scsi_device *sdev, + struct scsi_dh_data *scsi_dh_data) { - struct scsi_dh_data *scsi_dh_data; struct hp_sw_dh_data *h; unsigned long flags; int ret; - scsi_dh_data = kzalloc(sizeof(*scsi_dh_data) - + sizeof(*h) , GFP_KERNEL); + if (!scsi_dh_data) + scsi_dh_data = kzalloc(sizeof(*scsi_dh_data) + + sizeof(*h) , GFP_NOIO); if (!scsi_dh_data) { sdev_printk(KERN_ERR, sdev, "%s: Attach Failed\n", HP_SW_NAME); diff --git a/drivers/scsi/device_handler/scsi_dh_rdac.c b/drivers/scsi/device_handler/scsi_dh_rdac.c index 69c915a..77ebc2d 100644 --- a/drivers/scsi/device_handler/scsi_dh_rdac.c +++ b/drivers/scsi/device_handler/scsi_dh_rdac.c @@ -824,7 +824,13 @@ static bool rdac_match(struct scsi_device *sdev) return false; } -static int rdac_bus_attach(struct scsi_device *sdev); +static size_t rdac_dh_data_size(void) +{ + return sizeof(struct rdac_dh_data); +} + +static int rdac_bus_attach(struct scsi_device *sdev, + struct scsi_dh_data *scsi_dh_data); static void rdac_bus_detach(struct scsi_device *sdev); static struct scsi_device_handler rdac_dh = { @@ -837,19 +843,21 @@ static struct scsi_device_handler rdac_dh = { .detach = rdac_bus_detach, .activate = rdac_activate, .match = rdac_match, + .get_dh_data_size = rdac_dh_data_size, }; -static int rdac_bus_attach(struct scsi_device *sdev) +static int rdac_bus_attach(struct scsi_device *sdev, + struct scsi_dh_data *scsi_dh_data) { - struct scsi_dh_data *scsi_dh_data; struct rdac_dh_data *h; unsigned long flags; int err; char array_name[ARRAY_LABEL_LEN]; char array_id[UNIQUE_ID_LEN]; - scsi_dh_data = kzalloc(sizeof(*scsi_dh_data) - + sizeof(*h) , GFP_KERNEL); + if (!scsi_dh_data) + scsi_dh_data = kzalloc(sizeof(*scsi_dh_data) + + sizeof(*h) , GFP_NOIO); if (!scsi_dh_data) { sdev_printk(KERN_ERR, sdev, "%s: Attach failed\n", RDAC_NAME); diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index a7f9cba..4f4feb4 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -201,12 +201,13 @@ struct scsi_device_handler { const char *name; const struct scsi_dh_devlist *devlist; int (*check_sense)(struct scsi_device *, struct scsi_sense_hdr *); - int (*attach)(struct scsi_device *); + int (*attach)(struct scsi_device *, struct scsi_dh_data *); void (*detach)(struct scsi_device *); int (*activate)(struct scsi_device *, activate_complete, void *); int (*prep_fn)(struct scsi_device *, struct request *); int (*set_params)(struct scsi_device *, const char *); bool (*match)(struct scsi_device *); + size_t (*get_dh_data_size)(void); }; struct scsi_dh_data { diff --git a/include/scsi/scsi_dh.h b/include/scsi/scsi_dh.h index 620c723..26b7d8b 100644 --- a/include/scsi/scsi_dh.h +++ b/include/scsi/scsi_dh.h @@ -58,7 +58,9 @@ enum { #if defined(CONFIG_SCSI_DH) || defined(CONFIG_SCSI_DH_MODULE) extern int scsi_dh_activate(struct request_queue *, activate_complete, void *); extern int scsi_dh_handler_exist(const char *); -extern int scsi_dh_attach(struct request_queue *, const char *); +extern struct scsi_dh_data *scsi_dh_alloc_data(const char *, gfp_t); +extern int scsi_dh_attach(struct request_queue *, const char *, + struct scsi_dh_data *); extern void scsi_dh_detach(struct request_queue *); extern const char *scsi_dh_attached_handler_name(struct request_queue *, gfp_t); extern int scsi_dh_set_params(struct request_queue *, const char *); -- 1.7.1 -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html