|
@@ -1,213 +0,0 @@
|
|
|
-/*
|
|
|
- * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
|
|
|
- *
|
|
|
- * This file is released under the GPL.
|
|
|
- *
|
|
|
- * Multipath hardware handler registration.
|
|
|
- */
|
|
|
-
|
|
|
-#include "dm.h"
|
|
|
-#include "dm-hw-handler.h"
|
|
|
-
|
|
|
-#include <linux/slab.h>
|
|
|
-
|
|
|
-struct hwh_internal {
|
|
|
- struct hw_handler_type hwht;
|
|
|
-
|
|
|
- struct list_head list;
|
|
|
- long use;
|
|
|
-};
|
|
|
-
|
|
|
-#define hwht_to_hwhi(__hwht) container_of((__hwht), struct hwh_internal, hwht)
|
|
|
-
|
|
|
-static LIST_HEAD(_hw_handlers);
|
|
|
-static DECLARE_RWSEM(_hwh_lock);
|
|
|
-
|
|
|
-static struct hwh_internal *__find_hw_handler_type(const char *name)
|
|
|
-{
|
|
|
- struct hwh_internal *hwhi;
|
|
|
-
|
|
|
- list_for_each_entry(hwhi, &_hw_handlers, list) {
|
|
|
- if (!strcmp(name, hwhi->hwht.name))
|
|
|
- return hwhi;
|
|
|
- }
|
|
|
-
|
|
|
- return NULL;
|
|
|
-}
|
|
|
-
|
|
|
-static struct hwh_internal *get_hw_handler(const char *name)
|
|
|
-{
|
|
|
- struct hwh_internal *hwhi;
|
|
|
-
|
|
|
- down_read(&_hwh_lock);
|
|
|
- hwhi = __find_hw_handler_type(name);
|
|
|
- if (hwhi) {
|
|
|
- if ((hwhi->use == 0) && !try_module_get(hwhi->hwht.module))
|
|
|
- hwhi = NULL;
|
|
|
- else
|
|
|
- hwhi->use++;
|
|
|
- }
|
|
|
- up_read(&_hwh_lock);
|
|
|
-
|
|
|
- return hwhi;
|
|
|
-}
|
|
|
-
|
|
|
-struct hw_handler_type *dm_get_hw_handler(const char *name)
|
|
|
-{
|
|
|
- struct hwh_internal *hwhi;
|
|
|
-
|
|
|
- if (!name)
|
|
|
- return NULL;
|
|
|
-
|
|
|
- hwhi = get_hw_handler(name);
|
|
|
- if (!hwhi) {
|
|
|
- request_module("dm-%s", name);
|
|
|
- hwhi = get_hw_handler(name);
|
|
|
- }
|
|
|
-
|
|
|
- return hwhi ? &hwhi->hwht : NULL;
|
|
|
-}
|
|
|
-
|
|
|
-void dm_put_hw_handler(struct hw_handler_type *hwht)
|
|
|
-{
|
|
|
- struct hwh_internal *hwhi;
|
|
|
-
|
|
|
- if (!hwht)
|
|
|
- return;
|
|
|
-
|
|
|
- down_read(&_hwh_lock);
|
|
|
- hwhi = __find_hw_handler_type(hwht->name);
|
|
|
- if (!hwhi)
|
|
|
- goto out;
|
|
|
-
|
|
|
- if (--hwhi->use == 0)
|
|
|
- module_put(hwhi->hwht.module);
|
|
|
-
|
|
|
- BUG_ON(hwhi->use < 0);
|
|
|
-
|
|
|
- out:
|
|
|
- up_read(&_hwh_lock);
|
|
|
-}
|
|
|
-
|
|
|
-static struct hwh_internal *_alloc_hw_handler(struct hw_handler_type *hwht)
|
|
|
-{
|
|
|
- struct hwh_internal *hwhi = kzalloc(sizeof(*hwhi), GFP_KERNEL);
|
|
|
-
|
|
|
- if (hwhi)
|
|
|
- hwhi->hwht = *hwht;
|
|
|
-
|
|
|
- return hwhi;
|
|
|
-}
|
|
|
-
|
|
|
-int dm_register_hw_handler(struct hw_handler_type *hwht)
|
|
|
-{
|
|
|
- int r = 0;
|
|
|
- struct hwh_internal *hwhi = _alloc_hw_handler(hwht);
|
|
|
-
|
|
|
- if (!hwhi)
|
|
|
- return -ENOMEM;
|
|
|
-
|
|
|
- down_write(&_hwh_lock);
|
|
|
-
|
|
|
- if (__find_hw_handler_type(hwht->name)) {
|
|
|
- kfree(hwhi);
|
|
|
- r = -EEXIST;
|
|
|
- } else
|
|
|
- list_add(&hwhi->list, &_hw_handlers);
|
|
|
-
|
|
|
- up_write(&_hwh_lock);
|
|
|
-
|
|
|
- return r;
|
|
|
-}
|
|
|
-
|
|
|
-int dm_unregister_hw_handler(struct hw_handler_type *hwht)
|
|
|
-{
|
|
|
- struct hwh_internal *hwhi;
|
|
|
-
|
|
|
- down_write(&_hwh_lock);
|
|
|
-
|
|
|
- hwhi = __find_hw_handler_type(hwht->name);
|
|
|
- if (!hwhi) {
|
|
|
- up_write(&_hwh_lock);
|
|
|
- return -EINVAL;
|
|
|
- }
|
|
|
-
|
|
|
- if (hwhi->use) {
|
|
|
- up_write(&_hwh_lock);
|
|
|
- return -ETXTBSY;
|
|
|
- }
|
|
|
-
|
|
|
- list_del(&hwhi->list);
|
|
|
-
|
|
|
- up_write(&_hwh_lock);
|
|
|
-
|
|
|
- kfree(hwhi);
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-unsigned dm_scsi_err_handler(struct hw_handler *hwh, struct bio *bio)
|
|
|
-{
|
|
|
-#if 0
|
|
|
- int sense_key, asc, ascq;
|
|
|
-
|
|
|
- if (bio->bi_error & BIO_SENSE) {
|
|
|
- /* FIXME: This is just an initial guess. */
|
|
|
- /* key / asc / ascq */
|
|
|
- sense_key = (bio->bi_error >> 16) & 0xff;
|
|
|
- asc = (bio->bi_error >> 8) & 0xff;
|
|
|
- ascq = bio->bi_error & 0xff;
|
|
|
-
|
|
|
- switch (sense_key) {
|
|
|
- /* This block as a whole comes from the device.
|
|
|
- * So no point retrying on another path. */
|
|
|
- case 0x03: /* Medium error */
|
|
|
- case 0x05: /* Illegal request */
|
|
|
- case 0x07: /* Data protect */
|
|
|
- case 0x08: /* Blank check */
|
|
|
- case 0x0a: /* copy aborted */
|
|
|
- case 0x0c: /* obsolete - no clue ;-) */
|
|
|
- case 0x0d: /* volume overflow */
|
|
|
- case 0x0e: /* data miscompare */
|
|
|
- case 0x0f: /* reserved - no idea either. */
|
|
|
- return MP_ERROR_IO;
|
|
|
-
|
|
|
- /* For these errors it's unclear whether they
|
|
|
- * come from the device or the controller.
|
|
|
- * So just lets try a different path, and if
|
|
|
- * it eventually succeeds, user-space will clear
|
|
|
- * the paths again... */
|
|
|
- case 0x02: /* Not ready */
|
|
|
- case 0x04: /* Hardware error */
|
|
|
- case 0x09: /* vendor specific */
|
|
|
- case 0x0b: /* Aborted command */
|
|
|
- return MP_FAIL_PATH;
|
|
|
-
|
|
|
- case 0x06: /* Unit attention - might want to decode */
|
|
|
- if (asc == 0x04 && ascq == 0x01)
|
|
|
- /* "Unit in the process of
|
|
|
- * becoming ready" */
|
|
|
- return 0;
|
|
|
- return MP_FAIL_PATH;
|
|
|
-
|
|
|
- /* FIXME: For Unit Not Ready we may want
|
|
|
- * to have a generic pg activation
|
|
|
- * feature (START_UNIT). */
|
|
|
-
|
|
|
- /* Should these two ever end up in the
|
|
|
- * error path? I don't think so. */
|
|
|
- case 0x00: /* No sense */
|
|
|
- case 0x01: /* Recovered error */
|
|
|
- return 0;
|
|
|
- }
|
|
|
- }
|
|
|
-#endif
|
|
|
-
|
|
|
- /* We got no idea how to decode the other kinds of errors ->
|
|
|
- * assume generic error condition. */
|
|
|
- return MP_FAIL_PATH;
|
|
|
-}
|
|
|
-
|
|
|
-EXPORT_SYMBOL_GPL(dm_register_hw_handler);
|
|
|
-EXPORT_SYMBOL_GPL(dm_unregister_hw_handler);
|
|
|
-EXPORT_SYMBOL_GPL(dm_scsi_err_handler);
|