svn commit: r249152 - in stable/9/sys: cam cam/ata cam/scsi geom geom/part
Scott Long
scott4long at yahoo.com
Wed Apr 24 23:39:41 UTC 2013
I've already responded to Alexander in private, but in case he doesn't respond, I'm stating publicly that I object to this commit (and the corresponding one in HEAD) and I'd like to have it reviewed and reverted.
Scott
On Apr 5, 2013, at 4:41 AM, Alexander Motin <mav at FreeBSD.org> wrote:
> Author: mav
> Date: Fri Apr 5 11:41:56 2013
> New Revision: 249152
> URL: http://svnweb.freebsd.org/changeset/base/249152
>
> Log:
> MFC r238886, r238892:
> Implement media change notification for DA and CD removable media devices.
> It includes three parts:
> 1) Modifications to CAM to detect media media changes and report them to
> disk(9) layer. For modern SATA (and potentially UAS) devices it utilizes
> Asynchronous Notification mechanism to receive events from hardware.
> Active polling with TEST UNIT READY commands with 3 seconds period is used
> for incapable hardware. After that both CD and DA drivers work the same way,
> detecting two conditions: "NOT READY: Medium not present" after medium was
> detected previously, and "UNIT ATTENTION: Not ready to ready change, medium
> may have changed". First one reported to disk(9) as media removal, second
> as media insert/change. To reliably receive second event new
> AC_UNIT_ATTENTION async added to make UAs broadcasted to all periphs by
> generic error handling code in cam_periph_error().
> 2) Modifications to GEOM core to handle media remove and change events.
> Media removal handled by spoiling all consumers attached to the provider.
> Media change event also schedules provider retaste after spoiling to probe
> new media. New flag G_CF_ORPHAN was added to consumers to reflect that
> consumer is in process of destruction. It allows retaste to create new
> geom instance of the same class, while previous one is still dying.
> 3) Modifications to some GEOM classes: DEV -- to report media change
> events to devd; PART class already handles spoiling alike to orphan.
>
> Modified:
> stable/9/sys/cam/ata/ata_all.h
> stable/9/sys/cam/ata/ata_xpt.c
> stable/9/sys/cam/cam_ccb.h
> stable/9/sys/cam/cam_periph.c
> stable/9/sys/cam/cam_xpt.c
> stable/9/sys/cam/scsi/scsi_cd.c
> stable/9/sys/cam/scsi/scsi_da.c
> stable/9/sys/geom/geom.h
> stable/9/sys/geom/geom_dev.c
> stable/9/sys/geom/geom_disk.c
> stable/9/sys/geom/geom_disk.h
> stable/9/sys/geom/geom_event.c
> stable/9/sys/geom/geom_io.c
> stable/9/sys/geom/geom_slice.c
> stable/9/sys/geom/geom_subr.c
> stable/9/sys/geom/part/g_part.c
> Directory Properties:
> stable/9/sys/ (props changed)
>
> Modified: stable/9/sys/cam/ata/ata_all.h
> ==============================================================================
> --- stable/9/sys/cam/ata/ata_all.h Fri Apr 5 11:30:31 2013 (r249151)
> +++ stable/9/sys/cam/ata/ata_all.h Fri Apr 5 11:41:56 2013 (r249152)
> @@ -35,6 +35,7 @@ struct ccb_ataio;
> struct cam_periph;
> union ccb;
>
> +#define SID_AEN 0x04 /* Abuse inq_flags bit to track enabled AEN. */
> #define SID_DMA 0x10 /* Abuse inq_flags bit to track enabled DMA. */
>
> struct ata_cmd {
>
> Modified: stable/9/sys/cam/ata/ata_xpt.c
> ==============================================================================
> --- stable/9/sys/cam/ata/ata_xpt.c Fri Apr 5 11:30:31 2013 (r249151)
> +++ stable/9/sys/cam/ata/ata_xpt.c Fri Apr 5 11:41:56 2013 (r249152)
> @@ -463,6 +463,12 @@ negotiate:
> 0, 0x02);
> break;
> case PROBE_SETAN:
> + /* Remember what transport thinks about AEN. */
> + if (softc->caps & CTS_SATA_CAPS_H_AN)
> + path->device->inq_flags |= SID_AEN;
> + else
> + path->device->inq_flags &= ~SID_AEN;
> + xpt_async(AC_GETDEV_CHANGED, path, NULL);
> cam_fill_ataio(ataio,
> 1,
> probedone,
> @@ -1157,6 +1163,12 @@ notsata:
> cts.xport_specific.sata.valid = CTS_SATA_VALID_CAPS;
> xpt_action((union ccb *)&cts);
> softc->caps = caps;
> + /* Remember what transport thinks about AEN. */
> + if (softc->caps & CTS_SATA_CAPS_H_AN)
> + path->device->inq_flags |= SID_AEN;
> + else
> + path->device->inq_flags &= ~SID_AEN;
> + xpt_async(AC_GETDEV_CHANGED, path, NULL);
> if (periph->path->device->flags & CAM_DEV_UNCONFIGURED) {
> path->device->flags &= ~CAM_DEV_UNCONFIGURED;
> xpt_acquire_device(path->device);
>
> Modified: stable/9/sys/cam/cam_ccb.h
> ==============================================================================
> --- stable/9/sys/cam/cam_ccb.h Fri Apr 5 11:30:31 2013 (r249151)
> +++ stable/9/sys/cam/cam_ccb.h Fri Apr 5 11:41:56 2013 (r249152)
> @@ -755,6 +755,7 @@ struct ccb_relsim {
> * Definitions for the asynchronous callback CCB fields.
> */
> typedef enum {
> + AC_UNIT_ATTENTION = 0x4000,/* Device reported UNIT ATTENTION */
> AC_ADVINFO_CHANGED = 0x2000,/* Advance info might have changes */
> AC_CONTRACT = 0x1000,/* A contractual callback */
> AC_GETDEV_CHANGED = 0x800,/* Getdev info might have changed */
>
> Modified: stable/9/sys/cam/cam_periph.c
> ==============================================================================
> --- stable/9/sys/cam/cam_periph.c Fri Apr 5 11:30:31 2013 (r249151)
> +++ stable/9/sys/cam/cam_periph.c Fri Apr 5 11:41:56 2013 (r249152)
> @@ -1604,6 +1604,7 @@ cam_periph_error(union ccb *ccb, cam_fla
> const char *action_string;
> cam_status status;
> int frozen, error, openings, print, lost_device;
> + int error_code, sense_key, asc, ascq;
> u_int32_t relsim_flags, timeout;
>
> print = 1;
> @@ -1770,6 +1771,12 @@ cam_periph_error(union ccb *ccb, cam_fla
> xpt_async(AC_LOST_DEVICE, newpath, NULL);
> xpt_free_path(newpath);
> }
> +
> + /* Broadcast UNIT ATTENTIONs to all periphs. */
> + } else if (scsi_extract_sense_ccb(ccb,
> + &error_code, &sense_key, &asc, &ascq) &&
> + sense_key == SSD_KEY_UNIT_ATTENTION) {
> + xpt_async(AC_UNIT_ATTENTION, orig_ccb->ccb_h.path, orig_ccb);
> }
>
> /* Attempt a retry */
>
> Modified: stable/9/sys/cam/cam_xpt.c
> ==============================================================================
> --- stable/9/sys/cam/cam_xpt.c Fri Apr 5 11:30:31 2013 (r249151)
> +++ stable/9/sys/cam/cam_xpt.c Fri Apr 5 11:41:56 2013 (r249152)
> @@ -4053,6 +4053,7 @@ xpt_async_string(u_int32_t async_code)
> case AC_GETDEV_CHANGED: return ("AC_GETDEV_CHANGED");
> case AC_CONTRACT: return ("AC_CONTRACT");
> case AC_ADVINFO_CHANGED: return ("AC_ADVINFO_CHANGED");
> + case AC_UNIT_ATTENTION: return ("AC_UNIT_ATTENTION");
> }
> return ("AC_UNKNOWN");
> }
>
> Modified: stable/9/sys/cam/scsi/scsi_cd.c
> ==============================================================================
> --- stable/9/sys/cam/scsi/scsi_cd.c Fri Apr 5 11:30:31 2013 (r249151)
> +++ stable/9/sys/cam/scsi/scsi_cd.c Fri Apr 5 11:41:56 2013 (r249152)
> @@ -97,6 +97,7 @@ typedef enum {
> CD_FLAG_NEW_DISC = 0x0002,
> CD_FLAG_DISC_LOCKED = 0x0004,
> CD_FLAG_DISC_REMOVABLE = 0x0008,
> + CD_FLAG_SAW_MEDIA = 0x0010,
> CD_FLAG_CHANGER = 0x0040,
> CD_FLAG_ACTIVE = 0x0080,
> CD_FLAG_SCHED_ON_COMP = 0x0100,
> @@ -110,6 +111,7 @@ typedef enum {
> CD_CCB_PROBE = 0x01,
> CD_CCB_BUFFER_IO = 0x02,
> CD_CCB_WAITING = 0x03,
> + CD_CCB_TUR = 0x04,
> CD_CCB_TYPE_MASK = 0x0F,
> CD_CCB_RETRY_UA = 0x10
> } cd_ccb_state;
> @@ -154,12 +156,14 @@ struct cd_softc {
> struct cam_periph *periph;
> int minimum_command_size;
> int outstanding_cmds;
> + int tur;
> struct task sysctl_task;
> struct sysctl_ctx_list sysctl_ctx;
> struct sysctl_oid *sysctl_tree;
> STAILQ_HEAD(, cd_mode_params) mode_queue;
> struct cd_tocdata toc;
> struct disk *disk;
> + struct callout mediapoll_c;
> };
>
> struct cd_page_sizes {
> @@ -281,6 +285,7 @@ static int cdsendkey(struct cam_periph
> struct dvd_authinfo *authinfo);
> static int cdreaddvdstructure(struct cam_periph *periph,
> struct dvd_struct *dvdstruct);
> +static timeout_t cdmediapoll;
>
> static struct periph_driver cddriver =
> {
> @@ -290,6 +295,9 @@ static struct periph_driver cddriver =
>
> PERIPHDRIVER_DECLARE(cd, cddriver);
>
> +#ifndef CD_DEFAULT_POLL_PERIOD
> +#define CD_DEFAULT_POLL_PERIOD 3
> +#endif
> #ifndef CD_DEFAULT_RETRY
> #define CD_DEFAULT_RETRY 4
> #endif
> @@ -303,6 +311,7 @@ PERIPHDRIVER_DECLARE(cd, cddriver);
> #define CHANGER_MAX_BUSY_SECONDS 15
> #endif
>
> +static int cd_poll_period = CD_DEFAULT_POLL_PERIOD;
> static int cd_retry_count = CD_DEFAULT_RETRY;
> static int cd_timeout = CD_DEFAULT_TIMEOUT;
> static int changer_min_busy_seconds = CHANGER_MIN_BUSY_SECONDS;
> @@ -311,6 +320,9 @@ static int changer_max_busy_seconds = CH
> static SYSCTL_NODE(_kern_cam, OID_AUTO, cd, CTLFLAG_RD, 0, "CAM CDROM driver");
> static SYSCTL_NODE(_kern_cam_cd, OID_AUTO, changer, CTLFLAG_RD, 0,
> "CD Changer");
> +SYSCTL_INT(_kern_cam_cd, OID_AUTO, poll_period, CTLFLAG_RW,
> + &cd_poll_period, 0, "Media polling period in seconds");
> +TUNABLE_INT("kern.cam.cd.poll_period", &cd_poll_period);
> SYSCTL_INT(_kern_cam_cd, OID_AUTO, retry_count, CTLFLAG_RW,
> &cd_retry_count, 0, "Normal I/O retry count");
> TUNABLE_INT("kern.cam.cd.retry_count", &cd_retry_count);
> @@ -494,6 +506,7 @@ cdcleanup(struct cam_periph *periph)
> xpt_print(periph->path, "can't remove sysctl context\n");
> }
>
> + callout_drain(&softc->mediapoll_c);
> disk_destroy(softc->disk);
> free(softc, M_DEVBUF);
> cam_periph_lock(periph);
> @@ -504,6 +517,7 @@ cdasync(void *callback_arg, u_int32_t co
> struct cam_path *path, void *arg)
> {
> struct cam_periph *periph;
> + struct cd_softc *softc;
>
> periph = (struct cam_periph *)callback_arg;
> switch (code) {
> @@ -541,10 +555,39 @@ cdasync(void *callback_arg, u_int32_t co
>
> break;
> }
> + case AC_UNIT_ATTENTION:
> + {
> + union ccb *ccb;
> + int error_code, sense_key, asc, ascq;
> +
> + softc = (struct cd_softc *)periph->softc;
> + ccb = (union ccb *)arg;
> +
> + /*
> + * Handle all media change UNIT ATTENTIONs except
> + * our own, as they will be handled by cderror().
> + */
> + if (xpt_path_periph(ccb->ccb_h.path) != periph &&
> + scsi_extract_sense_ccb(ccb,
> + &error_code, &sense_key, &asc, &ascq)) {
> + if (asc == 0x28 && ascq == 0x00)
> + disk_media_changed(softc->disk, M_NOWAIT);
> + }
> + cam_periph_async(periph, code, path, arg);
> + break;
> + }
> + case AC_SCSI_AEN:
> + softc = (struct cd_softc *)periph->softc;
> + if (softc->state == CD_STATE_NORMAL && !softc->tur) {
> + if (cam_periph_acquire(periph) == CAM_REQ_CMP) {
> + softc->tur = 1;
> + xpt_schedule(periph, CAM_PRIORITY_DEV);
> + }
> + }
> + /* FALLTHROUGH */
> case AC_SENT_BDR:
> case AC_BUS_RESET:
> {
> - struct cd_softc *softc;
> struct ccb_hdr *ccbh;
>
> softc = (struct cd_softc *)periph->softc;
> @@ -784,8 +827,8 @@ cdregister(struct cam_periph *periph, vo
> * Add an async callback so that we get
> * notified if this device goes away.
> */
> - xpt_register_async(AC_SENT_BDR | AC_BUS_RESET | AC_LOST_DEVICE,
> - cdasync, periph, periph->path);
> + xpt_register_async(AC_SENT_BDR | AC_BUS_RESET | AC_LOST_DEVICE |
> + AC_SCSI_AEN | AC_UNIT_ATTENTION, cdasync, periph, periph->path);
>
> /*
> * If the target lun is greater than 0, we most likely have a CD
> @@ -1001,6 +1044,17 @@ cdregister(struct cam_periph *periph, vo
> }
> }
>
> + /*
> + * Schedule a periodic media polling events.
> + */
> + callout_init_mtx(&softc->mediapoll_c, periph->sim->mtx, 0);
> + if ((softc->flags & CD_FLAG_DISC_REMOVABLE) &&
> + (softc->flags & CD_FLAG_CHANGER) == 0 &&
> + (cgd->inq_flags & SID_AEN) == 0 &&
> + cd_poll_period != 0)
> + callout_reset(&softc->mediapoll_c, cd_poll_period * hz,
> + cdmediapoll, periph);
> +
> cdregisterexit:
>
> if ((softc->flags & CD_FLAG_CHANGER) == 0)
> @@ -1496,8 +1550,25 @@ cdstart(struct cam_periph *periph, union
> periph->immediate_priority = CAM_PRIORITY_NONE;
> wakeup(&periph->ccb_list);
> } else if (bp == NULL) {
> - xpt_release_ccb(start_ccb);
> + if (softc->tur) {
> + softc->tur = 0;
> + csio = &start_ccb->csio;
> + scsi_test_unit_ready(csio,
> + /*retries*/ cd_retry_count,
> + cddone,
> + MSG_SIMPLE_Q_TAG,
> + SSD_FULL_SIZE,
> + cd_timeout);
> + start_ccb->ccb_h.ccb_bp = NULL;
> + start_ccb->ccb_h.ccb_state = CD_CCB_TUR;
> + xpt_action(start_ccb);
> + } else
> + xpt_release_ccb(start_ccb);
> } else {
> + if (softc->tur) {
> + softc->tur = 0;
> + cam_periph_release_locked(periph);
> + }
> bioq_remove(&softc->bio_queue, bp);
>
> scsi_read_write(&start_ccb->csio,
> @@ -1541,7 +1612,7 @@ cdstart(struct cam_periph *periph, union
>
> xpt_action(start_ccb);
> }
> - if (bp != NULL) {
> + if (bp != NULL || softc->tur) {
> /* Have more work to do, so ensure we stay scheduled */
> xpt_schedule(periph, CAM_PRIORITY_NORMAL);
> }
> @@ -1835,6 +1906,25 @@ cddone(struct cam_periph *periph, union
> wakeup(&done_ccb->ccb_h.cbfcnp);
> return;
> }
> + case CD_CCB_TUR:
> + {
> + if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
> +
> + if (cderror(done_ccb, CAM_RETRY_SELTO,
> + SF_RETRY_UA | SF_NO_RECOVERY | SF_NO_PRINT) ==
> + ERESTART)
> + return;
> + if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
> + cam_release_devq(done_ccb->ccb_h.path,
> + /*relsim_flags*/0,
> + /*reduction*/0,
> + /*timeout*/0,
> + /*getcount_only*/0);
> + }
> + xpt_release_ccb(done_ccb);
> + cam_periph_release_locked(periph);
> + return;
> + }
> default:
> break;
> }
> @@ -2826,7 +2916,7 @@ cdcheckmedia(struct cam_periph *periph)
> cdprevent(periph, PR_ALLOW);
> return (error);
> } else {
> - softc->flags |= CD_FLAG_VALID_MEDIA;
> + softc->flags |= CD_FLAG_SAW_MEDIA | CD_FLAG_VALID_MEDIA;
> softc->disk->d_sectorsize = softc->params.blksize;
> softc->disk->d_mediasize =
> (off_t)softc->params.blksize * softc->params.disksize;
> @@ -3171,6 +3261,14 @@ cderror(union ccb *ccb, u_int32_t cam_fl
> &error_code, &sense_key, &asc, &ascq)) {
> if (sense_key == SSD_KEY_ILLEGAL_REQUEST)
> error = cd6byteworkaround(ccb);
> + else if (sense_key == SSD_KEY_UNIT_ATTENTION &&
> + asc == 0x28 && ascq == 0x00)
> + disk_media_changed(softc->disk, M_NOWAIT);
> + else if (sense_key == SSD_KEY_NOT_READY &&
> + asc == 0x3a && (softc->flags & CD_FLAG_SAW_MEDIA)) {
> + softc->flags &= ~CD_FLAG_SAW_MEDIA;
> + disk_media_gone(softc->disk, M_NOWAIT);
> + }
> }
>
> if (error == ERESTART)
> @@ -3186,6 +3284,26 @@ cderror(union ccb *ccb, u_int32_t cam_fl
> &softc->saved_ccb));
> }
>
> +static void
> +cdmediapoll(void *arg)
> +{
> + struct cam_periph *periph = arg;
> + struct cd_softc *softc = periph->softc;
> +
> + if (softc->flags & CD_FLAG_CHANGER)
> + return;
> +
> + if (softc->state == CD_STATE_NORMAL && !softc->tur) {
> + if (cam_periph_acquire(periph) == CAM_REQ_CMP) {
> + softc->tur = 1;
> + xpt_schedule(periph, CAM_PRIORITY_DEV);
> + }
> + }
> + /* Queue us up again */
> + if (cd_poll_period != 0)
> + callout_schedule(&softc->mediapoll_c, cd_poll_period * hz);
> +}
> +
> /*
> * Read table of contents
> */
>
> Modified: stable/9/sys/cam/scsi/scsi_da.c
> ==============================================================================
> --- stable/9/sys/cam/scsi/scsi_da.c Fri Apr 5 11:30:31 2013 (r249151)
> +++ stable/9/sys/cam/scsi/scsi_da.c Fri Apr 5 11:41:56 2013 (r249152)
> @@ -77,6 +77,7 @@ typedef enum {
> DA_FLAG_NEW_PACK = 0x002,
> DA_FLAG_PACK_LOCKED = 0x004,
> DA_FLAG_PACK_REMOVABLE = 0x008,
> + DA_FLAG_SAW_MEDIA = 0x010,
> DA_FLAG_NEED_OTAG = 0x020,
> DA_FLAG_WENT_IDLE = 0x040,
> DA_FLAG_RETRY_UA = 0x080,
> @@ -101,6 +102,7 @@ typedef enum {
> DA_CCB_WAITING = 0x04,
> DA_CCB_DUMP = 0x05,
> DA_CCB_DELETE = 0x06,
> + DA_CCB_TUR = 0x07,
> DA_CCB_TYPE_MASK = 0x0F,
> DA_CCB_RETRY_UA = 0x10
> } da_ccb_state;
> @@ -150,6 +152,7 @@ struct da_softc {
> int unmap_max_ranges;
> int unmap_max_lba;
> int delete_running;
> + int tur;
> da_delete_methods delete_method;
> struct disk_params params;
> struct disk *disk;
> @@ -161,6 +164,7 @@ struct da_softc {
> uint64_t wwpn;
> uint8_t unmap_buf[UNMAP_MAX_RANGES * 16 + 8];
> struct scsi_read_capacity_data_long rcaplong;
> + struct callout mediapoll_c;
> };
>
> struct da_quirk_entry {
> @@ -875,6 +879,11 @@ static void dasetgeom(struct cam_periph
> size_t rcap_size);
> static timeout_t dasendorderedtag;
> static void dashutdown(void *arg, int howto);
> +static timeout_t damediapoll;
> +
> +#ifndef DA_DEFAULT_POLL_PERIOD
> +#define DA_DEFAULT_POLL_PERIOD 3
> +#endif
>
> #ifndef DA_DEFAULT_TIMEOUT
> #define DA_DEFAULT_TIMEOUT 60 /* Timeout in seconds */
> @@ -889,12 +898,16 @@ static void dashutdown(void *arg, int h
> #endif
>
>
> +static int da_poll_period = DA_DEFAULT_POLL_PERIOD;
> static int da_retry_count = DA_DEFAULT_RETRY;
> static int da_default_timeout = DA_DEFAULT_TIMEOUT;
> static int da_send_ordered = DA_DEFAULT_SEND_ORDERED;
>
> static SYSCTL_NODE(_kern_cam, OID_AUTO, da, CTLFLAG_RD, 0,
> "CAM Direct Access Disk driver");
> +SYSCTL_INT(_kern_cam_da, OID_AUTO, poll_period, CTLFLAG_RW,
> + &da_poll_period, 0, "Media polling period in seconds");
> +TUNABLE_INT("kern.cam.da.poll_period", &da_poll_period);
> SYSCTL_INT(_kern_cam_da, OID_AUTO, retry_count, CTLFLAG_RW,
> &da_retry_count, 0, "Normal I/O retry count");
> TUNABLE_INT("kern.cam.da.retry_count", &da_retry_count);
> @@ -984,6 +997,9 @@ daopen(struct disk *dp)
> (softc->quirks & DA_Q_NO_PREVENT) == 0)
> daprevent(periph, PR_PREVENT);
>
> + if (error == 0)
> + softc->flags |= DA_FLAG_SAW_MEDIA;
> +
> cam_periph_unhold(periph);
> cam_periph_unlock(periph);
>
> @@ -1068,7 +1084,8 @@ daschedule(struct cam_periph *periph)
>
> /* Check if we have more work to do. */
> if (bioq_first(&softc->bio_queue) ||
> - (!softc->delete_running && bioq_first(&softc->delete_queue))) {
> + (!softc->delete_running && bioq_first(&softc->delete_queue)) ||
> + softc->tur) {
> prio = CAM_PRIORITY_NORMAL;
> }
>
> @@ -1315,6 +1332,7 @@ dacleanup(struct cam_periph *periph)
> xpt_print(periph->path, "can't remove sysctl context\n");
> }
>
> + callout_drain(&softc->mediapoll_c);
> disk_destroy(softc->disk);
> callout_drain(&softc->sendordered_c);
> free(softc, M_DEVBUF);
> @@ -1326,6 +1344,7 @@ daasync(void *callback_arg, u_int32_t co
> struct cam_path *path, void *arg)
> {
> struct cam_periph *periph;
> + struct da_softc *softc;
>
> periph = (struct cam_periph *)callback_arg;
> switch (code) {
> @@ -1377,10 +1396,43 @@ daasync(void *callback_arg, u_int32_t co
> }
> break;
> }
> + case AC_UNIT_ATTENTION:
> + {
> + union ccb *ccb;
> + int error_code, sense_key, asc, ascq;
> +
> + softc = (struct da_softc *)periph->softc;
> + ccb = (union ccb *)arg;
> +
> + /*
> + * Handle all UNIT ATTENTIONs except our own,
> + * as they will be handled by daerror().
> + */
> + if (xpt_path_periph(ccb->ccb_h.path) != periph &&
> + scsi_extract_sense_ccb(ccb,
> + &error_code, &sense_key, &asc, &ascq)) {
> + if (asc == 0x2A && ascq == 0x09) {
> + xpt_print(ccb->ccb_h.path,
> + "capacity data has changed\n");
> + dareprobe(periph);
> + } else if (asc == 0x28 && ascq == 0x00)
> + disk_media_changed(softc->disk, M_NOWAIT);
> + }
> + cam_periph_async(periph, code, path, arg);
> + break;
> + }
> + case AC_SCSI_AEN:
> + softc = (struct da_softc *)periph->softc;
> + if (softc->state == DA_STATE_NORMAL && !softc->tur) {
> + if (cam_periph_acquire(periph) == CAM_REQ_CMP) {
> + softc->tur = 1;
> + xpt_schedule(periph, CAM_PRIORITY_DEV);
> + }
> + }
> + /* FALLTHROUGH */
> case AC_SENT_BDR:
> case AC_BUS_RESET:
> {
> - struct da_softc *softc;
> struct ccb_hdr *ccbh;
>
> softc = (struct da_softc *)periph->softc;
> @@ -1711,9 +1763,9 @@ daregister(struct cam_periph *periph, vo
> * fine without them and the only alternative
> * would be to not attach the device on failure.
> */
> - xpt_register_async(AC_SENT_BDR | AC_BUS_RESET
> - | AC_LOST_DEVICE | AC_ADVINFO_CHANGED,
> - daasync, periph, periph->path);
> + xpt_register_async(AC_SENT_BDR | AC_BUS_RESET | AC_LOST_DEVICE |
> + AC_ADVINFO_CHANGED | AC_SCSI_AEN | AC_UNIT_ATTENTION,
> + daasync, periph, periph->path);
>
> /*
> * Emit an attribute changed notification just in case
> @@ -1723,6 +1775,16 @@ daregister(struct cam_periph *periph, vo
> */
> disk_attr_changed(softc->disk, "GEOM::physpath", M_NOWAIT);
>
> + /*
> + * Schedule a periodic media polling events.
> + */
> + callout_init_mtx(&softc->mediapoll_c, periph->sim->mtx, 0);
> + if ((softc->flags & DA_FLAG_PACK_REMOVABLE) &&
> + (cgd->inq_flags & SID_AEN) == 0 &&
> + da_poll_period != 0)
> + callout_reset(&softc->mediapoll_c, da_poll_period * hz,
> + damediapoll, periph);
> +
> xpt_schedule(periph, CAM_PRIORITY_DEV);
>
> return(CAM_REQ_CMP);
> @@ -1860,9 +1922,25 @@ dastart(struct cam_periph *periph, union
> /* Run regular command. */
> bp = bioq_takefirst(&softc->bio_queue);
> if (bp == NULL) {
> - xpt_release_ccb(start_ccb);
> + if (softc->tur) {
> + softc->tur = 0;
> + scsi_test_unit_ready(&start_ccb->csio,
> + /*retries*/ da_retry_count,
> + dadone,
> + MSG_SIMPLE_Q_TAG,
> + SSD_FULL_SIZE,
> + da_default_timeout * 1000);
> + start_ccb->ccb_h.ccb_bp = NULL;
> + start_ccb->ccb_h.ccb_state = DA_CCB_TUR;
> + xpt_action(start_ccb);
> + } else
> + xpt_release_ccb(start_ccb);
> break;
> }
> + if (softc->tur) {
> + softc->tur = 0;
> + cam_periph_release_locked(periph);
> + }
>
> if ((bp->bio_flags & BIO_ORDERED) != 0 ||
> (softc->flags & DA_FLAG_NEED_OTAG) != 0) {
> @@ -2429,6 +2507,25 @@ dadone(struct cam_periph *periph, union
> case DA_CCB_DUMP:
> /* No-op. We're polling */
> return;
> + case DA_CCB_TUR:
> + {
> + if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
> +
> + if (daerror(done_ccb, CAM_RETRY_SELTO,
> + SF_RETRY_UA | SF_NO_RECOVERY | SF_NO_PRINT) ==
> + ERESTART)
> + return;
> + if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
> + cam_release_devq(done_ccb->ccb_h.path,
> + /*relsim_flags*/0,
> + /*reduction*/0,
> + /*timeout*/0,
> + /*getcount_only*/0);
> + }
> + xpt_release_ccb(done_ccb);
> + cam_periph_release_locked(periph);
> + return;
> + }
> default:
> break;
> }
> @@ -2489,6 +2586,13 @@ daerror(union ccb *ccb, u_int32_t cam_fl
> xpt_print(periph->path, "capacity data has changed\n");
> dareprobe(periph);
> sense_flags |= SF_NO_PRINT;
> + } else if (sense_key == SSD_KEY_UNIT_ATTENTION &&
> + asc == 0x28 && ascq == 0x00)
> + disk_media_changed(softc->disk, M_NOWAIT);
> + else if (sense_key == SSD_KEY_NOT_READY &&
> + asc == 0x3a && (softc->flags & DA_FLAG_SAW_MEDIA)) {
> + softc->flags &= ~DA_FLAG_SAW_MEDIA;
> + disk_media_gone(softc->disk, M_NOWAIT);
> }
> }
> if (error == ERESTART)
> @@ -2505,6 +2609,23 @@ daerror(union ccb *ccb, u_int32_t cam_fl
> }
>
> static void
> +damediapoll(void *arg)
> +{
> + struct cam_periph *periph = arg;
> + struct da_softc *softc = periph->softc;
> +
> + if (softc->state == DA_STATE_NORMAL && !softc->tur) {
> + if (cam_periph_acquire(periph) == CAM_REQ_CMP) {
> + softc->tur = 1;
> + daschedule(periph);
> + }
> + }
> + /* Queue us up again */
> + if (da_poll_period != 0)
> + callout_schedule(&softc->mediapoll_c, da_poll_period * hz);
> +}
> +
> +static void
> daprevent(struct cam_periph *periph, int action)
> {
> struct da_softc *softc;
>
> Modified: stable/9/sys/geom/geom.h
> ==============================================================================
> --- stable/9/sys/geom/geom.h Fri Apr 5 11:30:31 2013 (r249151)
> +++ stable/9/sys/geom/geom.h Fri Apr 5 11:41:56 2013 (r249152)
> @@ -169,7 +169,9 @@ struct g_consumer {
> struct g_provider *provider;
> LIST_ENTRY(g_consumer) consumers; /* XXX: better name */
> int acr, acw, ace;
> - int spoiled;
> + int flags;
> +#define G_CF_SPOILED 0x1
> +#define G_CF_ORPHAN 0x4
> struct devstat *stat;
> u_int nstart, nend;
>
> @@ -242,6 +244,8 @@ int g_post_event(g_event_t *func, void *
> int g_waitfor_event(g_event_t *func, void *arg, int flag, ...);
> void g_cancel_event(void *ref);
> int g_attr_changed(struct g_provider *pp, const char *attr, int flag);
> +int g_media_changed(struct g_provider *pp, int flag);
> +int g_media_gone(struct g_provider *pp, int flag);
> void g_orphan_provider(struct g_provider *pp, int error);
> void g_waitidlelock(void);
>
>
> Modified: stable/9/sys/geom/geom_dev.c
> ==============================================================================
> --- stable/9/sys/geom/geom_dev.c Fri Apr 5 11:30:31 2013 (r249151)
> +++ stable/9/sys/geom/geom_dev.c Fri Apr 5 11:41:56 2013 (r249152)
> @@ -43,6 +43,7 @@ __FBSDID("$FreeBSD$");
> #include <sys/conf.h>
> #include <sys/ctype.h>
> #include <sys/bio.h>
> +#include <sys/bus.h>
> #include <sys/lock.h>
> #include <sys/mutex.h>
> #include <sys/proc.h>
> @@ -105,6 +106,21 @@ g_dev_print(void)
> static void
> g_dev_attrchanged(struct g_consumer *cp, const char *attr)
> {
> + struct cdev *dev;
> + char buf[SPECNAMELEN + 6];
> +
> + if (strcmp(attr, "GEOM::media") == 0) {
> + dev = cp->geom->softc;
> + snprintf(buf, sizeof(buf), "cdev=%s", dev->si_name);
> + devctl_notify_f("DEVFS", "CDEV", "MEDIACHANGE", buf, M_WAITOK);
> + dev = cp->cp_alias_dev;
> + if (dev != NULL) {
> + snprintf(buf, sizeof(buf), "cdev=%s", dev->si_name);
> + devctl_notify_f("DEVFS", "CDEV", "MEDIACHANGE", buf,
> + M_WAITOK);
> + }
> + return;
> + }
>
> if (strcmp(attr, "GEOM::physpath") != 0)
> return;
> @@ -119,7 +135,6 @@ g_dev_attrchanged(struct g_consumer *cp,
> g_io_getattr("GEOM::physpath", cp, &physpath_len, physpath);
> g_access(cp, -1, 0, 0);
> if (error == 0 && strlen(physpath) != 0) {
> - struct cdev *dev;
> struct cdev *old_alias_dev;
> struct cdev **alias_devp;
>
> @@ -161,9 +176,6 @@ g_dev_taste(struct g_class *mp, struct g
>
> g_trace(G_T_TOPOLOGY, "dev_taste(%s,%s)", mp->name, pp->name);
> g_topology_assert();
> - LIST_FOREACH(cp, &pp->consumers, consumers)
> - if (cp->geom->class == mp)
> - return (NULL);
> gp = g_new_geomf(mp, "%s", pp->name);
> cp = g_new_consumer(gp);
> error = g_attach(cp, pp);
>
> Modified: stable/9/sys/geom/geom_disk.c
> ==============================================================================
> --- stable/9/sys/geom/geom_disk.c Fri Apr 5 11:30:31 2013 (r249151)
> +++ stable/9/sys/geom/geom_disk.c Fri Apr 5 11:41:56 2013 (r249152)
> @@ -649,6 +649,32 @@ disk_attr_changed(struct disk *dp, const
> (void)g_attr_changed(pp, attr, flag);
> }
>
> +void
> +disk_media_changed(struct disk *dp, int flag)
> +{
> + struct g_geom *gp;
> + struct g_provider *pp;
> +
> + gp = dp->d_geom;
> + if (gp != NULL) {
> + LIST_FOREACH(pp, &gp->provider, provider)
> + g_media_changed(pp, flag);
> + }
> +}
> +
> +void
> +disk_media_gone(struct disk *dp, int flag)
> +{
> + struct g_geom *gp;
> + struct g_provider *pp;
> +
> + gp = dp->d_geom;
> + if (gp != NULL) {
> + LIST_FOREACH(pp, &gp->provider, provider)
> + g_media_gone(pp, flag);
> + }
> +}
> +
> static void
> g_kern_disks(void *p, int flag __unused)
> {
>
> Modified: stable/9/sys/geom/geom_disk.h
> ==============================================================================
> --- stable/9/sys/geom/geom_disk.h Fri Apr 5 11:30:31 2013 (r249151)
> +++ stable/9/sys/geom/geom_disk.h Fri Apr 5 11:41:56 2013 (r249152)
> @@ -112,6 +112,8 @@ void disk_create(struct disk *disk, int
> void disk_destroy(struct disk *disk);
> void disk_gone(struct disk *disk);
> void disk_attr_changed(struct disk *dp, const char *attr, int flag);
> +void disk_media_changed(struct disk *dp, int flag);
> +void disk_media_gone(struct disk *dp, int flag);
>
> #define DISK_VERSION_00 0x58561059
> #define DISK_VERSION_01 0x5856105a
>
> Modified: stable/9/sys/geom/geom_event.c
> ==============================================================================
> --- stable/9/sys/geom/geom_event.c Fri Apr 5 11:30:31 2013 (r249151)
> +++ stable/9/sys/geom/geom_event.c Fri Apr 5 11:41:56 2013 (r249152)
> @@ -202,14 +202,12 @@ g_orphan_register(struct g_provider *pp)
> * Tell all consumers the bad news.
> * Don't be surprised if they self-destruct.
> */
> - cp = LIST_FIRST(&pp->consumers);
> - while (cp != NULL) {
> - cp2 = LIST_NEXT(cp, consumers);
> + LIST_FOREACH_SAFE(cp, &pp->consumers, consumers, cp2) {
> KASSERT(cp->geom->orphan != NULL,
> ("geom %s has no orphan, class %s",
> cp->geom->name, cp->geom->class->name));
> + cp->flags |= G_CF_ORPHAN;
> cp->geom->orphan(cp);
> - cp = cp2;
> }
> if (LIST_EMPTY(&pp->consumers) && wf)
> g_destroy_provider(pp);
>
> Modified: stable/9/sys/geom/geom_io.c
> ==============================================================================
> --- stable/9/sys/geom/geom_io.c Fri Apr 5 11:30:31 2013 (r249151)
> +++ stable/9/sys/geom/geom_io.c Fri Apr 5 11:41:56 2013 (r249152)
> @@ -311,6 +311,8 @@ g_io_check(struct bio *bp)
> /* if provider is marked for error, don't disturb. */
> if (pp->error)
> return (pp->error);
> + if (cp->flags & G_CF_ORPHAN)
> + return (ENXIO);
>
> switch(bp->bio_cmd) {
> case BIO_READ:
>
> Modified: stable/9/sys/geom/geom_slice.c
> ==============================================================================
> --- stable/9/sys/geom/geom_slice.c Fri Apr 5 11:30:31 2013 (r249151)
> +++ stable/9/sys/geom/geom_slice.c Fri Apr 5 11:41:56 2013 (r249152)
> @@ -465,6 +465,7 @@ g_slice_spoiled(struct g_consumer *cp)
> g_topology_assert();
> gp = cp->geom;
> g_trace(G_T_TOPOLOGY, "g_slice_spoiled(%p/%s)", cp, gp->name);
> + cp->flags |= G_CF_ORPHAN;
> gsp = gp->softc;
> gp->softc = NULL;
> g_slice_free(gsp);
>
> Modified: stable/9/sys/geom/geom_subr.c
> ==============================================================================
> --- stable/9/sys/geom/geom_subr.c Fri Apr 5 11:30:31 2013 (r249151)
> +++ stable/9/sys/geom/geom_subr.c Fri Apr 5 11:41:56 2013 (r249152)
> @@ -260,10 +260,11 @@ g_modevent(module_t mod, int type, void
> static void
> g_retaste_event(void *arg, int flag)
> {
> - struct g_class *cp, *mp;
> - struct g_geom *gp, *gp2;
> + struct g_class *mp, *mp2;
> + struct g_geom *gp;
> struct g_hh00 *hh;
> struct g_provider *pp;
> + struct g_consumer *cp;
>
> g_topology_assert();
> if (flag == EV_CANCEL) /* XXX: can't happen ? */
> @@ -280,17 +281,20 @@ g_retaste_event(void *arg, int flag)
> }
> g_trace(G_T_TOPOLOGY, "g_retaste(%s)", mp->name);
>
> - LIST_FOREACH(cp, &g_classes, class) {
> - LIST_FOREACH(gp, &cp->geom, geom) {
> + LIST_FOREACH(mp2, &g_classes, class) {
> + LIST_FOREACH(gp, &mp2->geom, geom) {
> LIST_FOREACH(pp, &gp->provider, provider) {
> if (pp->acr || pp->acw || pp->ace)
> continue;
> - LIST_FOREACH(gp2, &mp->geom, geom) {
> - if (!strcmp(pp->name, gp2->name))
> + LIST_FOREACH(cp, &pp->consumers, consumers) {
> + if (cp->geom->class == mp &&
> + (cp->flags & G_CF_ORPHAN) == 0)
> break;
> }
> - if (gp2 != NULL)
> - g_wither_geom(gp2, ENXIO);
> + if (cp != NULL) {
> + cp->flags |= G_CF_ORPHAN;
> + g_wither_geom(cp->geom, ENXIO);
> + }
> mp->taste(mp, pp, 0);
> g_topology_assert();
> }
> @@ -531,7 +535,7 @@ g_new_provider_event(void *arg, int flag
> {
> struct g_class *mp;
> struct g_provider *pp;
> - struct g_consumer *cp;
> + struct g_consumer *cp, *next_cp;
>
> g_topology_assert();
> if (flag == EV_CANCEL)
> @@ -542,11 +546,17 @@ g_new_provider_event(void *arg, int flag
> G_VALID_PROVIDER(pp);
> KASSERT(!(pp->flags & G_PF_WITHER),
> ("g_new_provider_event but withered"));
> + LIST_FOREACH_SAFE(cp, &pp->consumers, consumers, next_cp) {
> + if ((cp->flags & G_CF_ORPHAN) == 0 &&
> + cp->geom->attrchanged != NULL)
> + cp->geom->attrchanged(cp, "GEOM::media");
> + }
> LIST_FOREACH(mp, &g_classes, class) {
> if (mp->taste == NULL)
> continue;
> LIST_FOREACH(cp, &pp->consumers, consumers)
> - if (cp->geom->class == mp)
> + if (cp->geom->class == mp &&
> + (cp->flags & G_CF_ORPHAN) == 0)
> break;
> if (cp != NULL)
> continue;
> @@ -803,7 +813,7 @@ g_access(struct g_consumer *cp, int dcr,
> * are probably just ahead of the event telling us that. Fail
> * now rather than having to unravel this later.
> */
> - if (cp->geom->spoiled != NULL && cp->spoiled &&
> + if (cp->geom->spoiled != NULL && (cp->flags & G_CF_SPOILED) &&
> (dcr > 0 || dcw > 0 || dce > 0))
> return (ENXIO);
>
> @@ -953,6 +963,7 @@ g_std_spoiled(struct g_consumer *cp)
> g_topology_assert();
> G_VALID_CONSUMER(cp);
> g_trace(G_T_TOPOLOGY, "g_std_spoiled(%p)", cp);
> + cp->flags |= G_CF_ORPHAN;
> g_detach(cp);
> gp = cp->geom;
> LIST_FOREACH(pp, &gp->provider, provider)
> @@ -988,9 +999,9 @@ g_spoil_event(void *arg, int flag)
> G_VALID_PROVIDER(pp);
> for (cp = LIST_FIRST(&pp->consumers); cp != NULL; cp = cp2) {
> cp2 = LIST_NEXT(cp, consumers);
> - if (!cp->spoiled)
> + if ((cp->flags & G_CF_SPOILED) == 0)
> continue;
> - cp->spoiled = 0;
> + cp->flags &= ~G_CF_SPOILED;
> if (cp->geom->spoiled == NULL)
> continue;
> cp->geom->spoiled(cp);
> @@ -1015,11 +1026,54 @@ g_spoil(struct g_provider *pp, struct g_
> KASSERT(cp2->acw == 0, ("spoiling cp->acw = %d", cp2->acw));
> */
> KASSERT(cp2->ace == 0, ("spoiling cp->ace = %d", cp2->ace));
> - cp2->spoiled++;
> + cp2->flags |= G_CF_SPOILED;
> }
> g_post_event(g_spoil_event, pp, M_WAITOK, pp, NULL);
> }
>
> +static void
> +g_media_changed_event(void *arg, int flag)
> +{
> + struct g_provider *pp;
> + int retaste;
> +
> + g_topology_assert();
> + if (flag == EV_CANCEL)
> + return;
> + pp = arg;
> + G_VALID_PROVIDER(pp);
> +
> + /*
> + * If provider was not open for writing, queue retaste after spoiling.
> + * If it was, retaste will happen automatically on close.
> + */
> + retaste = (pp->acw == 0 && pp->error == 0 &&
> + !(pp->geom->flags & G_GEOM_WITHER));
> + g_spoil_event(arg, flag);
> + if (retaste)
> + g_post_event(g_new_provider_event, pp, M_WAITOK, pp, NULL);
> +}
> +
> +int
> +g_media_changed(struct g_provider *pp, int flag)
> +{
> + struct g_consumer *cp;
> +
> + LIST_FOREACH(cp, &pp->consumers, consumers)
> + cp->flags |= G_CF_SPOILED;
> + return (g_post_event(g_media_changed_event, pp, flag, pp, NULL));
> +}
> +
> +int
> +g_media_gone(struct g_provider *pp, int flag)
> +{
> + struct g_consumer *cp;
> +
> + LIST_FOREACH(cp, &pp->consumers, consumers)
> + cp->flags |= G_CF_SPOILED;
> + return (g_post_event(g_spoil_event, pp, flag, pp, NULL));
> +}
> +
> int
> g_getattr__(const char *attr, struct g_consumer *cp, void *var, int len)
> {
> @@ -1175,15 +1229,15 @@ db_show_geom_consumer(int indent, struct
> cp->provider);
> }
> gprintln(" access: r%dw%de%d", cp->acr, cp->acw, cp->ace);
> - gprintln(" spoiled: %d", cp->spoiled);
> + gprintln(" flags: 0x%04x", cp->flags);
> gprintln(" nstart: %u", cp->nstart);
> gprintln(" nend: %u", cp->nend);
> } else {
> gprintf("consumer: %p (%s), access=r%dw%de%d", cp,
> cp->provider != NULL ? cp->provider->name : "none",
> cp->acr, cp->acw, cp->ace);
> - if (cp->spoiled)
> - db_printf(", spoiled=%d", cp->spoiled);
> + if (cp->flags)
> + db_printf(", flags=0x%04x", cp->flags);
> db_printf("\n");
> }
> }
>
> Modified: stable/9/sys/geom/part/g_part.c
> ==============================================================================
> --- stable/9/sys/geom/part/g_part.c Fri Apr 5 11:30:31 2013 (r249151)
> +++ stable/9/sys/geom/part/g_part.c Fri Apr 5 11:41:56 2013 (r249152)
> @@ -2055,6 +2055,7 @@ g_part_spoiled(struct g_consumer *cp)
> G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, cp->provider->name));
> g_topology_assert();
>
> + cp
More information about the svn-src-stable-9
mailing list