Loading block/genhd.c +516 −28 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ #include <linux/buffer_head.h> #include <linux/mutex.h> #include <linux/idr.h> #include <linux/log2.h> #include "blk.h" Loading @@ -35,6 +36,10 @@ static DEFINE_IDR(ext_devt_idr); static struct device_type disk_type; static void disk_add_events(struct gendisk *disk); static void disk_del_events(struct gendisk *disk); static void disk_release_events(struct gendisk *disk); /** * disk_get_part - get partition * @disk: disk to look partition from Loading Loading @@ -502,6 +507,64 @@ static int exact_lock(dev_t devt, void *data) return 0; } void register_disk(struct gendisk *disk) { struct device *ddev = disk_to_dev(disk); struct block_device *bdev; struct disk_part_iter piter; struct hd_struct *part; int err; ddev->parent = disk->driverfs_dev; dev_set_name(ddev, disk->disk_name); /* delay uevents, until we scanned partition table */ dev_set_uevent_suppress(ddev, 1); if (device_add(ddev)) return; if (!sysfs_deprecated) { err = sysfs_create_link(block_depr, &ddev->kobj, kobject_name(&ddev->kobj)); if (err) { device_del(ddev); return; } } disk->part0.holder_dir = kobject_create_and_add("holders", &ddev->kobj); disk->slave_dir = kobject_create_and_add("slaves", &ddev->kobj); /* No minors to use for partitions */ if (!disk_partitionable(disk)) goto exit; /* No such device (e.g., media were just removed) */ if (!get_capacity(disk)) goto exit; bdev = bdget_disk(disk, 0); if (!bdev) goto exit; bdev->bd_invalidated = 1; err = blkdev_get(bdev, FMODE_READ, NULL); if (err < 0) goto exit; blkdev_put(bdev, FMODE_READ); exit: /* announce disk after possible partitions are created */ dev_set_uevent_suppress(ddev, 0); kobject_uevent(&ddev->kobj, KOBJ_ADD); /* announce possible partitions */ disk_part_iter_init(&piter, disk, 0); while ((part = disk_part_iter_next(&piter))) kobject_uevent(&part_to_dev(part)->kobj, KOBJ_ADD); disk_part_iter_exit(&piter); } /** * add_disk - add partitioning information to kernel list * @disk: per-device partitioning information Loading Loading @@ -551,18 +614,48 @@ void add_disk(struct gendisk *disk) retval = sysfs_create_link(&disk_to_dev(disk)->kobj, &bdi->dev->kobj, "bdi"); WARN_ON(retval); } disk_add_events(disk); } EXPORT_SYMBOL(add_disk); EXPORT_SYMBOL(del_gendisk); /* in partitions/check.c */ void unlink_gendisk(struct gendisk *disk) void del_gendisk(struct gendisk *disk) { struct disk_part_iter piter; struct hd_struct *part; disk_del_events(disk); /* invalidate stuff */ disk_part_iter_init(&piter, disk, DISK_PITER_INCL_EMPTY | DISK_PITER_REVERSE); while ((part = disk_part_iter_next(&piter))) { invalidate_partition(disk, part->partno); delete_partition(disk, part->partno); } disk_part_iter_exit(&piter); invalidate_partition(disk, 0); blk_free_devt(disk_to_dev(disk)->devt); set_capacity(disk, 0); disk->flags &= ~GENHD_FL_UP; sysfs_remove_link(&disk_to_dev(disk)->kobj, "bdi"); bdi_unregister(&disk->queue->backing_dev_info); blk_unregister_queue(disk); blk_unregister_region(disk_devt(disk), disk->minors); part_stat_set_all(&disk->part0, 0); disk->part0.stamp = 0; kobject_put(disk->part0.holder_dir); kobject_put(disk->slave_dir); disk->driverfs_dev = NULL; if (!sysfs_deprecated) sysfs_remove_link(block_depr, dev_name(disk_to_dev(disk))); device_del(disk_to_dev(disk)); } EXPORT_SYMBOL(del_gendisk); /** * get_gendisk - get partitioning information for a given device Loading Loading @@ -1005,6 +1098,7 @@ static void disk_release(struct device *dev) { struct gendisk *disk = dev_to_disk(dev); disk_release_events(disk); kfree(disk->random); disk_replace_part_tbl(disk, NULL); free_part_stats(&disk->part0); Loading Loading @@ -1110,29 +1204,6 @@ static int __init proc_genhd_init(void) module_init(proc_genhd_init); #endif /* CONFIG_PROC_FS */ static void media_change_notify_thread(struct work_struct *work) { struct gendisk *gd = container_of(work, struct gendisk, async_notify); char event[] = "MEDIA_CHANGE=1"; char *envp[] = { event, NULL }; /* * set enviroment vars to indicate which event this is for * so that user space will know to go check the media status. */ kobject_uevent_env(&disk_to_dev(gd)->kobj, KOBJ_CHANGE, envp); put_device(gd->driverfs_dev); } #if 0 void genhd_media_change_notify(struct gendisk *disk) { get_device(disk->driverfs_dev); schedule_work(&disk->async_notify); } EXPORT_SYMBOL_GPL(genhd_media_change_notify); #endif /* 0 */ dev_t blk_lookup_devt(const char *name, int partno) { dev_t devt = MKDEV(0, 0); Loading Loading @@ -1198,8 +1269,6 @@ struct gendisk *alloc_disk_node(int minors, int node_id) disk_to_dev(disk)->class = &block_class; disk_to_dev(disk)->type = &disk_type; device_initialize(disk_to_dev(disk)); INIT_WORK(&disk->async_notify, media_change_notify_thread); } return disk; } Loading Loading @@ -1291,3 +1360,422 @@ int invalidate_partition(struct gendisk *disk, int partno) } EXPORT_SYMBOL(invalidate_partition); /* * Disk events - monitor disk events like media change and eject request. */ struct disk_events { struct list_head node; /* all disk_event's */ struct gendisk *disk; /* the associated disk */ spinlock_t lock; int block; /* event blocking depth */ unsigned int pending; /* events already sent out */ unsigned int clearing; /* events being cleared */ long poll_msecs; /* interval, -1 for default */ struct delayed_work dwork; }; static const char *disk_events_strs[] = { [ilog2(DISK_EVENT_MEDIA_CHANGE)] = "media_change", [ilog2(DISK_EVENT_EJECT_REQUEST)] = "eject_request", }; static char *disk_uevents[] = { [ilog2(DISK_EVENT_MEDIA_CHANGE)] = "DISK_MEDIA_CHANGE=1", [ilog2(DISK_EVENT_EJECT_REQUEST)] = "DISK_EJECT_REQUEST=1", }; /* list of all disk_events */ static DEFINE_MUTEX(disk_events_mutex); static LIST_HEAD(disk_events); /* disable in-kernel polling by default */ static unsigned long disk_events_dfl_poll_msecs = 0; static unsigned long disk_events_poll_jiffies(struct gendisk *disk) { struct disk_events *ev = disk->ev; long intv_msecs = 0; /* * If device-specific poll interval is set, always use it. If * the default is being used, poll iff there are events which * can't be monitored asynchronously. */ if (ev->poll_msecs >= 0) intv_msecs = ev->poll_msecs; else if (disk->events & ~disk->async_events) intv_msecs = disk_events_dfl_poll_msecs; return msecs_to_jiffies(intv_msecs); } static void __disk_block_events(struct gendisk *disk, bool sync) { struct disk_events *ev = disk->ev; unsigned long flags; bool cancel; spin_lock_irqsave(&ev->lock, flags); cancel = !ev->block++; spin_unlock_irqrestore(&ev->lock, flags); if (cancel) { if (sync) cancel_delayed_work_sync(&disk->ev->dwork); else cancel_delayed_work(&disk->ev->dwork); } } static void __disk_unblock_events(struct gendisk *disk, bool check_now) { struct disk_events *ev = disk->ev; unsigned long intv; unsigned long flags; spin_lock_irqsave(&ev->lock, flags); if (WARN_ON_ONCE(ev->block <= 0)) goto out_unlock; if (--ev->block) goto out_unlock; /* * Not exactly a latency critical operation, set poll timer * slack to 25% and kick event check. */ intv = disk_events_poll_jiffies(disk); set_timer_slack(&ev->dwork.timer, intv / 4); if (check_now) queue_delayed_work(system_nrt_wq, &ev->dwork, 0); else if (intv) queue_delayed_work(system_nrt_wq, &ev->dwork, intv); out_unlock: spin_unlock_irqrestore(&ev->lock, flags); } /** * disk_block_events - block and flush disk event checking * @disk: disk to block events for * * On return from this function, it is guaranteed that event checking * isn't in progress and won't happen until unblocked by * disk_unblock_events(). Events blocking is counted and the actual * unblocking happens after the matching number of unblocks are done. * * Note that this intentionally does not block event checking from * disk_clear_events(). * * CONTEXT: * Might sleep. */ void disk_block_events(struct gendisk *disk) { if (disk->ev) __disk_block_events(disk, true); } /** * disk_unblock_events - unblock disk event checking * @disk: disk to unblock events for * * Undo disk_block_events(). When the block count reaches zero, it * starts events polling if configured. * * CONTEXT: * Don't care. Safe to call from irq context. */ void disk_unblock_events(struct gendisk *disk) { if (disk->ev) __disk_unblock_events(disk, true); } /** * disk_check_events - schedule immediate event checking * @disk: disk to check events for * * Schedule immediate event checking on @disk if not blocked. * * CONTEXT: * Don't care. Safe to call from irq context. */ void disk_check_events(struct gendisk *disk) { if (disk->ev) { __disk_block_events(disk, false); __disk_unblock_events(disk, true); } } EXPORT_SYMBOL_GPL(disk_check_events); /** * disk_clear_events - synchronously check, clear and return pending events * @disk: disk to fetch and clear events from * @mask: mask of events to be fetched and clearted * * Disk events are synchronously checked and pending events in @mask * are cleared and returned. This ignores the block count. * * CONTEXT: * Might sleep. */ unsigned int disk_clear_events(struct gendisk *disk, unsigned int mask) { const struct block_device_operations *bdops = disk->fops; struct disk_events *ev = disk->ev; unsigned int pending; if (!ev) { /* for drivers still using the old ->media_changed method */ if ((mask & DISK_EVENT_MEDIA_CHANGE) && bdops->media_changed && bdops->media_changed(disk)) return DISK_EVENT_MEDIA_CHANGE; return 0; } /* tell the workfn about the events being cleared */ spin_lock_irq(&ev->lock); ev->clearing |= mask; spin_unlock_irq(&ev->lock); /* uncondtionally schedule event check and wait for it to finish */ __disk_block_events(disk, true); queue_delayed_work(system_nrt_wq, &ev->dwork, 0); flush_delayed_work(&ev->dwork); __disk_unblock_events(disk, false); /* then, fetch and clear pending events */ spin_lock_irq(&ev->lock); WARN_ON_ONCE(ev->clearing & mask); /* cleared by workfn */ pending = ev->pending & mask; ev->pending &= ~mask; spin_unlock_irq(&ev->lock); return pending; } static void disk_events_workfn(struct work_struct *work) { struct delayed_work *dwork = to_delayed_work(work); struct disk_events *ev = container_of(dwork, struct disk_events, dwork); struct gendisk *disk = ev->disk; char *envp[ARRAY_SIZE(disk_uevents) + 1] = { }; unsigned int clearing = ev->clearing; unsigned int events; unsigned long intv; int nr_events = 0, i; /* check events */ events = disk->fops->check_events(disk, clearing); /* accumulate pending events and schedule next poll if necessary */ spin_lock_irq(&ev->lock); events &= ~ev->pending; ev->pending |= events; ev->clearing &= ~clearing; intv = disk_events_poll_jiffies(disk); if (!ev->block && intv) queue_delayed_work(system_nrt_wq, &ev->dwork, intv); spin_unlock_irq(&ev->lock); /* tell userland about new events */ for (i = 0; i < ARRAY_SIZE(disk_uevents); i++) if (events & (1 << i)) envp[nr_events++] = disk_uevents[i]; if (nr_events) kobject_uevent_env(&disk_to_dev(disk)->kobj, KOBJ_CHANGE, envp); } /* * A disk events enabled device has the following sysfs nodes under * its /sys/block/X/ directory. * * events : list of all supported events * events_async : list of events which can be detected w/o polling * events_poll_msecs : polling interval, 0: disable, -1: system default */ static ssize_t __disk_events_show(unsigned int events, char *buf) { const char *delim = ""; ssize_t pos = 0; int i; for (i = 0; i < ARRAY_SIZE(disk_events_strs); i++) if (events & (1 << i)) { pos += sprintf(buf + pos, "%s%s", delim, disk_events_strs[i]); delim = " "; } if (pos) pos += sprintf(buf + pos, "\n"); return pos; } static ssize_t disk_events_show(struct device *dev, struct device_attribute *attr, char *buf) { struct gendisk *disk = dev_to_disk(dev); return __disk_events_show(disk->events, buf); } static ssize_t disk_events_async_show(struct device *dev, struct device_attribute *attr, char *buf) { struct gendisk *disk = dev_to_disk(dev); return __disk_events_show(disk->async_events, buf); } static ssize_t disk_events_poll_msecs_show(struct device *dev, struct device_attribute *attr, char *buf) { struct gendisk *disk = dev_to_disk(dev); return sprintf(buf, "%ld\n", disk->ev->poll_msecs); } static ssize_t disk_events_poll_msecs_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct gendisk *disk = dev_to_disk(dev); long intv; if (!count || !sscanf(buf, "%ld", &intv)) return -EINVAL; if (intv < 0 && intv != -1) return -EINVAL; __disk_block_events(disk, true); disk->ev->poll_msecs = intv; __disk_unblock_events(disk, true); return count; } static const DEVICE_ATTR(events, S_IRUGO, disk_events_show, NULL); static const DEVICE_ATTR(events_async, S_IRUGO, disk_events_async_show, NULL); static const DEVICE_ATTR(events_poll_msecs, S_IRUGO|S_IWUSR, disk_events_poll_msecs_show, disk_events_poll_msecs_store); static const struct attribute *disk_events_attrs[] = { &dev_attr_events.attr, &dev_attr_events_async.attr, &dev_attr_events_poll_msecs.attr, NULL, }; /* * The default polling interval can be specified by the kernel * parameter block.events_dfl_poll_msecs which defaults to 0 * (disable). This can also be modified runtime by writing to * /sys/module/block/events_dfl_poll_msecs. */ static int disk_events_set_dfl_poll_msecs(const char *val, const struct kernel_param *kp) { struct disk_events *ev; int ret; ret = param_set_ulong(val, kp); if (ret < 0) return ret; mutex_lock(&disk_events_mutex); list_for_each_entry(ev, &disk_events, node) disk_check_events(ev->disk); mutex_unlock(&disk_events_mutex); return 0; } static const struct kernel_param_ops disk_events_dfl_poll_msecs_param_ops = { .set = disk_events_set_dfl_poll_msecs, .get = param_get_ulong, }; #undef MODULE_PARAM_PREFIX #define MODULE_PARAM_PREFIX "block." module_param_cb(events_dfl_poll_msecs, &disk_events_dfl_poll_msecs_param_ops, &disk_events_dfl_poll_msecs, 0644); /* * disk_{add|del|release}_events - initialize and destroy disk_events. */ static void disk_add_events(struct gendisk *disk) { struct disk_events *ev; if (!disk->fops->check_events || !(disk->events | disk->async_events)) return; ev = kzalloc(sizeof(*ev), GFP_KERNEL); if (!ev) { pr_warn("%s: failed to initialize events\n", disk->disk_name); return; } if (sysfs_create_files(&disk_to_dev(disk)->kobj, disk_events_attrs) < 0) { pr_warn("%s: failed to create sysfs files for events\n", disk->disk_name); kfree(ev); return; } disk->ev = ev; INIT_LIST_HEAD(&ev->node); ev->disk = disk; spin_lock_init(&ev->lock); ev->block = 1; ev->poll_msecs = -1; INIT_DELAYED_WORK(&ev->dwork, disk_events_workfn); mutex_lock(&disk_events_mutex); list_add_tail(&ev->node, &disk_events); mutex_unlock(&disk_events_mutex); /* * Block count is initialized to 1 and the following initial * unblock kicks it into action. */ __disk_unblock_events(disk, true); } static void disk_del_events(struct gendisk *disk) { if (!disk->ev) return; __disk_block_events(disk, true); mutex_lock(&disk_events_mutex); list_del_init(&disk->ev->node); mutex_unlock(&disk_events_mutex); sysfs_remove_files(&disk_to_dev(disk)->kobj, disk_events_attrs); } static void disk_release_events(struct gendisk *disk) { /* the block count should be 1 from disk_del_events() */ WARN_ON_ONCE(disk->ev && disk->ev->block != 1); kfree(disk->ev); } drivers/cdrom/cdrom.c +52 −3 Original line number Diff line number Diff line Loading @@ -1348,7 +1348,10 @@ static int cdrom_select_disc(struct cdrom_device_info *cdi, int slot) if (!CDROM_CAN(CDC_SELECT_DISC)) return -EDRIVE_CANT_DO_THIS; (void) cdi->ops->media_changed(cdi, slot); if (cdi->ops->check_events) cdi->ops->check_events(cdi, 0, slot); else cdi->ops->media_changed(cdi, slot); if (slot == CDSL_NONE) { /* set media changed bits, on both queues */ Loading Loading @@ -1392,6 +1395,41 @@ static int cdrom_select_disc(struct cdrom_device_info *cdi, int slot) return slot; } /* * As cdrom implements an extra ioctl consumer for media changed * event, it needs to buffer ->check_events() output, such that event * is not lost for both the usual VFS and ioctl paths. * cdi->{vfs|ioctl}_events are used to buffer pending events for each * path. * * XXX: Locking is non-existent. cdi->ops->check_events() can be * called in parallel and buffering fields are accessed without any * exclusion. The original media_changed code had the same problem. * It might be better to simply deprecate CDROM_MEDIA_CHANGED ioctl * and remove this cruft altogether. It doesn't have much usefulness * at this point. */ static void cdrom_update_events(struct cdrom_device_info *cdi, unsigned int clearing) { unsigned int events; events = cdi->ops->check_events(cdi, clearing, CDSL_CURRENT); cdi->vfs_events |= events; cdi->ioctl_events |= events; } unsigned int cdrom_check_events(struct cdrom_device_info *cdi, unsigned int clearing) { unsigned int events; cdrom_update_events(cdi, clearing); events = cdi->vfs_events; cdi->vfs_events = 0; return events; } /* We want to make media_changed accessible to the user through an * ioctl. The main problem now is that we must double-buffer the * low-level implementation, to assure that the VFS and the user both Loading @@ -1403,15 +1441,26 @@ int media_changed(struct cdrom_device_info *cdi, int queue) { unsigned int mask = (1 << (queue & 1)); int ret = !!(cdi->mc_flags & mask); bool changed; if (!CDROM_CAN(CDC_MEDIA_CHANGED)) return ret; /* changed since last call? */ if (cdi->ops->media_changed(cdi, CDSL_CURRENT)) { if (cdi->ops->check_events) { BUG_ON(!queue); /* shouldn't be called from VFS path */ cdrom_update_events(cdi, DISK_EVENT_MEDIA_CHANGE); changed = cdi->ioctl_events & DISK_EVENT_MEDIA_CHANGE; cdi->ioctl_events = 0; } else changed = cdi->ops->media_changed(cdi, CDSL_CURRENT); if (changed) { cdi->mc_flags = 0x3; /* set bit on both queues */ ret |= 1; cdi->media_written = 0; } cdi->mc_flags &= ~mask; /* clear bit */ return ret; } Loading drivers/scsi/scsi_lib.c +1 −12 Original line number Diff line number Diff line Loading @@ -1984,8 +1984,7 @@ EXPORT_SYMBOL(scsi_mode_sense); * in. * * Returns zero if unsuccessful or an error if TUR failed. For * removable media, a return of NOT_READY or UNIT_ATTENTION is * translated to success, with the ->changed flag updated. * removable media, UNIT_ATTENTION sets ->changed flag. **/ int scsi_test_unit_ready(struct scsi_device *sdev, int timeout, int retries, Loading @@ -2012,16 +2011,6 @@ scsi_test_unit_ready(struct scsi_device *sdev, int timeout, int retries, } while (scsi_sense_valid(sshdr) && sshdr->sense_key == UNIT_ATTENTION && --retries); if (!sshdr) /* could not allocate sense buffer, so can't process it */ return result; if (sdev->removable && scsi_sense_valid(sshdr) && (sshdr->sense_key == UNIT_ATTENTION || sshdr->sense_key == NOT_READY)) { sdev->changed = 1; result = 0; } if (!sshdr_external) kfree(sshdr); return result; Loading drivers/scsi/sd.c +47 −48 Original line number Diff line number Diff line Loading @@ -991,30 +991,50 @@ static int sd_ioctl(struct block_device *bdev, fmode_t mode, static void set_media_not_present(struct scsi_disk *sdkp) { if (sdkp->media_present) sdkp->device->changed = 1; sdkp->media_present = 0; sdkp->capacity = 0; } static int media_not_present(struct scsi_disk *sdkp, struct scsi_sense_hdr *sshdr) { if (!scsi_sense_valid(sshdr)) return 0; /* not invoked for commands that could return deferred errors */ switch (sshdr->sense_key) { case UNIT_ATTENTION: sdkp->device->changed = 1; /* fall through */ case NOT_READY: /* medium not present */ if (sshdr->asc == 0x3A) { set_media_not_present(sdkp); return 1; } } return 0; } /** * sd_media_changed - check if our medium changed * sd_check_events - check media events * @disk: kernel device descriptor * @clearing: disk events currently being cleared * * Returns 0 if not applicable or no change; 1 if change * Returns mask of DISK_EVENT_*. * * Note: this function is invoked from the block subsystem. **/ static int sd_media_changed(struct gendisk *disk) static unsigned int sd_check_events(struct gendisk *disk, unsigned int clearing) { struct scsi_disk *sdkp = scsi_disk(disk); struct scsi_device *sdp = sdkp->device; struct scsi_sense_hdr *sshdr = NULL; int retval; SCSI_LOG_HLQUEUE(3, sd_printk(KERN_INFO, sdkp, "sd_media_changed\n")); if (!sdp->removable) return 0; SCSI_LOG_HLQUEUE(3, sd_printk(KERN_INFO, sdkp, "sd_check_events\n")); /* * If the device is offline, don't send any commands - just pretend as Loading @@ -1024,7 +1044,6 @@ static int sd_media_changed(struct gendisk *disk) */ if (!scsi_device_online(sdp)) { set_media_not_present(sdkp); retval = 1; goto out; } Loading @@ -1045,34 +1064,30 @@ static int sd_media_changed(struct gendisk *disk) sshdr); } /* * Unable to test, unit probably not ready. This usually * means there is no disc in the drive. Mark as changed, * and we will figure it out later once the drive is * available again. */ if (retval || (scsi_sense_valid(sshdr) && /* 0x3a is medium not present */ sshdr->asc == 0x3a)) { /* failed to execute TUR, assume media not present */ if (host_byte(retval)) { set_media_not_present(sdkp); retval = 1; goto out; } if (media_not_present(sdkp, sshdr)) goto out; /* * For removable scsi disk we have to recognise the presence * of a disk in the drive. This is kept in the struct scsi_disk * struct and tested at open ! Daniel Roche (dan@lectra.fr) * of a disk in the drive. */ if (!sdkp->media_present) sdp->changed = 1; sdkp->media_present = 1; retval = sdp->changed; sdp->changed = 0; out: if (retval != sdkp->previous_state) /* for backward compatibility */ if (sdp->changed) sdev_evt_send_simple(sdp, SDEV_EVT_MEDIA_CHANGE, GFP_KERNEL); sdkp->previous_state = retval; kfree(sshdr); retval = sdp->changed ? DISK_EVENT_MEDIA_CHANGE : 0; sdp->changed = 0; return retval; } Loading Loading @@ -1165,7 +1180,7 @@ static const struct block_device_operations sd_fops = { #ifdef CONFIG_COMPAT .compat_ioctl = sd_compat_ioctl, #endif .media_changed = sd_media_changed, .check_events = sd_check_events, .revalidate_disk = sd_revalidate_disk, .unlock_native_capacity = sd_unlock_native_capacity, }; Loading Loading @@ -1301,23 +1316,6 @@ static int sd_done(struct scsi_cmnd *SCpnt) return good_bytes; } static int media_not_present(struct scsi_disk *sdkp, struct scsi_sense_hdr *sshdr) { if (!scsi_sense_valid(sshdr)) return 0; /* not invoked for commands that could return deferred errors */ if (sshdr->sense_key != NOT_READY && sshdr->sense_key != UNIT_ATTENTION) return 0; if (sshdr->asc != 0x3A) /* medium not present */ return 0; set_media_not_present(sdkp); return 1; } /* * spinup disk - called only in sd_revalidate_disk() */ Loading Loading @@ -1492,7 +1490,7 @@ static void read_capacity_error(struct scsi_disk *sdkp, struct scsi_device *sdp, */ if (sdp->removable && sense_valid && sshdr->sense_key == NOT_READY) sdp->changed = 1; set_media_not_present(sdkp); /* * We used to set media_present to 0 here to indicate no media Loading Loading @@ -2347,8 +2345,10 @@ static void sd_probe_async(void *data, async_cookie_t cookie) gd->driverfs_dev = &sdp->sdev_gendev; gd->flags = GENHD_FL_EXT_DEVT; if (sdp->removable) if (sdp->removable) { gd->flags |= GENHD_FL_REMOVABLE; gd->events |= DISK_EVENT_MEDIA_CHANGE; } add_disk(gd); sd_dif_config_host(sdkp); Loading Loading @@ -2430,7 +2430,6 @@ static int sd_probe(struct device *dev) sdkp->disk = gd; sdkp->index = index; atomic_set(&sdkp->openers, 0); sdkp->previous_state = 1; if (!sdp->request_queue->rq_timeout) { if (sdp->type != TYPE_MOD) Loading drivers/scsi/sd.h +0 −1 Original line number Diff line number Diff line Loading @@ -55,7 +55,6 @@ struct scsi_disk { u8 media_present; u8 write_prot; u8 protection_type;/* Data Integrity Field */ unsigned previous_state : 1; unsigned ATO : 1; /* state of disk ATO bit */ unsigned WCE : 1; /* state of disk WCE bit */ unsigned RCD : 1; /* state of disk RCD bit, unused */ Loading Loading
block/genhd.c +516 −28 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ #include <linux/buffer_head.h> #include <linux/mutex.h> #include <linux/idr.h> #include <linux/log2.h> #include "blk.h" Loading @@ -35,6 +36,10 @@ static DEFINE_IDR(ext_devt_idr); static struct device_type disk_type; static void disk_add_events(struct gendisk *disk); static void disk_del_events(struct gendisk *disk); static void disk_release_events(struct gendisk *disk); /** * disk_get_part - get partition * @disk: disk to look partition from Loading Loading @@ -502,6 +507,64 @@ static int exact_lock(dev_t devt, void *data) return 0; } void register_disk(struct gendisk *disk) { struct device *ddev = disk_to_dev(disk); struct block_device *bdev; struct disk_part_iter piter; struct hd_struct *part; int err; ddev->parent = disk->driverfs_dev; dev_set_name(ddev, disk->disk_name); /* delay uevents, until we scanned partition table */ dev_set_uevent_suppress(ddev, 1); if (device_add(ddev)) return; if (!sysfs_deprecated) { err = sysfs_create_link(block_depr, &ddev->kobj, kobject_name(&ddev->kobj)); if (err) { device_del(ddev); return; } } disk->part0.holder_dir = kobject_create_and_add("holders", &ddev->kobj); disk->slave_dir = kobject_create_and_add("slaves", &ddev->kobj); /* No minors to use for partitions */ if (!disk_partitionable(disk)) goto exit; /* No such device (e.g., media were just removed) */ if (!get_capacity(disk)) goto exit; bdev = bdget_disk(disk, 0); if (!bdev) goto exit; bdev->bd_invalidated = 1; err = blkdev_get(bdev, FMODE_READ, NULL); if (err < 0) goto exit; blkdev_put(bdev, FMODE_READ); exit: /* announce disk after possible partitions are created */ dev_set_uevent_suppress(ddev, 0); kobject_uevent(&ddev->kobj, KOBJ_ADD); /* announce possible partitions */ disk_part_iter_init(&piter, disk, 0); while ((part = disk_part_iter_next(&piter))) kobject_uevent(&part_to_dev(part)->kobj, KOBJ_ADD); disk_part_iter_exit(&piter); } /** * add_disk - add partitioning information to kernel list * @disk: per-device partitioning information Loading Loading @@ -551,18 +614,48 @@ void add_disk(struct gendisk *disk) retval = sysfs_create_link(&disk_to_dev(disk)->kobj, &bdi->dev->kobj, "bdi"); WARN_ON(retval); } disk_add_events(disk); } EXPORT_SYMBOL(add_disk); EXPORT_SYMBOL(del_gendisk); /* in partitions/check.c */ void unlink_gendisk(struct gendisk *disk) void del_gendisk(struct gendisk *disk) { struct disk_part_iter piter; struct hd_struct *part; disk_del_events(disk); /* invalidate stuff */ disk_part_iter_init(&piter, disk, DISK_PITER_INCL_EMPTY | DISK_PITER_REVERSE); while ((part = disk_part_iter_next(&piter))) { invalidate_partition(disk, part->partno); delete_partition(disk, part->partno); } disk_part_iter_exit(&piter); invalidate_partition(disk, 0); blk_free_devt(disk_to_dev(disk)->devt); set_capacity(disk, 0); disk->flags &= ~GENHD_FL_UP; sysfs_remove_link(&disk_to_dev(disk)->kobj, "bdi"); bdi_unregister(&disk->queue->backing_dev_info); blk_unregister_queue(disk); blk_unregister_region(disk_devt(disk), disk->minors); part_stat_set_all(&disk->part0, 0); disk->part0.stamp = 0; kobject_put(disk->part0.holder_dir); kobject_put(disk->slave_dir); disk->driverfs_dev = NULL; if (!sysfs_deprecated) sysfs_remove_link(block_depr, dev_name(disk_to_dev(disk))); device_del(disk_to_dev(disk)); } EXPORT_SYMBOL(del_gendisk); /** * get_gendisk - get partitioning information for a given device Loading Loading @@ -1005,6 +1098,7 @@ static void disk_release(struct device *dev) { struct gendisk *disk = dev_to_disk(dev); disk_release_events(disk); kfree(disk->random); disk_replace_part_tbl(disk, NULL); free_part_stats(&disk->part0); Loading Loading @@ -1110,29 +1204,6 @@ static int __init proc_genhd_init(void) module_init(proc_genhd_init); #endif /* CONFIG_PROC_FS */ static void media_change_notify_thread(struct work_struct *work) { struct gendisk *gd = container_of(work, struct gendisk, async_notify); char event[] = "MEDIA_CHANGE=1"; char *envp[] = { event, NULL }; /* * set enviroment vars to indicate which event this is for * so that user space will know to go check the media status. */ kobject_uevent_env(&disk_to_dev(gd)->kobj, KOBJ_CHANGE, envp); put_device(gd->driverfs_dev); } #if 0 void genhd_media_change_notify(struct gendisk *disk) { get_device(disk->driverfs_dev); schedule_work(&disk->async_notify); } EXPORT_SYMBOL_GPL(genhd_media_change_notify); #endif /* 0 */ dev_t blk_lookup_devt(const char *name, int partno) { dev_t devt = MKDEV(0, 0); Loading Loading @@ -1198,8 +1269,6 @@ struct gendisk *alloc_disk_node(int minors, int node_id) disk_to_dev(disk)->class = &block_class; disk_to_dev(disk)->type = &disk_type; device_initialize(disk_to_dev(disk)); INIT_WORK(&disk->async_notify, media_change_notify_thread); } return disk; } Loading Loading @@ -1291,3 +1360,422 @@ int invalidate_partition(struct gendisk *disk, int partno) } EXPORT_SYMBOL(invalidate_partition); /* * Disk events - monitor disk events like media change and eject request. */ struct disk_events { struct list_head node; /* all disk_event's */ struct gendisk *disk; /* the associated disk */ spinlock_t lock; int block; /* event blocking depth */ unsigned int pending; /* events already sent out */ unsigned int clearing; /* events being cleared */ long poll_msecs; /* interval, -1 for default */ struct delayed_work dwork; }; static const char *disk_events_strs[] = { [ilog2(DISK_EVENT_MEDIA_CHANGE)] = "media_change", [ilog2(DISK_EVENT_EJECT_REQUEST)] = "eject_request", }; static char *disk_uevents[] = { [ilog2(DISK_EVENT_MEDIA_CHANGE)] = "DISK_MEDIA_CHANGE=1", [ilog2(DISK_EVENT_EJECT_REQUEST)] = "DISK_EJECT_REQUEST=1", }; /* list of all disk_events */ static DEFINE_MUTEX(disk_events_mutex); static LIST_HEAD(disk_events); /* disable in-kernel polling by default */ static unsigned long disk_events_dfl_poll_msecs = 0; static unsigned long disk_events_poll_jiffies(struct gendisk *disk) { struct disk_events *ev = disk->ev; long intv_msecs = 0; /* * If device-specific poll interval is set, always use it. If * the default is being used, poll iff there are events which * can't be monitored asynchronously. */ if (ev->poll_msecs >= 0) intv_msecs = ev->poll_msecs; else if (disk->events & ~disk->async_events) intv_msecs = disk_events_dfl_poll_msecs; return msecs_to_jiffies(intv_msecs); } static void __disk_block_events(struct gendisk *disk, bool sync) { struct disk_events *ev = disk->ev; unsigned long flags; bool cancel; spin_lock_irqsave(&ev->lock, flags); cancel = !ev->block++; spin_unlock_irqrestore(&ev->lock, flags); if (cancel) { if (sync) cancel_delayed_work_sync(&disk->ev->dwork); else cancel_delayed_work(&disk->ev->dwork); } } static void __disk_unblock_events(struct gendisk *disk, bool check_now) { struct disk_events *ev = disk->ev; unsigned long intv; unsigned long flags; spin_lock_irqsave(&ev->lock, flags); if (WARN_ON_ONCE(ev->block <= 0)) goto out_unlock; if (--ev->block) goto out_unlock; /* * Not exactly a latency critical operation, set poll timer * slack to 25% and kick event check. */ intv = disk_events_poll_jiffies(disk); set_timer_slack(&ev->dwork.timer, intv / 4); if (check_now) queue_delayed_work(system_nrt_wq, &ev->dwork, 0); else if (intv) queue_delayed_work(system_nrt_wq, &ev->dwork, intv); out_unlock: spin_unlock_irqrestore(&ev->lock, flags); } /** * disk_block_events - block and flush disk event checking * @disk: disk to block events for * * On return from this function, it is guaranteed that event checking * isn't in progress and won't happen until unblocked by * disk_unblock_events(). Events blocking is counted and the actual * unblocking happens after the matching number of unblocks are done. * * Note that this intentionally does not block event checking from * disk_clear_events(). * * CONTEXT: * Might sleep. */ void disk_block_events(struct gendisk *disk) { if (disk->ev) __disk_block_events(disk, true); } /** * disk_unblock_events - unblock disk event checking * @disk: disk to unblock events for * * Undo disk_block_events(). When the block count reaches zero, it * starts events polling if configured. * * CONTEXT: * Don't care. Safe to call from irq context. */ void disk_unblock_events(struct gendisk *disk) { if (disk->ev) __disk_unblock_events(disk, true); } /** * disk_check_events - schedule immediate event checking * @disk: disk to check events for * * Schedule immediate event checking on @disk if not blocked. * * CONTEXT: * Don't care. Safe to call from irq context. */ void disk_check_events(struct gendisk *disk) { if (disk->ev) { __disk_block_events(disk, false); __disk_unblock_events(disk, true); } } EXPORT_SYMBOL_GPL(disk_check_events); /** * disk_clear_events - synchronously check, clear and return pending events * @disk: disk to fetch and clear events from * @mask: mask of events to be fetched and clearted * * Disk events are synchronously checked and pending events in @mask * are cleared and returned. This ignores the block count. * * CONTEXT: * Might sleep. */ unsigned int disk_clear_events(struct gendisk *disk, unsigned int mask) { const struct block_device_operations *bdops = disk->fops; struct disk_events *ev = disk->ev; unsigned int pending; if (!ev) { /* for drivers still using the old ->media_changed method */ if ((mask & DISK_EVENT_MEDIA_CHANGE) && bdops->media_changed && bdops->media_changed(disk)) return DISK_EVENT_MEDIA_CHANGE; return 0; } /* tell the workfn about the events being cleared */ spin_lock_irq(&ev->lock); ev->clearing |= mask; spin_unlock_irq(&ev->lock); /* uncondtionally schedule event check and wait for it to finish */ __disk_block_events(disk, true); queue_delayed_work(system_nrt_wq, &ev->dwork, 0); flush_delayed_work(&ev->dwork); __disk_unblock_events(disk, false); /* then, fetch and clear pending events */ spin_lock_irq(&ev->lock); WARN_ON_ONCE(ev->clearing & mask); /* cleared by workfn */ pending = ev->pending & mask; ev->pending &= ~mask; spin_unlock_irq(&ev->lock); return pending; } static void disk_events_workfn(struct work_struct *work) { struct delayed_work *dwork = to_delayed_work(work); struct disk_events *ev = container_of(dwork, struct disk_events, dwork); struct gendisk *disk = ev->disk; char *envp[ARRAY_SIZE(disk_uevents) + 1] = { }; unsigned int clearing = ev->clearing; unsigned int events; unsigned long intv; int nr_events = 0, i; /* check events */ events = disk->fops->check_events(disk, clearing); /* accumulate pending events and schedule next poll if necessary */ spin_lock_irq(&ev->lock); events &= ~ev->pending; ev->pending |= events; ev->clearing &= ~clearing; intv = disk_events_poll_jiffies(disk); if (!ev->block && intv) queue_delayed_work(system_nrt_wq, &ev->dwork, intv); spin_unlock_irq(&ev->lock); /* tell userland about new events */ for (i = 0; i < ARRAY_SIZE(disk_uevents); i++) if (events & (1 << i)) envp[nr_events++] = disk_uevents[i]; if (nr_events) kobject_uevent_env(&disk_to_dev(disk)->kobj, KOBJ_CHANGE, envp); } /* * A disk events enabled device has the following sysfs nodes under * its /sys/block/X/ directory. * * events : list of all supported events * events_async : list of events which can be detected w/o polling * events_poll_msecs : polling interval, 0: disable, -1: system default */ static ssize_t __disk_events_show(unsigned int events, char *buf) { const char *delim = ""; ssize_t pos = 0; int i; for (i = 0; i < ARRAY_SIZE(disk_events_strs); i++) if (events & (1 << i)) { pos += sprintf(buf + pos, "%s%s", delim, disk_events_strs[i]); delim = " "; } if (pos) pos += sprintf(buf + pos, "\n"); return pos; } static ssize_t disk_events_show(struct device *dev, struct device_attribute *attr, char *buf) { struct gendisk *disk = dev_to_disk(dev); return __disk_events_show(disk->events, buf); } static ssize_t disk_events_async_show(struct device *dev, struct device_attribute *attr, char *buf) { struct gendisk *disk = dev_to_disk(dev); return __disk_events_show(disk->async_events, buf); } static ssize_t disk_events_poll_msecs_show(struct device *dev, struct device_attribute *attr, char *buf) { struct gendisk *disk = dev_to_disk(dev); return sprintf(buf, "%ld\n", disk->ev->poll_msecs); } static ssize_t disk_events_poll_msecs_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct gendisk *disk = dev_to_disk(dev); long intv; if (!count || !sscanf(buf, "%ld", &intv)) return -EINVAL; if (intv < 0 && intv != -1) return -EINVAL; __disk_block_events(disk, true); disk->ev->poll_msecs = intv; __disk_unblock_events(disk, true); return count; } static const DEVICE_ATTR(events, S_IRUGO, disk_events_show, NULL); static const DEVICE_ATTR(events_async, S_IRUGO, disk_events_async_show, NULL); static const DEVICE_ATTR(events_poll_msecs, S_IRUGO|S_IWUSR, disk_events_poll_msecs_show, disk_events_poll_msecs_store); static const struct attribute *disk_events_attrs[] = { &dev_attr_events.attr, &dev_attr_events_async.attr, &dev_attr_events_poll_msecs.attr, NULL, }; /* * The default polling interval can be specified by the kernel * parameter block.events_dfl_poll_msecs which defaults to 0 * (disable). This can also be modified runtime by writing to * /sys/module/block/events_dfl_poll_msecs. */ static int disk_events_set_dfl_poll_msecs(const char *val, const struct kernel_param *kp) { struct disk_events *ev; int ret; ret = param_set_ulong(val, kp); if (ret < 0) return ret; mutex_lock(&disk_events_mutex); list_for_each_entry(ev, &disk_events, node) disk_check_events(ev->disk); mutex_unlock(&disk_events_mutex); return 0; } static const struct kernel_param_ops disk_events_dfl_poll_msecs_param_ops = { .set = disk_events_set_dfl_poll_msecs, .get = param_get_ulong, }; #undef MODULE_PARAM_PREFIX #define MODULE_PARAM_PREFIX "block." module_param_cb(events_dfl_poll_msecs, &disk_events_dfl_poll_msecs_param_ops, &disk_events_dfl_poll_msecs, 0644); /* * disk_{add|del|release}_events - initialize and destroy disk_events. */ static void disk_add_events(struct gendisk *disk) { struct disk_events *ev; if (!disk->fops->check_events || !(disk->events | disk->async_events)) return; ev = kzalloc(sizeof(*ev), GFP_KERNEL); if (!ev) { pr_warn("%s: failed to initialize events\n", disk->disk_name); return; } if (sysfs_create_files(&disk_to_dev(disk)->kobj, disk_events_attrs) < 0) { pr_warn("%s: failed to create sysfs files for events\n", disk->disk_name); kfree(ev); return; } disk->ev = ev; INIT_LIST_HEAD(&ev->node); ev->disk = disk; spin_lock_init(&ev->lock); ev->block = 1; ev->poll_msecs = -1; INIT_DELAYED_WORK(&ev->dwork, disk_events_workfn); mutex_lock(&disk_events_mutex); list_add_tail(&ev->node, &disk_events); mutex_unlock(&disk_events_mutex); /* * Block count is initialized to 1 and the following initial * unblock kicks it into action. */ __disk_unblock_events(disk, true); } static void disk_del_events(struct gendisk *disk) { if (!disk->ev) return; __disk_block_events(disk, true); mutex_lock(&disk_events_mutex); list_del_init(&disk->ev->node); mutex_unlock(&disk_events_mutex); sysfs_remove_files(&disk_to_dev(disk)->kobj, disk_events_attrs); } static void disk_release_events(struct gendisk *disk) { /* the block count should be 1 from disk_del_events() */ WARN_ON_ONCE(disk->ev && disk->ev->block != 1); kfree(disk->ev); }
drivers/cdrom/cdrom.c +52 −3 Original line number Diff line number Diff line Loading @@ -1348,7 +1348,10 @@ static int cdrom_select_disc(struct cdrom_device_info *cdi, int slot) if (!CDROM_CAN(CDC_SELECT_DISC)) return -EDRIVE_CANT_DO_THIS; (void) cdi->ops->media_changed(cdi, slot); if (cdi->ops->check_events) cdi->ops->check_events(cdi, 0, slot); else cdi->ops->media_changed(cdi, slot); if (slot == CDSL_NONE) { /* set media changed bits, on both queues */ Loading Loading @@ -1392,6 +1395,41 @@ static int cdrom_select_disc(struct cdrom_device_info *cdi, int slot) return slot; } /* * As cdrom implements an extra ioctl consumer for media changed * event, it needs to buffer ->check_events() output, such that event * is not lost for both the usual VFS and ioctl paths. * cdi->{vfs|ioctl}_events are used to buffer pending events for each * path. * * XXX: Locking is non-existent. cdi->ops->check_events() can be * called in parallel and buffering fields are accessed without any * exclusion. The original media_changed code had the same problem. * It might be better to simply deprecate CDROM_MEDIA_CHANGED ioctl * and remove this cruft altogether. It doesn't have much usefulness * at this point. */ static void cdrom_update_events(struct cdrom_device_info *cdi, unsigned int clearing) { unsigned int events; events = cdi->ops->check_events(cdi, clearing, CDSL_CURRENT); cdi->vfs_events |= events; cdi->ioctl_events |= events; } unsigned int cdrom_check_events(struct cdrom_device_info *cdi, unsigned int clearing) { unsigned int events; cdrom_update_events(cdi, clearing); events = cdi->vfs_events; cdi->vfs_events = 0; return events; } /* We want to make media_changed accessible to the user through an * ioctl. The main problem now is that we must double-buffer the * low-level implementation, to assure that the VFS and the user both Loading @@ -1403,15 +1441,26 @@ int media_changed(struct cdrom_device_info *cdi, int queue) { unsigned int mask = (1 << (queue & 1)); int ret = !!(cdi->mc_flags & mask); bool changed; if (!CDROM_CAN(CDC_MEDIA_CHANGED)) return ret; /* changed since last call? */ if (cdi->ops->media_changed(cdi, CDSL_CURRENT)) { if (cdi->ops->check_events) { BUG_ON(!queue); /* shouldn't be called from VFS path */ cdrom_update_events(cdi, DISK_EVENT_MEDIA_CHANGE); changed = cdi->ioctl_events & DISK_EVENT_MEDIA_CHANGE; cdi->ioctl_events = 0; } else changed = cdi->ops->media_changed(cdi, CDSL_CURRENT); if (changed) { cdi->mc_flags = 0x3; /* set bit on both queues */ ret |= 1; cdi->media_written = 0; } cdi->mc_flags &= ~mask; /* clear bit */ return ret; } Loading
drivers/scsi/scsi_lib.c +1 −12 Original line number Diff line number Diff line Loading @@ -1984,8 +1984,7 @@ EXPORT_SYMBOL(scsi_mode_sense); * in. * * Returns zero if unsuccessful or an error if TUR failed. For * removable media, a return of NOT_READY or UNIT_ATTENTION is * translated to success, with the ->changed flag updated. * removable media, UNIT_ATTENTION sets ->changed flag. **/ int scsi_test_unit_ready(struct scsi_device *sdev, int timeout, int retries, Loading @@ -2012,16 +2011,6 @@ scsi_test_unit_ready(struct scsi_device *sdev, int timeout, int retries, } while (scsi_sense_valid(sshdr) && sshdr->sense_key == UNIT_ATTENTION && --retries); if (!sshdr) /* could not allocate sense buffer, so can't process it */ return result; if (sdev->removable && scsi_sense_valid(sshdr) && (sshdr->sense_key == UNIT_ATTENTION || sshdr->sense_key == NOT_READY)) { sdev->changed = 1; result = 0; } if (!sshdr_external) kfree(sshdr); return result; Loading
drivers/scsi/sd.c +47 −48 Original line number Diff line number Diff line Loading @@ -991,30 +991,50 @@ static int sd_ioctl(struct block_device *bdev, fmode_t mode, static void set_media_not_present(struct scsi_disk *sdkp) { if (sdkp->media_present) sdkp->device->changed = 1; sdkp->media_present = 0; sdkp->capacity = 0; } static int media_not_present(struct scsi_disk *sdkp, struct scsi_sense_hdr *sshdr) { if (!scsi_sense_valid(sshdr)) return 0; /* not invoked for commands that could return deferred errors */ switch (sshdr->sense_key) { case UNIT_ATTENTION: sdkp->device->changed = 1; /* fall through */ case NOT_READY: /* medium not present */ if (sshdr->asc == 0x3A) { set_media_not_present(sdkp); return 1; } } return 0; } /** * sd_media_changed - check if our medium changed * sd_check_events - check media events * @disk: kernel device descriptor * @clearing: disk events currently being cleared * * Returns 0 if not applicable or no change; 1 if change * Returns mask of DISK_EVENT_*. * * Note: this function is invoked from the block subsystem. **/ static int sd_media_changed(struct gendisk *disk) static unsigned int sd_check_events(struct gendisk *disk, unsigned int clearing) { struct scsi_disk *sdkp = scsi_disk(disk); struct scsi_device *sdp = sdkp->device; struct scsi_sense_hdr *sshdr = NULL; int retval; SCSI_LOG_HLQUEUE(3, sd_printk(KERN_INFO, sdkp, "sd_media_changed\n")); if (!sdp->removable) return 0; SCSI_LOG_HLQUEUE(3, sd_printk(KERN_INFO, sdkp, "sd_check_events\n")); /* * If the device is offline, don't send any commands - just pretend as Loading @@ -1024,7 +1044,6 @@ static int sd_media_changed(struct gendisk *disk) */ if (!scsi_device_online(sdp)) { set_media_not_present(sdkp); retval = 1; goto out; } Loading @@ -1045,34 +1064,30 @@ static int sd_media_changed(struct gendisk *disk) sshdr); } /* * Unable to test, unit probably not ready. This usually * means there is no disc in the drive. Mark as changed, * and we will figure it out later once the drive is * available again. */ if (retval || (scsi_sense_valid(sshdr) && /* 0x3a is medium not present */ sshdr->asc == 0x3a)) { /* failed to execute TUR, assume media not present */ if (host_byte(retval)) { set_media_not_present(sdkp); retval = 1; goto out; } if (media_not_present(sdkp, sshdr)) goto out; /* * For removable scsi disk we have to recognise the presence * of a disk in the drive. This is kept in the struct scsi_disk * struct and tested at open ! Daniel Roche (dan@lectra.fr) * of a disk in the drive. */ if (!sdkp->media_present) sdp->changed = 1; sdkp->media_present = 1; retval = sdp->changed; sdp->changed = 0; out: if (retval != sdkp->previous_state) /* for backward compatibility */ if (sdp->changed) sdev_evt_send_simple(sdp, SDEV_EVT_MEDIA_CHANGE, GFP_KERNEL); sdkp->previous_state = retval; kfree(sshdr); retval = sdp->changed ? DISK_EVENT_MEDIA_CHANGE : 0; sdp->changed = 0; return retval; } Loading Loading @@ -1165,7 +1180,7 @@ static const struct block_device_operations sd_fops = { #ifdef CONFIG_COMPAT .compat_ioctl = sd_compat_ioctl, #endif .media_changed = sd_media_changed, .check_events = sd_check_events, .revalidate_disk = sd_revalidate_disk, .unlock_native_capacity = sd_unlock_native_capacity, }; Loading Loading @@ -1301,23 +1316,6 @@ static int sd_done(struct scsi_cmnd *SCpnt) return good_bytes; } static int media_not_present(struct scsi_disk *sdkp, struct scsi_sense_hdr *sshdr) { if (!scsi_sense_valid(sshdr)) return 0; /* not invoked for commands that could return deferred errors */ if (sshdr->sense_key != NOT_READY && sshdr->sense_key != UNIT_ATTENTION) return 0; if (sshdr->asc != 0x3A) /* medium not present */ return 0; set_media_not_present(sdkp); return 1; } /* * spinup disk - called only in sd_revalidate_disk() */ Loading Loading @@ -1492,7 +1490,7 @@ static void read_capacity_error(struct scsi_disk *sdkp, struct scsi_device *sdp, */ if (sdp->removable && sense_valid && sshdr->sense_key == NOT_READY) sdp->changed = 1; set_media_not_present(sdkp); /* * We used to set media_present to 0 here to indicate no media Loading Loading @@ -2347,8 +2345,10 @@ static void sd_probe_async(void *data, async_cookie_t cookie) gd->driverfs_dev = &sdp->sdev_gendev; gd->flags = GENHD_FL_EXT_DEVT; if (sdp->removable) if (sdp->removable) { gd->flags |= GENHD_FL_REMOVABLE; gd->events |= DISK_EVENT_MEDIA_CHANGE; } add_disk(gd); sd_dif_config_host(sdkp); Loading Loading @@ -2430,7 +2430,6 @@ static int sd_probe(struct device *dev) sdkp->disk = gd; sdkp->index = index; atomic_set(&sdkp->openers, 0); sdkp->previous_state = 1; if (!sdp->request_queue->rq_timeout) { if (sdp->type != TYPE_MOD) Loading
drivers/scsi/sd.h +0 −1 Original line number Diff line number Diff line Loading @@ -55,7 +55,6 @@ struct scsi_disk { u8 media_present; u8 write_prot; u8 protection_type;/* Data Integrity Field */ unsigned previous_state : 1; unsigned ATO : 1; /* state of disk ATO bit */ unsigned WCE : 1; /* state of disk WCE bit */ unsigned RCD : 1; /* state of disk RCD bit, unused */ Loading