3246 ZFS I/O deadman thread
[zfs.git] / module / zfs / zio_inject.c
index b3469fd..eb589c4 100644 (file)
@@ -19,8 +19,8 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012 by Delphix. All rights reserved.
  */
 
 /*
 #include <sys/arc.h>
 #include <sys/zio_impl.h>
 #include <sys/zfs_ioctl.h>
-#include <sys/spa_impl.h>
 #include <sys/vdev_impl.h>
+#include <sys/dmu_objset.h>
 #include <sys/fs/zfs.h>
 
-uint32_t zio_injection_enabled;
+uint32_t zio_injection_enabled = 0;
 
 typedef struct inject_handler {
        int                     zi_id;
@@ -70,8 +70,9 @@ zio_match_handler(zbookmark_t *zb, uint64_t type,
        /*
         * Check for a match against the MOS, which is based on type
         */
-       if (zb->zb_objset == 0 && record->zi_objset == 0 &&
-           record->zi_object == 0) {
+       if (zb->zb_objset == DMU_META_OBJSET &&
+           record->zi_objset == DMU_META_OBJSET &&
+           record->zi_object == DMU_META_DNODE_OBJECT) {
                if (record->zi_type == DMU_OT_NONE ||
                    type == record->zi_type)
                        return (record->zi_freq == 0 ||
@@ -96,6 +97,31 @@ zio_match_handler(zbookmark_t *zb, uint64_t type,
 }
 
 /*
+ * Panic the system when a config change happens in the function
+ * specified by tag.
+ */
+void
+zio_handle_panic_injection(spa_t *spa, char *tag, uint64_t type)
+{
+       inject_handler_t *handler;
+
+       rw_enter(&inject_lock, RW_READER);
+
+       for (handler = list_head(&inject_handlers); handler != NULL;
+           handler = list_next(&inject_handlers, handler)) {
+
+               if (spa != handler->zi_spa)
+                       continue;
+
+               if (handler->zi_record.zi_type == type &&
+                   strcmp(tag, handler->zi_record.zi_func) == 0)
+                       panic("Panic requested in function %s\n", tag);
+       }
+
+       rw_exit(&inject_lock);
+}
+
+/*
  * Determine if the I/O in question should return failure.  Returns the errno
  * to be returned to the caller.
  */
@@ -122,12 +148,8 @@ zio_handle_fault_injection(zio_t *zio, int error)
        for (handler = list_head(&inject_handlers); handler != NULL;
            handler = list_next(&inject_handlers, handler)) {
 
-               /* Ignore errors not destined for this pool */
-               if (zio->io_spa != handler->zi_spa)
-                       continue;
-
-               /* Ignore device errors */
-               if (handler->zi_record.zi_guid != 0)
+               if (zio->io_spa != handler->zi_spa ||
+                   handler->zi_record.zi_cmd != ZINJECT_DATA_FAULT)
                        continue;
 
                /* If this handler matches, return EIO */
@@ -159,7 +181,7 @@ zio_handle_label_injection(zio_t *zio, int error)
        int label;
        int ret = 0;
 
-       if (offset + zio->io_size > VDEV_LABEL_START_SIZE &&
+       if (offset >= VDEV_LABEL_START_SIZE &&
            offset < vd->vdev_psize - VDEV_LABEL_END_SIZE)
                return (0);
 
@@ -170,8 +192,7 @@ zio_handle_label_injection(zio_t *zio, int error)
                uint64_t start = handler->zi_record.zi_start;
                uint64_t end = handler->zi_record.zi_end;
 
-               /* Ignore device only faults */
-               if (handler->zi_record.zi_start == 0)
+               if (handler->zi_record.zi_cmd != ZINJECT_LABEL_FAULT)
                        continue;
 
                /*
@@ -195,21 +216,44 @@ zio_handle_label_injection(zio_t *zio, int error)
 
 
 int
-zio_handle_device_injection(vdev_t *vd, int error)
+zio_handle_device_injection(vdev_t *vd, zio_t *zio, int error)
 {
        inject_handler_t *handler;
        int ret = 0;
 
+       /*
+        * We skip over faults in the labels unless it's during
+        * device open (i.e. zio == NULL).
+        */
+       if (zio != NULL) {
+               uint64_t offset = zio->io_offset;
+
+               if (offset < VDEV_LABEL_START_SIZE ||
+                   offset >= vd->vdev_psize - VDEV_LABEL_END_SIZE)
+                       return (0);
+       }
+
        rw_enter(&inject_lock, RW_READER);
 
        for (handler = list_head(&inject_handlers); handler != NULL;
            handler = list_next(&inject_handlers, handler)) {
 
-               /* Ignore label specific faults */
-               if (handler->zi_record.zi_start != 0)
+               if (handler->zi_record.zi_cmd != ZINJECT_DEVICE_FAULT)
                        continue;
 
                if (vd->vdev_guid == handler->zi_record.zi_guid) {
+                       if (handler->zi_record.zi_failfast &&
+                           (zio == NULL || (zio->io_flags &
+                           (ZIO_FLAG_IO_RETRY | ZIO_FLAG_TRYHARD)))) {
+                               continue;
+                       }
+
+                       /* Handle type specific I/O failures */
+                       if (zio != NULL &&
+                           handler->zi_record.zi_iotype != ZIO_TYPES &&
+                           handler->zi_record.zi_iotype != zio->io_type)
+                               continue;
+
                        if (handler->zi_record.zi_error == error) {
                                /*
                                 * For a failed open, pretend like the device
@@ -218,6 +262,16 @@ zio_handle_device_injection(vdev_t *vd, int error)
                                if (error == ENXIO)
                                        vd->vdev_stat.vs_aux =
                                            VDEV_AUX_OPEN_FAILED;
+
+                               /*
+                                * Treat these errors as if they had been
+                                * retried so that all the appropriate stats
+                                * and FMA events are generated.
+                                */
+                               if (!handler->zi_record.zi_failfast &&
+                                   zio != NULL)
+                                       zio->io_flags |= ZIO_FLAG_IO_RETRY;
+
                                ret = error;
                                break;
                        }
@@ -234,6 +288,107 @@ zio_handle_device_injection(vdev_t *vd, int error)
 }
 
 /*
+ * Simulate hardware that ignores cache flushes.  For requested number
+ * of seconds nix the actual writing to disk.
+ */
+void
+zio_handle_ignored_writes(zio_t *zio)
+{
+       inject_handler_t *handler;
+
+       rw_enter(&inject_lock, RW_READER);
+
+       for (handler = list_head(&inject_handlers); handler != NULL;
+           handler = list_next(&inject_handlers, handler)) {
+
+               /* Ignore errors not destined for this pool */
+               if (zio->io_spa != handler->zi_spa ||
+                   handler->zi_record.zi_cmd != ZINJECT_IGNORED_WRITES)
+                       continue;
+
+               /*
+                * Positive duration implies # of seconds, negative
+                * a number of txgs
+                */
+               if (handler->zi_record.zi_timer == 0) {
+                       if (handler->zi_record.zi_duration > 0)
+                               handler->zi_record.zi_timer = ddi_get_lbolt64();
+                       else
+                               handler->zi_record.zi_timer = zio->io_txg;
+               }
+
+               /* Have a "problem" writing 60% of the time */
+               if (spa_get_random(100) < 60)
+                       zio->io_pipeline &= ~ZIO_VDEV_IO_STAGES;
+               break;
+       }
+
+       rw_exit(&inject_lock);
+}
+
+void
+spa_handle_ignored_writes(spa_t *spa)
+{
+       inject_handler_t *handler;
+
+       if (zio_injection_enabled == 0)
+               return;
+
+       rw_enter(&inject_lock, RW_READER);
+
+       for (handler = list_head(&inject_handlers); handler != NULL;
+           handler = list_next(&inject_handlers, handler)) {
+
+               if (spa != handler->zi_spa ||
+                   handler->zi_record.zi_cmd != ZINJECT_IGNORED_WRITES)
+                       continue;
+
+               if (handler->zi_record.zi_duration > 0) {
+                       VERIFY(handler->zi_record.zi_timer == 0 ||
+                           handler->zi_record.zi_timer +
+                           handler->zi_record.zi_duration * hz >
+                           ddi_get_lbolt64());
+               } else {
+                       /* duration is negative so the subtraction here adds */
+                       VERIFY(handler->zi_record.zi_timer == 0 ||
+                           handler->zi_record.zi_timer -
+                           handler->zi_record.zi_duration >=
+                           spa_syncing_txg(spa));
+               }
+       }
+
+       rw_exit(&inject_lock);
+}
+
+uint64_t
+zio_handle_io_delay(zio_t *zio)
+{
+       vdev_t *vd = zio->io_vd;
+       inject_handler_t *handler;
+       uint64_t seconds = 0;
+
+       if (zio_injection_enabled == 0)
+               return (0);
+
+       rw_enter(&inject_lock, RW_READER);
+
+       for (handler = list_head(&inject_handlers); handler != NULL;
+           handler = list_next(&inject_handlers, handler)) {
+
+               if (handler->zi_record.zi_cmd != ZINJECT_DELAY_IO)
+                       continue;
+
+               if (vd->vdev_guid == handler->zi_record.zi_guid) {
+                       seconds = handler->zi_record.zi_timer;
+                       break;
+               }
+
+       }
+       rw_exit(&inject_lock);
+       return (seconds);
+}
+
+/*
  * Create a new handler for the given record.  We add it to the list, adding
  * a reference to the spa_t in the process.  We increment zio_injection_enabled,
  * which is the switch to trigger all fault injection.
@@ -330,7 +485,6 @@ int
 zio_clear_fault(int id)
 {
        inject_handler_t *handler;
-       int ret;
 
        rw_enter(&inject_lock, RW_WRITER);
 
@@ -340,18 +494,18 @@ zio_clear_fault(int id)
                        break;
 
        if (handler == NULL) {
-               ret = ENOENT;
-       } else {
-               list_remove(&inject_handlers, handler);
-               spa_inject_delref(handler->zi_spa);
-               kmem_free(handler, sizeof (inject_handler_t));
-               atomic_add_32(&zio_injection_enabled, -1);
-               ret = 0;
+               rw_exit(&inject_lock);
+               return (ENOENT);
        }
 
+       list_remove(&inject_handlers, handler);
        rw_exit(&inject_lock);
 
-       return (ret);
+       spa_inject_delref(handler->zi_spa);
+       kmem_free(handler, sizeof (inject_handler_t));
+       atomic_add_32(&zio_injection_enabled, -1);
+
+       return (0);
 }
 
 void
@@ -368,3 +522,8 @@ zio_inject_fini(void)
        list_destroy(&inject_handlers);
        rw_destroy(&inject_lock);
 }
+
+#if defined(_KERNEL) && defined(HAVE_SPL)
+module_param(zio_injection_enabled, int, 0644);
+MODULE_PARM_DESC(zio_injection_enabled, "Enable fault injection");
+#endif