diff options
Diffstat (limited to 'drivers/scsi/scsi_scan.c')
-rw-r--r-- | drivers/scsi/scsi_scan.c | 106 |
1 files changed, 68 insertions, 38 deletions
diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index f9ecc3dea7df..f14945996ede 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c | |||
@@ -205,12 +205,11 @@ static struct scsi_device *scsi_alloc_sdev(struct scsi_target *starget, | |||
205 | int display_failure_msg = 1, ret; | 205 | int display_failure_msg = 1, ret; |
206 | struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); | 206 | struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); |
207 | 207 | ||
208 | sdev = kmalloc(sizeof(*sdev) + shost->transportt->device_size, | 208 | sdev = kzalloc(sizeof(*sdev) + shost->transportt->device_size, |
209 | GFP_ATOMIC); | 209 | GFP_ATOMIC); |
210 | if (!sdev) | 210 | if (!sdev) |
211 | goto out; | 211 | goto out; |
212 | 212 | ||
213 | memset(sdev, 0, sizeof(*sdev)); | ||
214 | sdev->vendor = scsi_null_device_strs; | 213 | sdev->vendor = scsi_null_device_strs; |
215 | sdev->model = scsi_null_device_strs; | 214 | sdev->model = scsi_null_device_strs; |
216 | sdev->rev = scsi_null_device_strs; | 215 | sdev->rev = scsi_null_device_strs; |
@@ -252,6 +251,7 @@ static struct scsi_device *scsi_alloc_sdev(struct scsi_target *starget, | |||
252 | /* release fn is set up in scsi_sysfs_device_initialise, so | 251 | /* release fn is set up in scsi_sysfs_device_initialise, so |
253 | * have to free and put manually here */ | 252 | * have to free and put manually here */ |
254 | put_device(&starget->dev); | 253 | put_device(&starget->dev); |
254 | kfree(sdev); | ||
255 | goto out; | 255 | goto out; |
256 | } | 256 | } |
257 | 257 | ||
@@ -288,10 +288,7 @@ static void scsi_target_dev_release(struct device *dev) | |||
288 | { | 288 | { |
289 | struct device *parent = dev->parent; | 289 | struct device *parent = dev->parent; |
290 | struct scsi_target *starget = to_scsi_target(dev); | 290 | struct scsi_target *starget = to_scsi_target(dev); |
291 | struct Scsi_Host *shost = dev_to_shost(parent); | ||
292 | 291 | ||
293 | if (shost->hostt->target_destroy) | ||
294 | shost->hostt->target_destroy(starget); | ||
295 | kfree(starget); | 292 | kfree(starget); |
296 | put_device(parent); | 293 | put_device(parent); |
297 | } | 294 | } |
@@ -333,13 +330,13 @@ static struct scsi_target *scsi_alloc_target(struct device *parent, | |||
333 | + shost->transportt->target_size; | 330 | + shost->transportt->target_size; |
334 | struct scsi_target *starget; | 331 | struct scsi_target *starget; |
335 | struct scsi_target *found_target; | 332 | struct scsi_target *found_target; |
333 | int error; | ||
336 | 334 | ||
337 | starget = kmalloc(size, GFP_KERNEL); | 335 | starget = kzalloc(size, GFP_KERNEL); |
338 | if (!starget) { | 336 | if (!starget) { |
339 | printk(KERN_ERR "%s: allocation failure\n", __FUNCTION__); | 337 | printk(KERN_ERR "%s: allocation failure\n", __FUNCTION__); |
340 | return NULL; | 338 | return NULL; |
341 | } | 339 | } |
342 | memset(starget, 0, size); | ||
343 | dev = &starget->dev; | 340 | dev = &starget->dev; |
344 | device_initialize(dev); | 341 | device_initialize(dev); |
345 | starget->reap_ref = 1; | 342 | starget->reap_ref = 1; |
@@ -351,6 +348,8 @@ static struct scsi_target *scsi_alloc_target(struct device *parent, | |||
351 | starget->channel = channel; | 348 | starget->channel = channel; |
352 | INIT_LIST_HEAD(&starget->siblings); | 349 | INIT_LIST_HEAD(&starget->siblings); |
353 | INIT_LIST_HEAD(&starget->devices); | 350 | INIT_LIST_HEAD(&starget->devices); |
351 | starget->state = STARGET_RUNNING; | ||
352 | retry: | ||
354 | spin_lock_irqsave(shost->host_lock, flags); | 353 | spin_lock_irqsave(shost->host_lock, flags); |
355 | 354 | ||
356 | found_target = __scsi_find_target(parent, channel, id); | 355 | found_target = __scsi_find_target(parent, channel, id); |
@@ -361,10 +360,20 @@ static struct scsi_target *scsi_alloc_target(struct device *parent, | |||
361 | spin_unlock_irqrestore(shost->host_lock, flags); | 360 | spin_unlock_irqrestore(shost->host_lock, flags); |
362 | /* allocate and add */ | 361 | /* allocate and add */ |
363 | transport_setup_device(dev); | 362 | transport_setup_device(dev); |
364 | device_add(dev); | 363 | error = device_add(dev); |
364 | if (error) { | ||
365 | dev_err(dev, "target device_add failed, error %d\n", error); | ||
366 | spin_lock_irqsave(shost->host_lock, flags); | ||
367 | list_del_init(&starget->siblings); | ||
368 | spin_unlock_irqrestore(shost->host_lock, flags); | ||
369 | transport_destroy_device(dev); | ||
370 | put_device(parent); | ||
371 | kfree(starget); | ||
372 | return NULL; | ||
373 | } | ||
365 | transport_add_device(dev); | 374 | transport_add_device(dev); |
366 | if (shost->hostt->target_alloc) { | 375 | if (shost->hostt->target_alloc) { |
367 | int error = shost->hostt->target_alloc(starget); | 376 | error = shost->hostt->target_alloc(starget); |
368 | 377 | ||
369 | if(error) { | 378 | if(error) { |
370 | dev_printk(KERN_ERR, dev, "target allocation failed, error %d\n", error); | 379 | dev_printk(KERN_ERR, dev, "target allocation failed, error %d\n", error); |
@@ -383,8 +392,15 @@ static struct scsi_target *scsi_alloc_target(struct device *parent, | |||
383 | found_target->reap_ref++; | 392 | found_target->reap_ref++; |
384 | spin_unlock_irqrestore(shost->host_lock, flags); | 393 | spin_unlock_irqrestore(shost->host_lock, flags); |
385 | put_device(parent); | 394 | put_device(parent); |
386 | kfree(starget); | 395 | if (found_target->state != STARGET_DEL) { |
387 | return found_target; | 396 | kfree(starget); |
397 | return found_target; | ||
398 | } | ||
399 | /* Unfortunately, we found a dying target; need to | ||
400 | * wait until it's dead before we can get a new one */ | ||
401 | put_device(&found_target->dev); | ||
402 | flush_scheduled_work(); | ||
403 | goto retry; | ||
388 | } | 404 | } |
389 | 405 | ||
390 | static void scsi_target_reap_usercontext(void *data) | 406 | static void scsi_target_reap_usercontext(void *data) |
@@ -393,21 +409,15 @@ static void scsi_target_reap_usercontext(void *data) | |||
393 | struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); | 409 | struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); |
394 | unsigned long flags; | 410 | unsigned long flags; |
395 | 411 | ||
412 | transport_remove_device(&starget->dev); | ||
413 | device_del(&starget->dev); | ||
414 | transport_destroy_device(&starget->dev); | ||
396 | spin_lock_irqsave(shost->host_lock, flags); | 415 | spin_lock_irqsave(shost->host_lock, flags); |
397 | 416 | if (shost->hostt->target_destroy) | |
398 | if (--starget->reap_ref == 0 && list_empty(&starget->devices)) { | 417 | shost->hostt->target_destroy(starget); |
399 | list_del_init(&starget->siblings); | 418 | list_del_init(&starget->siblings); |
400 | spin_unlock_irqrestore(shost->host_lock, flags); | ||
401 | transport_remove_device(&starget->dev); | ||
402 | device_del(&starget->dev); | ||
403 | transport_destroy_device(&starget->dev); | ||
404 | put_device(&starget->dev); | ||
405 | return; | ||
406 | |||
407 | } | ||
408 | spin_unlock_irqrestore(shost->host_lock, flags); | 419 | spin_unlock_irqrestore(shost->host_lock, flags); |
409 | 420 | put_device(&starget->dev); | |
410 | return; | ||
411 | } | 421 | } |
412 | 422 | ||
413 | /** | 423 | /** |
@@ -421,7 +431,23 @@ static void scsi_target_reap_usercontext(void *data) | |||
421 | */ | 431 | */ |
422 | void scsi_target_reap(struct scsi_target *starget) | 432 | void scsi_target_reap(struct scsi_target *starget) |
423 | { | 433 | { |
424 | scsi_execute_in_process_context(scsi_target_reap_usercontext, starget); | 434 | struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); |
435 | unsigned long flags; | ||
436 | |||
437 | spin_lock_irqsave(shost->host_lock, flags); | ||
438 | |||
439 | if (--starget->reap_ref == 0 && list_empty(&starget->devices)) { | ||
440 | BUG_ON(starget->state == STARGET_DEL); | ||
441 | starget->state = STARGET_DEL; | ||
442 | spin_unlock_irqrestore(shost->host_lock, flags); | ||
443 | execute_in_process_context(scsi_target_reap_usercontext, | ||
444 | starget, &starget->ew); | ||
445 | return; | ||
446 | |||
447 | } | ||
448 | spin_unlock_irqrestore(shost->host_lock, flags); | ||
449 | |||
450 | return; | ||
425 | } | 451 | } |
426 | 452 | ||
427 | /** | 453 | /** |
@@ -689,12 +715,8 @@ static int scsi_add_lun(struct scsi_device *sdev, char *inq_result, int *bflags) | |||
689 | if (inq_result[7] & 0x10) | 715 | if (inq_result[7] & 0x10) |
690 | sdev->sdtr = 1; | 716 | sdev->sdtr = 1; |
691 | 717 | ||
692 | sprintf(sdev->devfs_name, "scsi/host%d/bus%d/target%d/lun%d", | ||
693 | sdev->host->host_no, sdev->channel, | ||
694 | sdev->id, sdev->lun); | ||
695 | |||
696 | /* | 718 | /* |
697 | * End driverfs/devfs code. | 719 | * End sysfs code. |
698 | */ | 720 | */ |
699 | 721 | ||
700 | if ((sdev->scsi_level >= SCSI_2) && (inq_result[7] & 2) && | 722 | if ((sdev->scsi_level >= SCSI_2) && (inq_result[7] & 2) && |
@@ -871,6 +893,19 @@ static int scsi_probe_and_add_lun(struct scsi_target *starget, | |||
871 | goto out_free_result; | 893 | goto out_free_result; |
872 | } | 894 | } |
873 | 895 | ||
896 | /* | ||
897 | * Non-standard SCSI targets may set the PDT to 0x1f (unknown or | ||
898 | * no device type) instead of using the Peripheral Qualifier to | ||
899 | * indicate that no LUN is present. For example, USB UFI does this. | ||
900 | */ | ||
901 | if (starget->pdt_1f_for_no_lun && (result[0] & 0x1f) == 0x1f) { | ||
902 | SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO | ||
903 | "scsi scan: peripheral device type" | ||
904 | " of 31, no device added\n")); | ||
905 | res = SCSI_SCAN_TARGET_PRESENT; | ||
906 | goto out_free_result; | ||
907 | } | ||
908 | |||
874 | res = scsi_add_lun(sdev, result, &bflags); | 909 | res = scsi_add_lun(sdev, result, &bflags); |
875 | if (res == SCSI_SCAN_LUN_PRESENT) { | 910 | if (res == SCSI_SCAN_LUN_PRESENT) { |
876 | if (bflags & BLIST_KEY) { | 911 | if (bflags & BLIST_KEY) { |
@@ -1261,9 +1296,8 @@ static int scsi_report_lun_scan(struct scsi_target *starget, int bflags, | |||
1261 | struct scsi_device *__scsi_add_device(struct Scsi_Host *shost, uint channel, | 1296 | struct scsi_device *__scsi_add_device(struct Scsi_Host *shost, uint channel, |
1262 | uint id, uint lun, void *hostdata) | 1297 | uint id, uint lun, void *hostdata) |
1263 | { | 1298 | { |
1264 | struct scsi_device *sdev; | 1299 | struct scsi_device *sdev = ERR_PTR(-ENODEV); |
1265 | struct device *parent = &shost->shost_gendev; | 1300 | struct device *parent = &shost->shost_gendev; |
1266 | int res; | ||
1267 | struct scsi_target *starget; | 1301 | struct scsi_target *starget; |
1268 | 1302 | ||
1269 | starget = scsi_alloc_target(parent, channel, id); | 1303 | starget = scsi_alloc_target(parent, channel, id); |
@@ -1272,12 +1306,8 @@ struct scsi_device *__scsi_add_device(struct Scsi_Host *shost, uint channel, | |||
1272 | 1306 | ||
1273 | get_device(&starget->dev); | 1307 | get_device(&starget->dev); |
1274 | mutex_lock(&shost->scan_mutex); | 1308 | mutex_lock(&shost->scan_mutex); |
1275 | if (scsi_host_scan_allowed(shost)) { | 1309 | if (scsi_host_scan_allowed(shost)) |
1276 | res = scsi_probe_and_add_lun(starget, lun, NULL, &sdev, 1, | 1310 | scsi_probe_and_add_lun(starget, lun, NULL, &sdev, 1, hostdata); |
1277 | hostdata); | ||
1278 | if (res != SCSI_SCAN_LUN_PRESENT) | ||
1279 | sdev = ERR_PTR(-ENODEV); | ||
1280 | } | ||
1281 | mutex_unlock(&shost->scan_mutex); | 1311 | mutex_unlock(&shost->scan_mutex); |
1282 | scsi_target_reap(starget); | 1312 | scsi_target_reap(starget); |
1283 | put_device(&starget->dev); | 1313 | put_device(&starget->dev); |