Logo Search packages:      
Sourcecode: linux version File versions  Download package

dm-hw-handler.c

/*
 * 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);

Generated by  Doxygen 1.6.0   Back to index