print_formatted_time (fp, format, rs, rsf, us, usf, ss, ssf, cpu)
     FILE *fp;
     char *format;
     time_t rs;
     int rsf;
     time_t us;
     int usf;
     time_t ss;
     int ssf, cpu;
{
  int prec, lng, len;
  char *str, *s, ts[INT_STRLEN_BOUND (time_t) + sizeof ("mSS.FFFF")];
  time_t sum;
  int sum_frac;
  int sindex, ssize;

  len = strlen (format);
  ssize = (len + 64) - (len % 64);
  str = (char *)xmalloc (ssize);
  sindex = 0;

  for (s = format; *s; s++)
    {
      if (*s != '%' || s[1] == '\0')
	{
	  RESIZE_MALLOCED_BUFFER (str, sindex, 1, ssize, 64);
	  str[sindex++] = *s;
	}
      else if (s[1] == '%')
	{
	  s++;
	  RESIZE_MALLOCED_BUFFER (str, sindex, 1, ssize, 64);
	  str[sindex++] = *s;
	}
      else if (s[1] == 'P')
	{
	  s++;
#if 0
	  /* clamp CPU usage at 100% */
	  if (cpu > 10000)
	    cpu = 10000;
#endif
	  sum = cpu / 100;
	  sum_frac = (cpu % 100) * 10;
	  len = mkfmt (ts, 2, 0, sum, sum_frac);
	  RESIZE_MALLOCED_BUFFER (str, sindex, len, ssize, 64);
	  strcpy (str + sindex, ts);
	  sindex += len;
	}
      else
	{
	  prec = 3;	/* default is three places past the decimal point. */
	  lng = 0;	/* default is to not use minutes or append `s' */
	  s++;
	  if (DIGIT (*s))		/* `precision' */
	    {
	      prec = *s++ - '0';
	      if (prec > 3) prec = 3;
	    }
	  if (*s == 'l')		/* `length extender' */
	    {
	      lng = 1;
	      s++;
	    }
	  if (*s == 'R' || *s == 'E')
	    len = mkfmt (ts, prec, lng, rs, rsf);
	  else if (*s == 'U')
	    len = mkfmt (ts, prec, lng, us, usf);
	  else if (*s == 'S')
	    len = mkfmt (ts, prec, lng, ss, ssf);
	  else
	    {
	      internal_error (_("TIMEFORMAT: `%c': invalid format character"), *s);
	      free (str);
	      return;
	    }
	  RESIZE_MALLOCED_BUFFER (str, sindex, len, ssize, 64);
	  strcpy (str + sindex, ts);
	  sindex += len;
	}
    }

  str[sindex] = '\0';
  fprintf (fp, "%s\n", str);
  fflush (fp);

  free (str);
}

commit bash-20120224 snapshot


bind_tempenv_variable (name, value)
     const char *name;
     char *value;
{
  SHELL_VAR *var;

  var = temporary_env ? hash_lookup (name, temporary_env) : (SHELL_VAR *)NULL;

  if (var)
    {
      FREE (value_cell (var));
      var_setvalue (var, savestring (value));
      INVALIDATE_EXPORTSTR (var);
    }

  return (var);
}

commit bash-20120224 snapshot


find_variable_internal (name, force_tempenv)
     const char *name;
     int force_tempenv;
{
  SHELL_VAR *var;
  int search_tempenv;

  var = (SHELL_VAR *)NULL;

  /* If explicitly requested, first look in the temporary environment for
     the variable.  This allows constructs such as "foo=x eval 'echo $foo'"
     to get the `exported' value of $foo.  This happens if we are executing
     a function or builtin, or if we are looking up a variable in a
     "subshell environment". */
  search_tempenv = force_tempenv || (expanding_redir == 0 && subshell_environment);

  if (search_tempenv && temporary_env)
    var = hash_lookup (name, temporary_env);

  if (var == 0)
    var = var_lookup (name, shell_variables);

  if (var == 0)
    return ((SHELL_VAR *)NULL);

  return (var->dynamic_value ? (*(var->dynamic_value)) (var) : var);
}

commit bash-20120224 snapshot


sv_globignore (name)
     char *name;
{
  if (privileged_mode == 0)
    setup_glob_ignore (name);
}

commit bash-20120224 snapshot


cpl_prune ()
{
  struct cpelement *cp;

  while (coproc_list.head && coproc_list.ncoproc > COPROC_MAX)
    {
      cp = coproc_list.head;
      coproc_list.head = coproc_list.head->next;
      coproc_dispose (cp->coproc);
      cpe_dispose (cp);
      coproc_list.ncoproc--;
    }
}

commit bash-20120224 snapshot


kill_all_local_variables ()
{
  VAR_CONTEXT *vc;

  for (vc = shell_variables; vc; vc = vc->down)
    if (vc_isfuncenv (vc) && vc->scope == variable_context)
      break;
  if (vc == 0)
    return;		/* XXX */

  if (vc->table && vc_haslocals (vc))
    {
      delete_all_variables (vc->table);
      hash_dispose (vc->table);
    }
  vc->table = (HASH_TABLE *)NULL;
}

commit bash-20120224 snapshot


make_var_export_array (vcxt)
     VAR_CONTEXT *vcxt;
{
  char **list;
  SHELL_VAR **vars;

#if 0
  vars = map_over (visible_and_exported, vcxt);
#else
  vars = map_over (export_environment_candidate, vcxt);
#endif

  if (vars == 0)
    return (char **)NULL;

  list = make_env_array_from_var_list (vars);

  free (vars);
  return (list);
}

commit bash-20120224 snapshot


get_variable_value (var)
     SHELL_VAR *var;
{
  if (var == 0)
    return ((char *)NULL);
#if defined (ARRAY_VARS)
  else if (array_p (var))
    return (array_reference (array_cell (var), 0));
  else if (assoc_p (var))
    return (assoc_reference (assoc_cell (var), "0"));
#endif
  else
    return (value_cell (var));
}

commit bash-20120224 snapshot


qsort_var_comp (var1, var2)
     SHELL_VAR **var1, **var2;
{
  int result;

  if ((result = (*var1)->name[0] - (*var2)->name[0]) == 0)
    result = strcmp ((*var1)->name, (*var2)->name);

  return (result);
}

commit bash-20120224 snapshot


assign_in_env (word, flags)
     WORD_DESC *word;
     int flags;
{
  int offset;
  char *name, *temp, *value;
  SHELL_VAR *var;
  const char *string;

  string = word->word;

  offset = assignment (string, 0);
  name = savestring (string);
  value = (char *)NULL;

  if (name[offset] == '=')
    {
      name[offset] = 0;

      /* ignore the `+' when assigning temporary environment */
      if (name[offset - 1] == '+')
	name[offset - 1] = '\0';

      var = find_variable (name);
      if (var && (readonly_p (var) || noassign_p (var)))
	{
	  if (readonly_p (var))
	    err_readonly (name);
	  free (name);
  	  return (0);
	}

      temp = name + offset + 1;
      value = expand_assignment_string_to_string (temp, 0);
    }

  if (temporary_env == 0)
    temporary_env = hash_create (TEMPENV_HASH_BUCKETS);

  var = hash_lookup (name, temporary_env);
  if (var == 0)
    var = make_new_variable (name, temporary_env);
  else
    FREE (value_cell (var));

  if (value == 0)
    {
      value = (char *)xmalloc (1);	/* like do_assignment_internal */
      value[0] = '\0';
    }

  var_setvalue (var, value);
  var->attributes |= (att_exported|att_tempvar);
  var->context = variable_context;	/* XXX */

  INVALIDATE_EXPORTSTR (var);
  var->exportstr = mk_env_string (name, value);

  array_needs_making = 1;

#if 0
  if (ifsname (name))
    setifs (var);
else
#endif
  if (flags)
    stupidly_hack_special_variables (name);

  if (echo_command_at_execute)
    /* The Korn shell prints the `+ ' in front of assignment statements,
	so we do too. */
    xtrace_print_assignment (name, value, 0, 1);

  free (name);
  return 1;
}

commit bash-20120224 snapshot


all_visible_variables ()
{
  return (vapply (visible_var));
}

commit bash-20120224 snapshot


execute_command (command)
     COMMAND *command;
{
  struct fd_bitmap *bitmap;
  int result;

  current_fds_to_close = (struct fd_bitmap *)NULL;
  bitmap = new_fd_bitmap (FD_BITMAP_DEFAULT_SIZE);
  begin_unwind_frame ("execute-command");
  add_unwind_protect (dispose_fd_bitmap, (char *)bitmap);

  /* Just do the command, but not asynchronously. */
  result = execute_command_internal (command, 0, NO_PIPE, NO_PIPE, bitmap);

  dispose_fd_bitmap (bitmap);
  discard_unwind_frame ("execute-command");

#if defined (PROCESS_SUBSTITUTION)
  /* don't unlink fifos if we're in a shell function; wait until the function
     returns. */
  if (variable_context == 0)
    unlink_fifo_list ();
#endif /* PROCESS_SUBSTITUTION */

  QUIT;
  return (result);
}

commit bash-20120224 snapshot


coproc_closeall ()
{
#if MULTIPLE_COPROCS
  cpl_closeall ();
#else
  coproc_close (&sh_coproc);	/* XXX - will require changes for multiple coprocs */
#endif
}

commit bash-20120224 snapshot


coproc_fdchk (fd)
     int fd;
{
#if MULTIPLE_COPROCS
  cpl_fdchk (fd);
#else
  coproc_checkfd (&sh_coproc, fd);
#endif
}

commit bash-20120224 snapshot


ansiexpand (string, start, end, lenp)
     char *string;
     int start, end, *lenp;
{
  char *temp, *t;
  int len, tlen;

  temp = (char *)xmalloc (end - start + 1);
  for (tlen = 0, len = start; len < end; )
    temp[tlen++] = string[len++];
  temp[tlen] = '\0';

  if (*temp)
    {
      t = ansicstr (temp, tlen, 2, (int *)NULL, lenp);
      free (temp);
      return (t);
    }
  else
    {
      if (lenp)
	*lenp = 0;
      return (temp);
    }
}

commit bash-20120224 snapshot


bind_var_to_int (var, val)
     char *var;
     intmax_t val;
{
  char ibuf[INT_STRLEN_BOUND (intmax_t) + 1], *p;

  p = fmtulong (val, 10, ibuf, sizeof (ibuf), 0);
  return (bind_int_variable (var, p));
}

commit bash-20120224 snapshot


merge_temporary_env ()
{
  if (temporary_env)
    dispose_temporary_env (push_temp_var);
}

commit bash-20120224 snapshot


free_variable_hash_data (data)
     PTR_T data;
{
  SHELL_VAR *var;

  var = (SHELL_VAR *)data;
  dispose_variable (var);
}

commit bash-20120224 snapshot


getcoprocbypid (pid)
     pid_t pid;
{
#if MULTIPLE_COPROCS
  struct cpelement *p;

  p = cpl_search (pid);
  return (p ? p->coproc : 0);
#else
  return (pid == sh_coproc.c_pid ? &sh_coproc : 0);
#endif
}

commit bash-20120224 snapshot


coproc_fdrestore (cp)
     struct coproc *cp;
{
  cp->c_rfd = cp->c_rsave;
  cp->c_wfd = cp->c_wsave;
}

commit bash-20120224 snapshot


u32toutf16 (c, s)
     u_bits32_t c;
     unsigned short *s;
{
  int l;

  l = 0;
  if (c < 0x0d800)
    {
      s[0] = (unsigned short) (c & 0xFFFF);
      l = 1;
    }
  else if (c >= 0x0e000 && c <= 0x010ffff)
    {
      c -= 0x010000;
      s[0] = (unsigned short)((c >> 10) + 0xd800);
      s[1] = (unsigned short)((c & 0x3ff) + 0xdc00);
      l = 2;
    }
  s[l] = 0;
  return l;
}

commit bash-20120224 snapshot


err_readonly (s)
     const char *s;
{
  report_error (_("%s: readonly variable"), s);
}

commit bash-20120224 snapshot


find_special_var (name)
     const char *name;
{
  register int i, r;

  for (i = 0; special_vars[i].name; i++)
    {
      r = special_vars[i].name[0] - name[0];
      if (r == 0)
	r = strcmp (special_vars[i].name, name);
      if (r == 0)
	return i;
      else if (r > 0)
	/* Can't match any of rest of elements in sorted list.  Take this out
	   if it causes problems in certain environments. */
	break;
    }
  return -1;
}

commit bash-20120224 snapshot


execute_cond_node (cond)
     COND_COM *cond;
{
  int result, invert, patmatch, rmatch, mflags, ignore;
  char *arg1, *arg2;

  invert = (cond->flags & CMD_INVERT_RETURN);
  ignore = (cond->flags & CMD_IGNORE_RETURN);
  if (ignore)
    {
      if (cond->left)
	cond->left->flags |= CMD_IGNORE_RETURN;
      if (cond->right)
	cond->right->flags |= CMD_IGNORE_RETURN;
    }

  if (cond->type == COND_EXPR)
    result = execute_cond_node (cond->left);
  else if (cond->type == COND_OR)
    {
      result = execute_cond_node (cond->left);
      if (result != EXECUTION_SUCCESS)
	result = execute_cond_node (cond->right);
    }
  else if (cond->type == COND_AND)
    {
      result = execute_cond_node (cond->left);
      if (result == EXECUTION_SUCCESS)
	result = execute_cond_node (cond->right);
    }
  else if (cond->type == COND_UNARY)
    {
      if (ignore)
	comsub_ignore_return++;
      arg1 = cond_expand_word (cond->left->op, 0);
      if (ignore)
	comsub_ignore_return--;
      if (arg1 == 0)
	arg1 = nullstr;
      if (echo_command_at_execute)
	xtrace_print_cond_term (cond->type, invert, cond->op, arg1, (char *)NULL);
      result = unary_test (cond->op->word, arg1) ? EXECUTION_SUCCESS : EXECUTION_FAILURE;
      if (arg1 != nullstr)
	free (arg1);
    }
  else if (cond->type == COND_BINARY)
    {
      rmatch = 0;
      patmatch = (((cond->op->word[1] == '=') && (cond->op->word[2] == '\0') &&
		   (cond->op->word[0] == '!' || cond->op->word[0] == '=')) ||
		  (cond->op->word[0] == '=' && cond->op->word[1] == '\0'));
#if defined (COND_REGEXP)
      rmatch = (cond->op->word[0] == '=' && cond->op->word[1] == '~' &&
		cond->op->word[2] == '\0');
#endif

      if (ignore)
	comsub_ignore_return++;
      arg1 = cond_expand_word (cond->left->op, 0);
      if (ignore)
	comsub_ignore_return--;
      if (arg1 == 0)
	arg1 = nullstr;
      if (ignore)
	comsub_ignore_return++;
      arg2 = cond_expand_word (cond->right->op,
			       (rmatch && shell_compatibility_level > 31) ? 2 : (patmatch ? 1 : 0));
      if (ignore)
	comsub_ignore_return--;
      if (arg2 == 0)
	arg2 = nullstr;

      if (echo_command_at_execute)
	xtrace_print_cond_term (cond->type, invert, cond->op, arg1, arg2);

#if defined (COND_REGEXP)
      if (rmatch)
	{
	  mflags = SHMAT_PWARN;
#if defined (ARRAY_VARS)
	  mflags |= SHMAT_SUBEXP;
#endif

	  result = sh_regmatch (arg1, arg2, mflags);
	}
      else
#endif /* COND_REGEXP */
	{
	  int oe;
	  oe = extended_glob;
	  extended_glob = 1;
	  result = binary_test (cond->op->word, arg1, arg2, TEST_PATMATCH|TEST_ARITHEXP|TEST_LOCALE)
				  ? EXECUTION_SUCCESS
				  : EXECUTION_FAILURE;
	  extended_glob = oe;
	}
      if (arg1 != nullstr)
	free (arg1);
      if (arg2 != nullstr)
	free (arg2);
    }
  else
    {
      command_error ("execute_cond_node", CMDERR_BADTYPE, cond->type, 0);
      jump_to_top_level (DISCARD);
      result = EXECUTION_FAILURE;
    }

  if (invert)
    result = (result == EXECUTION_SUCCESS) ? EXECUTION_FAILURE : EXECUTION_SUCCESS;

  return result;
}

commit bash-20120224 snapshot


set_ppid ()
{
  char namebuf[INT_STRLEN_BOUND(pid_t) + 1], *name;
  SHELL_VAR *temp_var;

  name = inttostr (getppid (), namebuf, sizeof(namebuf));
  temp_var = find_variable ("PPID");
  if (temp_var)
    VUNSETATTR (temp_var, (att_readonly | att_exported));
  temp_var = bind_variable ("PPID", name, 0);
  VSETATTR (temp_var, (att_readonly | att_integer));
}

commit bash-20120224 snapshot


error_prolog (print_lineno)
     int print_lineno;
{
  char *ename;
  int line;

  ename = get_name_for_error ();
  line = (print_lineno && interactive_shell == 0) ? executing_line_number () : -1;

  if (line > 0)
    fprintf (stderr, "%s:%s%d: ", ename, gnu_error_format ? "" : _(" line "), line);
  else
    fprintf (stderr, "%s: ", ename);
}

commit bash-20120224 snapshot


bind_int_variable (lhs, rhs)
     char *lhs, *rhs;
{
  register SHELL_VAR *v;
  int isint, isarr;

  isint = isarr = 0;
#if defined (ARRAY_VARS)
  if (valid_array_reference (lhs))
    {
      isarr = 1;
      v = array_variable_part (lhs, (char **)0, (int *)0);
    }
  else
#endif
    v = find_variable (lhs);

  if (v)
    {
      isint = integer_p (v);
      VUNSETATTR (v, att_integer);
    }

#if defined (ARRAY_VARS)
  if (isarr)
    v = assign_array_element (lhs, rhs, 0);
  else
#endif
    v = bind_variable (lhs, rhs, 0);

  if (v && isint)
    VSETATTR (v, att_integer);

  return (v);
}

commit bash-20120224 snapshot


lastpipe_cleanup (s)
     int s;
{
  unfreeze_jobs_list ();
}

commit bash-20120224 snapshot


vlist_add (vlist, var, flags)
     VARLIST *vlist;
     SHELL_VAR *var;
     int flags;
{
  register int i;

  for (i = 0; i < vlist->list_len; i++)
    if (STREQ (var->name, vlist->list[i]->name))
      break;
  if (i < vlist->list_len)
    return;

  if (i >= vlist->list_size)
    vlist = vlist_realloc (vlist, vlist->list_size + 16);

  vlist->list[vlist->list_len++] = var;
  vlist->list[vlist->list_len] = (SHELL_VAR *)NULL;
}

commit bash-20120224 snapshot


close_pipes (in, out)
     int in, out;
{
  if (in >= 0)
    close (in);
  if (out >= 0)
    close (out);
}

commit bash-20120224 snapshot


sv_locale (name)
     char *name;
{
  char *v;
  int r;

  v = get_string_value (name);
  if (name[0] == 'L' && name[1] == 'A')	/* LANG */
    r = set_lang (name, v);
  else
    r = set_locale_var (name, v);		/* LC_*, TEXTDOMAIN* */

#if 1
  if (r == 0 && posixly_correct)
    last_command_exit_value = 1;
#endif
}

commit bash-20120224 snapshot


execute_shell_script (sample, sample_len, command, args, env)
     char *sample;
     int sample_len;
     char *command;
     char **args, **env;
{
  char *execname, *firstarg;
  int i, start, size_increment, larry;

  /* Find the name of the interpreter to exec. */
  execname = getinterp (sample, sample_len, &i);
  size_increment = 1;

  /* Now the argument, if any. */
  for (firstarg = (char *)NULL, start = i; WHITECHAR(i); i++)
    ;

  /* If there is more text on the line, then it is an argument for the
     interpreter. */

  if (STRINGCHAR(i))
    {
      for (start = i; STRINGCHAR(i); i++)
	;
      firstarg = substring ((char *)sample, start, i);
      size_increment = 2;
    }

  larry = strvec_len (args) + size_increment;
  args = strvec_resize (args, larry + 1);

  for (i = larry - 1; i; i--)
    args[i] = args[i - size_increment];

  args[0] = execname;
  if (firstarg)
    {
      args[1] = firstarg;
      args[2] = command;
    }
  else
    args[1] = command;

  args[larry] = (char *)NULL;

  return (shell_execve (execname, args, env));
}

commit bash-20120224 snapshot


err_unboundvar (s)
     const char *s;
{
  report_error (_("%s: unbound variable"), s);
}

commit bash-20120224 snapshot


get_funcname (self)
     SHELL_VAR *self;
{
#if ! defined (ARRAY_VARS)
  char *t;
  if (variable_context && this_shell_function)
    {
      FREE (value_cell (self));
      t = savestring (this_shell_function->name);
      var_setvalue (self, t);
    }
#endif
  return (self);
}

commit bash-20120224 snapshot


get_word_rgb_row(j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
/* This version is for reading raw-word-format PPM files with any maxval */
{
  ppm_source_ptr source = (ppm_source_ptr)sinfo;
  register JSAMPROW ptr;
  register U_CHAR *bufferptr;
  register JSAMPLE *rescale = source->rescale;
  JDIMENSION col;
  unsigned int maxval = source->maxval;
  register int rindex = rgb_red[cinfo->in_color_space];
  register int gindex = rgb_green[cinfo->in_color_space];
  register int bindex = rgb_blue[cinfo->in_color_space];
  register int aindex = alpha_index[cinfo->in_color_space];
  register int ps = rgb_pixelsize[cinfo->in_color_space];

  if (!ReadOK(source->pub.input_file, source->iobuffer, source->buffer_width))
    ERREXIT(cinfo, JERR_INPUT_EOF);
  ptr = source->pub.buffer[0];
  bufferptr = source->iobuffer;
  for (col = cinfo->image_width; col > 0; col--) {
    register unsigned int temp;
    temp  = UCH(*bufferptr++) << 8;
    temp |= UCH(*bufferptr++);
    if (temp > maxval)
      ERREXIT(cinfo, JERR_PPM_OUTOFRANGE);
    ptr[rindex] = rescale[temp];
    temp  = UCH(*bufferptr++) << 8;
    temp |= UCH(*bufferptr++);
    if (temp > maxval)
      ERREXIT(cinfo, JERR_PPM_OUTOFRANGE);
    ptr[gindex] = rescale[temp];
    temp  = UCH(*bufferptr++) << 8;
    temp |= UCH(*bufferptr++);
    if (temp > maxval)
      ERREXIT(cinfo, JERR_PPM_OUTOFRANGE);
    ptr[bindex] = rescale[temp];
    if (aindex >= 0)
      ptr[aindex] = 0xFF;
    ptr += ps;
  }
  return 1;
}

tjLoadImage: Fix issues w/loading 16-bit PPMs/PGMs

- The PPM reader now throws an error rather than segfaulting (due to a
  buffer overrun) if an application attempts to load a 16-bit PPM file
  into a grayscale uncompressed image buffer.  No known applications
  allowed that (not even the test applications in libjpeg-turbo),
  because that mode of operation was never expected to work and did not
  work under any circumstances.  (In fact, it was necessary to modify
  TJBench in order to reproduce the issue outside of a fuzzing
  environment.)  This was purely a matter of making the library bow out
  gracefully rather than crash if an application tries to do something
  really stupid.

- The PPM reader now throws an error rather than generating incorrect
  pixels if an application attempts to load a 16-bit PGM file into an
  RGB uncompressed image buffer.

- The PPM reader now correctly loads 16-bit PPM files into extended
  RGB uncompressed image buffers.  (Previously it generated incorrect
  pixels unless the input colorspace was JCS_RGB or JCS_EXT_RGB.)

The only way that users could have potentially encountered these issues
was through the tjLoadImage() function.  cjpeg and TJBench were
unaffected.

typedef unsigned char U_CHAR;

#define UCH(x)  ((int)(x))

#define ReadOK(file, buffer, len) \
  (JFREAD(file, buffer, len) == ((size_t)(len)))

static int alpha_index[JPEG_NUMCS] = {
  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 3, 3, 0, 0, -1
};

typedef struct {
  struct cjpeg_source_struct pub; /* public fields */

  /* Usually these two pointers point to the same place: */
  U_CHAR *iobuffer;             /* fread's I/O buffer */
  JSAMPROW pixrow;              /* compressor input buffer */
  size_t buffer_width;          /* width of I/O buffer */
  JSAMPLE *rescale;             /* => maxval-remapping array, or NULL */
  unsigned int maxval;
} ppm_source_struct;

typedef ppm_source_struct *ppm_source_ptr;

#define GRAY_RGB_READ_LOOP(read_op, alpha_set_op) { \
  for (col = cinfo->image_width; col > 0; col--) { \
    ptr[rindex] = ptr[gindex] = ptr[bindex] = read_op; \
    alpha_set_op \
    ptr += ps; \
  } \
}

ppm_source_ptr source = (ppm_source_ptr)sinfo;

FILE *infile = source->pub.input_file;

register JSAMPROW ptr;

register JSAMPLE *rescale = source->rescale;

JDIMENSION col;

unsigned int maxval = source->maxval;

register int rindex = rgb_red[cinfo->in_color_space];

register int gindex = rgb_green[cinfo->in_color_space];

register int bindex = rgb_blue[cinfo->in_color_space];

register int aindex = alpha_index[cinfo->in_color_space];

register int ps = rgb_pixelsize[cinfo->in_color_space];

#define RGB_READ_LOOP(read_op, alpha_set_op) { \
  for (col = cinfo->image_width; col > 0; col--) { \
    ptr[rindex] = read_op; \
    ptr[gindex] = read_op; \
    ptr[bindex] = read_op; \
    alpha_set_op \
    ptr += ps; \
  } \
}

register U_CHAR *bufferptr;

int c;

unsigned int w, h, maxval;


	return 0;
}

void iscsi_host_for_each_session(struct Scsi_Host *shost,
				 void (*fn)(struct iscsi_cls_session *))
{

scsi: iscsi: Verify lengths on passthrough PDUs

Open-iSCSI sends passthrough PDUs over netlink, but the kernel should be
verifying that the provided PDU header and data lengths fall within the
netlink message to prevent accessing beyond that in memory.

Cc: stable@vger.kernel.org
Reported-by: Adam Nichols <adam@grimm-co.com>
Reviewed-by: Lee Duncan <lduncan@suse.com>
Reviewed-by: Mike Christie <michael.christie@oracle.com>
Signed-off-by: Chris Leech <cleech@redhat.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>

struct iscsi_internal {
	struct scsi_transport_template t;
	struct iscsi_transport *iscsi_transport;
	struct list_head list;
	struct device dev;

	struct transport_container conn_cont;
	struct transport_container session_cont;
}

static void stop_conn_work_fn(struct work_struct *work);

static struct workqueue_struct *iscsi_eh_timer_workq;

static struct workqueue_struct *iscsi_destroy_workq;

#define to_iscsi_internal(tmpl) \
	container_of(tmpl, struct iscsi_internal, t)

#define dev_to_iscsi_internal(_dev) \
	container_of(_dev, struct iscsi_internal, dev)

static struct class iscsi_transport_class = {
	.name = "iscsi_transport",
	.dev_release = iscsi_transport_release,
};

#define show_transport_attr(name, format)				\
static ssize_t								\
show_transport_##name(struct device *dev, 				\
		      struct device_attribute *attr,char *buf)		\
{									\
	struct iscsi_internal *priv = dev_to_iscsi_internal(dev);	\
	return sprintf(buf, format"\n", priv->iscsi_transport->name);	\
}									\
static DEVICE_ATTR(name, S_IRUGO, show_transport_##name, NULL);

static struct attribute *iscsi_transport_attrs[] = {
	&dev_attr_handle.attr,
	&dev_attr_caps.attr,
	NULL,
};

static struct attribute_group iscsi_transport_group = {
	.attrs = iscsi_transport_attrs,
};

#define iscsi_dev_to_endpoint(_dev) \
	container_of(_dev, struct iscsi_endpoint, dev)

#define ISCSI_ATTR(_prefix,_name,_mode,_show,_store)	\
struct device_attribute dev_attr_##_prefix##_##_name =	\
        __ATTR(_name,_mode,_show,_store)

static struct class iscsi_endpoint_class = {
	.name = "iscsi_endpoint",
	.dev_release = iscsi_endpoint_release,
};

static struct attribute *iscsi_endpoint_attrs[] = {
	&dev_attr_ep_handle.attr,
	NULL,
};

static struct attribute_group iscsi_endpoint_group = {
	.attrs = iscsi_endpoint_attrs,
};

static struct class iscsi_iface_class = {
	.name = "iscsi_iface",
	.dev_release = iscsi_iface_release,
};

#define ISCSI_IFACE_ATTR(_prefix, _name, _mode, _show, _store)	\
struct device_attribute dev_attr_##_prefix##_##_name =		\
	__ATTR(_name, _mode, _show, _store)

#define iscsi_iface_attr_show(type, name, param_type, param)		\
static ssize_t								\
show_##type##_##name(struct device *dev, struct device_attribute *attr,	\
		     char *buf)						\
{									\
	struct iscsi_iface *iface = iscsi_dev_to_iface(dev);		\
	struct iscsi_transport *t = iface->transport;			\
	return t->get_iface_param(iface, param_type, param, buf);	\
}									\


}
EXPORT_SYMBOL_GPL(iscsi_block_session);

static void __iscsi_unbind_session(struct work_struct *work)
{
	struct iscsi_cls_session *session =
			container_of(work, struct iscsi_cls_session,
				     unbind_work);
	struct Scsi_Host *shost = iscsi_session_to_shost(session);
	struct iscsi_cls_host *ihost = shost->shost_data;
	unsigned long flags;
	unsigned int target_id;

	ISCSI_DBG_TRANS_SESSION(session, "Unbinding session\n");

	/* Prevent new scans and make sure scanning is not in progress */
	mutex_lock(&ihost->mutex);
	spin_lock_irqsave(&session->lock, flags);
	if (session->target_id == ISCSI_MAX_TARGET) {
		spin_unlock_irqrestore(&session->lock, flags);
		mutex_unlock(&ihost->mutex);
		goto unbind_session_exit;
	}

	target_id = session->target_id;
	session->target_id = ISCSI_MAX_TARGET;
	spin_unlock_irqrestore(&session->lock, flags);
	mutex_unlock(&ihost->mutex);

	scsi_remove_target(&session->dev);

	if (session->ida_used)
		ida_simple_remove(&iscsi_sess_ida, target_id);

unbind_session_exit:

static int dbg_session;

static int dbg_conn;

#define ISCSI_DBG_TRANS_SESSION(_session, dbg_fmt, arg...)		\
	do {								\
		if (dbg_session)					\
			iscsi_cls_session_printk(KERN_INFO, _session,	\
						 "%s: " dbg_fmt,	\
						 __func__, ##arg);	\
		iscsi_dbg_trace(trace_iscsi_dbg_trans_session,		\
				&(_session)->dev,			\
				"%s " dbg_fmt, __func__, ##arg);	\
	} while (0);

#define ISCSI_DBG_TRANS_CONN(_conn, dbg_fmt, arg...)			\
	do {								\
		if (dbg_conn)						\
			iscsi_cls_conn_printk(KERN_INFO, _conn,		\
					      "%s: " dbg_fmt,		\
					      __func__, ##arg);		\
		iscsi_dbg_trace(trace_iscsi_dbg_trans_conn,		\
				&(_conn)->dev,				\
				"%s " dbg_fmt, __func__, ##arg);	\
	} while (0);

struct iscsi_internal {
	struct scsi_transport_template t;
	struct iscsi_transport *iscsi_transport;
	struct list_head list;
	struct device dev;

	struct transport_container conn_cont;
	struct transport_container session_cont;
}

static void stop_conn_work_fn(struct work_struct *work);

static DECLARE_WORK(stop_conn_work, stop_conn_work_fn);

static atomic_t iscsi_session_nr;

static struct workqueue_struct *iscsi_eh_timer_workq;

static struct workqueue_struct *iscsi_destroy_workq;

static DEFINE_IDA(iscsi_sess_ida);

static LIST_HEAD(iscsi_transports);

static DEFINE_SPINLOCK(iscsi_transport_lock);

#define to_iscsi_internal(tmpl) \
	container_of(tmpl, struct iscsi_internal, t)

#define dev_to_iscsi_internal(_dev) \
	container_of(_dev, struct iscsi_internal, dev)

static struct class iscsi_transport_class = {
	.name = "iscsi_transport",
	.dev_release = iscsi_transport_release,
};

static DEVICE_ATTR(handle, S_IRUGO, show_transport_handle, NULL);

#define show_transport_attr(name, format)				\
static ssize_t								\
show_transport_##name(struct device *dev, 				\
		      struct device_attribute *attr,char *buf)		\
{									\
	struct iscsi_internal *priv = dev_to_iscsi_internal(dev);	\
	return sprintf(buf, format"\n", priv->iscsi_transport->name);	\
}									\
static DEVICE_ATTR(name, S_IRUGO, show_transport_##name, NULL);

static struct attribute *iscsi_transport_attrs[] = {
	&dev_attr_handle.attr,
	&dev_attr_caps.attr,
	NULL,
};

static struct attribute_group iscsi_transport_group = {
	.attrs = iscsi_transport_attrs,
};

#define iscsi_dev_to_endpoint(_dev) \
	container_of(_dev, struct iscsi_endpoint, dev)

#define ISCSI_ATTR(_prefix,_name,_mode,_show,_store)	\
struct device_attribute dev_attr_##_prefix##_##_name =	\
        __ATTR(_name,_mode,_show,_store)

static struct class iscsi_endpoint_class = {
	.name = "iscsi_endpoint",
	.dev_release = iscsi_endpoint_release,
};

static ISCSI_ATTR(ep, handle, S_IRUGO, show_ep_handle, NULL);

static struct attribute *iscsi_endpoint_attrs[] = {
	&dev_attr_ep_handle.attr,
	NULL,
};

static struct attribute_group iscsi_endpoint_group = {
	.attrs = iscsi_endpoint_attrs,
};

static struct class iscsi_iface_class = {
	.name = "iscsi_iface",
	.dev_release = iscsi_iface_release,
};


 * @session: iscsi class session
 * @event: type of event
 */
int iscsi_session_event(struct iscsi_cls_session *session,
			enum iscsi_uevent_e event)
{
	struct iscsi_internal *priv;
	struct Scsi_Host *shost;
	struct iscsi_uevent *ev;
	struct sk_buff  *skb;
	struct nlmsghdr *nlh;
	int rc, len = nlmsg_total_size(sizeof(*ev));

	priv = iscsi_if_transport_lookup(session->transport);
	if (!priv)
		return -EINVAL;
	shost = iscsi_session_to_shost(session);

	skb = alloc_skb(len, GFP_KERNEL);
	if (!skb) {
		iscsi_cls_session_printk(KERN_ERR, session,
					 "Cannot notify userspace of session "
					 "event %u\n", event);
		return -ENOMEM;
	}

	nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0);
	ev = nlmsg_data(nlh);
	ev->transport_handle = iscsi_handle(session->transport);

	ev->type = event;
	switch (event) {
	case ISCSI_KEVENT_DESTROY_SESSION:
		ev->r.d_session.host_no = shost->host_no;
		ev->r.d_session.sid = session->sid;
		break;
	case ISCSI_KEVENT_CREATE_SESSION:
		ev->r.c_session_ret.host_no = shost->host_no;
		ev->r.c_session_ret.sid = session->sid;
		break;
	case ISCSI_KEVENT_UNBIND_SESSION:
		ev->r.unbind_session.host_no = shost->host_no;
		ev->r.unbind_session.sid = session->sid;
		break;
	default:
		iscsi_cls_session_printk(KERN_ERR, session, "Invalid event "
					 "%u.\n", event);
		kfree_skb(skb);
		return -EINVAL;
	}

	/*
	 * this will occur if the daemon is not up, so we just warn
	 * the user and when the daemon is restarted it will handle it
	 */
	rc = iscsi_multicast_skb(skb, ISCSI_NL_GRP_ISCSID, GFP_KERNEL);
	if (rc == -ESRCH)
		iscsi_cls_session_printk(KERN_ERR, session,
					 "Cannot notify userspace of session "
					 "event %u. Check iscsi daemon\n",
					 event);

	ISCSI_DBG_TRANS_SESSION(session, "Completed handling event %d rc %d\n",

static int dbg_session;

static int dbg_conn;

#define ISCSI_DBG_TRANS_SESSION(_session, dbg_fmt, arg...)		\
	do {								\
		if (dbg_session)					\
			iscsi_cls_session_printk(KERN_INFO, _session,	\
						 "%s: " dbg_fmt,	\
						 __func__, ##arg);	\
		iscsi_dbg_trace(trace_iscsi_dbg_trans_session,		\
				&(_session)->dev,			\
				"%s " dbg_fmt, __func__, ##arg);	\
	} while (0);

struct iscsi_internal {
	struct scsi_transport_template t;
	struct iscsi_transport *iscsi_transport;
	struct list_head list;
	struct device dev;

	struct transport_container conn_cont;
	struct transport_container session_cont;
}

static void stop_conn_work_fn(struct work_struct *work);

static struct workqueue_struct *iscsi_eh_timer_workq;

static struct workqueue_struct *iscsi_destroy_workq;

#define to_iscsi_internal(tmpl) \
	container_of(tmpl, struct iscsi_internal, t)

#define dev_to_iscsi_internal(_dev) \
	container_of(_dev, struct iscsi_internal, dev)

static struct class iscsi_transport_class = {
	.name = "iscsi_transport",
	.dev_release = iscsi_transport_release,
};

static DEVICE_ATTR(handle, S_IRUGO, show_transport_handle, NULL);

#define show_transport_attr(name, format)				\
static ssize_t								\
show_transport_##name(struct device *dev, 				\
		      struct device_attribute *attr,char *buf)		\
{									\
	struct iscsi_internal *priv = dev_to_iscsi_internal(dev);	\
	return sprintf(buf, format"\n", priv->iscsi_transport->name);	\
}									\
static DEVICE_ATTR(name, S_IRUGO, show_transport_##name, NULL);

static struct attribute *iscsi_transport_attrs[] = {
	&dev_attr_handle.attr,
	&dev_attr_caps.attr,
	NULL,
};

static struct attribute_group iscsi_transport_group = {
	.attrs = iscsi_transport_attrs,
};

#define iscsi_dev_to_endpoint(_dev) \
	container_of(_dev, struct iscsi_endpoint, dev)

#define ISCSI_ATTR(_prefix,_name,_mode,_show,_store)	\
struct device_attribute dev_attr_##_prefix##_##_name =	\
        __ATTR(_name,_mode,_show,_store)

static struct class iscsi_endpoint_class = {
	.name = "iscsi_endpoint",
	.dev_release = iscsi_endpoint_release,
};

static ISCSI_ATTR(ep, handle, S_IRUGO, show_ep_handle, NULL);

static struct attribute *iscsi_endpoint_attrs[] = {
	&dev_attr_ep_handle.attr,
	NULL,
};

static struct attribute_group iscsi_endpoint_group = {
	.attrs = iscsi_endpoint_attrs,
};

static struct class iscsi_iface_class = {
	.name = "iscsi_iface",
	.dev_release = iscsi_iface_release,
};

#define ISCSI_IFACE_ATTR(_prefix, _name, _mode, _show, _store)	\
struct device_attribute dev_attr_##_prefix##_##_name =		\
	__ATTR(_name, _mode, _show, _store)

#define iscsi_iface_attr_show(type, name, param_type, param)		\
static ssize_t								\
show_##type##_##name(struct device *dev, struct device_attribute *attr,	\
		     char *buf)						\
{									\
	struct iscsi_iface *iface = iscsi_dev_to_iface(dev);		\
	struct iscsi_transport *t = iface->transport;			\
	return t->get_iface_param(iface, param_type, param, buf);	\
}									\


}
EXPORT_SYMBOL_GPL(iscsi_unblock_session);

static void __iscsi_block_session(struct work_struct *work)
{
	struct iscsi_cls_session *session =
			container_of(work, struct iscsi_cls_session,
				     block_work);
	unsigned long flags;

	ISCSI_DBG_TRANS_SESSION(session, "Blocking session\n");
	spin_lock_irqsave(&session->lock, flags);
	session->state = ISCSI_SESSION_FAILED;
	spin_unlock_irqrestore(&session->lock, flags);
	scsi_target_block(&session->dev);
	ISCSI_DBG_TRANS_SESSION(session, "Completed SCSI target blocking\n");
	if (session->recovery_tmo >= 0)
		queue_delayed_work(iscsi_eh_timer_workq,


	NULL,
};

static void iscsi_flashnode_conn_release(struct device *dev)
{
	struct iscsi_bus_flash_conn *fnode_conn = iscsi_dev_to_flash_conn(dev);

	kfree(fnode_conn->ipaddress);
	kfree(fnode_conn->redirect_ipaddr);


static umode_t iscsi_iface_attr_is_visible(struct kobject *kobj,
					  struct attribute *attr, int i)
{
	struct device *dev = container_of(kobj, struct device, kobj);
	struct iscsi_iface *iface = iscsi_dev_to_iface(dev);
	struct iscsi_transport *t = iface->transport;
	int param;
	int param_type;

	if (attr == &dev_attr_iface_enabled.attr)
		param = ISCSI_NET_PARAM_IFACE_ENABLE;
	else if (attr == &dev_attr_iface_vlan_id.attr)
		param = ISCSI_NET_PARAM_VLAN_ID;
	else if (attr == &dev_attr_iface_vlan_priority.attr)
		param = ISCSI_NET_PARAM_VLAN_PRIORITY;
	else if (attr == &dev_attr_iface_vlan_enabled.attr)
		param = ISCSI_NET_PARAM_VLAN_ENABLED;
	else if (attr == &dev_attr_iface_mtu.attr)
		param = ISCSI_NET_PARAM_MTU;
	else if (attr == &dev_attr_iface_port.attr)
		param = ISCSI_NET_PARAM_PORT;
	else if (attr == &dev_attr_iface_ipaddress_state.attr)
		param = ISCSI_NET_PARAM_IPADDR_STATE;
	else if (attr == &dev_attr_iface_delayed_ack_en.attr)
		param = ISCSI_NET_PARAM_DELAYED_ACK_EN;
	else if (attr == &dev_attr_iface_tcp_nagle_disable.attr)
		param = ISCSI_NET_PARAM_TCP_NAGLE_DISABLE;
	else if (attr == &dev_attr_iface_tcp_wsf_disable.attr)
		param = ISCSI_NET_PARAM_TCP_WSF_DISABLE;
	else if (attr == &dev_attr_iface_tcp_wsf.attr)
		param = ISCSI_NET_PARAM_TCP_WSF;
	else if (attr == &dev_attr_iface_tcp_timer_scale.attr)
		param = ISCSI_NET_PARAM_TCP_TIMER_SCALE;
	else if (attr == &dev_attr_iface_tcp_timestamp_en.attr)
		param = ISCSI_NET_PARAM_TCP_TIMESTAMP_EN;
	else if (attr == &dev_attr_iface_cache_id.attr)
		param = ISCSI_NET_PARAM_CACHE_ID;
	else if (attr == &dev_attr_iface_redirect_en.attr)
		param = ISCSI_NET_PARAM_REDIRECT_EN;
	else if (attr == &dev_attr_iface_def_taskmgmt_tmo.attr)
		param = ISCSI_IFACE_PARAM_DEF_TASKMGMT_TMO;
	else if (attr == &dev_attr_iface_header_digest.attr)
		param = ISCSI_IFACE_PARAM_HDRDGST_EN;
	else if (attr == &dev_attr_iface_data_digest.attr)
		param = ISCSI_IFACE_PARAM_DATADGST_EN;
	else if (attr == &dev_attr_iface_immediate_data.attr)
		param = ISCSI_IFACE_PARAM_IMM_DATA_EN;
	else if (attr == &dev_attr_iface_initial_r2t.attr)
		param = ISCSI_IFACE_PARAM_INITIAL_R2T_EN;
	else if (attr == &dev_attr_iface_data_seq_in_order.attr)
		param = ISCSI_IFACE_PARAM_DATASEQ_INORDER_EN;
	else if (attr == &dev_attr_iface_data_pdu_in_order.attr)
		param = ISCSI_IFACE_PARAM_PDU_INORDER_EN;
	else if (attr == &dev_attr_iface_erl.attr)
		param = ISCSI_IFACE_PARAM_ERL;
	else if (attr == &dev_attr_iface_max_recv_dlength.attr)
		param = ISCSI_IFACE_PARAM_MAX_RECV_DLENGTH;
	else if (attr == &dev_attr_iface_first_burst_len.attr)
		param = ISCSI_IFACE_PARAM_FIRST_BURST;
	else if (attr == &dev_attr_iface_max_outstanding_r2t.attr)
		param = ISCSI_IFACE_PARAM_MAX_R2T;
	else if (attr == &dev_attr_iface_max_burst_len.attr)
		param = ISCSI_IFACE_PARAM_MAX_BURST;
	else if (attr == &dev_attr_iface_chap_auth.attr)
		param = ISCSI_IFACE_PARAM_CHAP_AUTH_EN;
	else if (attr == &dev_attr_iface_bidi_chap.attr)
		param = ISCSI_IFACE_PARAM_BIDI_CHAP_EN;
	else if (attr == &dev_attr_iface_discovery_auth_optional.attr)
		param = ISCSI_IFACE_PARAM_DISCOVERY_AUTH_OPTIONAL;
	else if (attr == &dev_attr_iface_discovery_logout.attr)
		param = ISCSI_IFACE_PARAM_DISCOVERY_LOGOUT_EN;
	else if (attr == &dev_attr_iface_strict_login_comp_en.attr)
		param = ISCSI_IFACE_PARAM_STRICT_LOGIN_COMP_EN;
	else if (attr == &dev_attr_iface_initiator_name.attr)
		param = ISCSI_IFACE_PARAM_INITIATOR_NAME;
	else if (iface->iface_type == ISCSI_IFACE_TYPE_IPV4) {
		if (attr == &dev_attr_ipv4_iface_ipaddress.attr)
			param = ISCSI_NET_PARAM_IPV4_ADDR;
		else if (attr == &dev_attr_ipv4_iface_gateway.attr)
			param = ISCSI_NET_PARAM_IPV4_GW;
		else if (attr == &dev_attr_ipv4_iface_subnet.attr)
			param = ISCSI_NET_PARAM_IPV4_SUBNET;
		else if (attr == &dev_attr_ipv4_iface_bootproto.attr)
			param = ISCSI_NET_PARAM_IPV4_BOOTPROTO;
		else if (attr ==
			 &dev_attr_ipv4_iface_dhcp_dns_address_en.attr)
			param = ISCSI_NET_PARAM_IPV4_DHCP_DNS_ADDR_EN;
		else if (attr ==
			 &dev_attr_ipv4_iface_dhcp_slp_da_info_en.attr)
			param = ISCSI_NET_PARAM_IPV4_DHCP_SLP_DA_EN;
		else if (attr == &dev_attr_ipv4_iface_tos_en.attr)
			param = ISCSI_NET_PARAM_IPV4_TOS_EN;
		else if (attr == &dev_attr_ipv4_iface_tos.attr)
			param = ISCSI_NET_PARAM_IPV4_TOS;
		else if (attr == &dev_attr_ipv4_iface_grat_arp_en.attr)
			param = ISCSI_NET_PARAM_IPV4_GRAT_ARP_EN;
		else if (attr ==
			 &dev_attr_ipv4_iface_dhcp_alt_client_id_en.attr)
			param = ISCSI_NET_PARAM_IPV4_DHCP_ALT_CLIENT_ID_EN;
		else if (attr == &dev_attr_ipv4_iface_dhcp_alt_client_id.attr)
			param = ISCSI_NET_PARAM_IPV4_DHCP_ALT_CLIENT_ID;
		else if (attr ==
			 &dev_attr_ipv4_iface_dhcp_req_vendor_id_en.attr)
			param = ISCSI_NET_PARAM_IPV4_DHCP_REQ_VENDOR_ID_EN;
		else if (attr ==
			 &dev_attr_ipv4_iface_dhcp_use_vendor_id_en.attr)
			param = ISCSI_NET_PARAM_IPV4_DHCP_USE_VENDOR_ID_EN;
		else if (attr == &dev_attr_ipv4_iface_dhcp_vendor_id.attr)
			param = ISCSI_NET_PARAM_IPV4_DHCP_VENDOR_ID;
		else if (attr ==
			 &dev_attr_ipv4_iface_dhcp_learn_iqn_en.attr)
			param = ISCSI_NET_PARAM_IPV4_DHCP_LEARN_IQN_EN;
		else if (attr ==
			 &dev_attr_ipv4_iface_fragment_disable.attr)
			param = ISCSI_NET_PARAM_IPV4_FRAGMENT_DISABLE;
		else if (attr ==
			 &dev_attr_ipv4_iface_incoming_forwarding_en.attr)
			param = ISCSI_NET_PARAM_IPV4_IN_FORWARD_EN;
		else if (attr == &dev_attr_ipv4_iface_ttl.attr)
			param = ISCSI_NET_PARAM_IPV4_TTL;
		else
			return 0;
	} else if (iface->iface_type == ISCSI_IFACE_TYPE_IPV6) {
		if (attr == &dev_attr_ipv6_iface_ipaddress.attr)
			param = ISCSI_NET_PARAM_IPV6_ADDR;
		else if (attr == &dev_attr_ipv6_iface_link_local_addr.attr)
			param = ISCSI_NET_PARAM_IPV6_LINKLOCAL;
		else if (attr == &dev_attr_ipv6_iface_router_addr.attr)
			param = ISCSI_NET_PARAM_IPV6_ROUTER;
		else if (attr == &dev_attr_ipv6_iface_ipaddr_autocfg.attr)
			param = ISCSI_NET_PARAM_IPV6_ADDR_AUTOCFG;
		else if (attr == &dev_attr_ipv6_iface_link_local_autocfg.attr)
			param = ISCSI_NET_PARAM_IPV6_LINKLOCAL_AUTOCFG;
		else if (attr == &dev_attr_ipv6_iface_link_local_state.attr)
			param = ISCSI_NET_PARAM_IPV6_LINKLOCAL_STATE;
		else if (attr == &dev_attr_ipv6_iface_router_state.attr)
			param = ISCSI_NET_PARAM_IPV6_ROUTER_STATE;
		else if (attr ==
			 &dev_attr_ipv6_iface_grat_neighbor_adv_en.attr)
			param = ISCSI_NET_PARAM_IPV6_GRAT_NEIGHBOR_ADV_EN;
		else if (attr == &dev_attr_ipv6_iface_mld_en.attr)
			param = ISCSI_NET_PARAM_IPV6_MLD_EN;
		else if (attr == &dev_attr_ipv6_iface_flow_label.attr)
			param = ISCSI_NET_PARAM_IPV6_FLOW_LABEL;
		else if (attr == &dev_attr_ipv6_iface_traffic_class.attr)
			param = ISCSI_NET_PARAM_IPV6_TRAFFIC_CLASS;
		else if (attr == &dev_attr_ipv6_iface_hop_limit.attr)
			param = ISCSI_NET_PARAM_IPV6_HOP_LIMIT;
		else if (attr == &dev_attr_ipv6_iface_nd_reachable_tmo.attr)
			param = ISCSI_NET_PARAM_IPV6_ND_REACHABLE_TMO;
		else if (attr == &dev_attr_ipv6_iface_nd_rexmit_time.attr)
			param = ISCSI_NET_PARAM_IPV6_ND_REXMIT_TIME;
		else if (attr == &dev_attr_ipv6_iface_nd_stale_tmo.attr)
			param = ISCSI_NET_PARAM_IPV6_ND_STALE_TMO;
		else if (attr == &dev_attr_ipv6_iface_dup_addr_detect_cnt.attr)
			param = ISCSI_NET_PARAM_IPV6_DUP_ADDR_DETECT_CNT;
		else if (attr == &dev_attr_ipv6_iface_router_adv_link_mtu.attr)
			param = ISCSI_NET_PARAM_IPV6_RTR_ADV_LINK_MTU;
		else
			return 0;
	} else {
		WARN_ONCE(1, "Invalid iface attr");
		return 0;
	}

	switch (param) {
	case ISCSI_IFACE_PARAM_DEF_TASKMGMT_TMO:
	case ISCSI_IFACE_PARAM_HDRDGST_EN:
	case ISCSI_IFACE_PARAM_DATADGST_EN:
	case ISCSI_IFACE_PARAM_IMM_DATA_EN:
	case ISCSI_IFACE_PARAM_INITIAL_R2T_EN:
	case ISCSI_IFACE_PARAM_DATASEQ_INORDER_EN:
	case ISCSI_IFACE_PARAM_PDU_INORDER_EN:
	case ISCSI_IFACE_PARAM_ERL:
	case ISCSI_IFACE_PARAM_MAX_RECV_DLENGTH:
	case ISCSI_IFACE_PARAM_FIRST_BURST:
	case ISCSI_IFACE_PARAM_MAX_R2T:
	case ISCSI_IFACE_PARAM_MAX_BURST:
	case ISCSI_IFACE_PARAM_CHAP_AUTH_EN:
	case ISCSI_IFACE_PARAM_BIDI_CHAP_EN:
	case ISCSI_IFACE_PARAM_DISCOVERY_AUTH_OPTIONAL:
	case ISCSI_IFACE_PARAM_DISCOVERY_LOGOUT_EN:
	case ISCSI_IFACE_PARAM_STRICT_LOGIN_COMP_EN:
	case ISCSI_IFACE_PARAM_INITIATOR_NAME:
		param_type = ISCSI_IFACE_PARAM;
		break;
	default:
		param_type = ISCSI_NET_PARAM;
	}

	return t->attr_is_visible(param_type, param);


	NULL,
};

static umode_t iscsi_flashnode_conn_attr_is_visible(struct kobject *kobj,
						    struct attribute *attr,
						    int i)
{
	struct device *dev = container_of(kobj, struct device, kobj);
	struct iscsi_bus_flash_conn *fnode_conn = iscsi_dev_to_flash_conn(dev);
	struct iscsi_transport *t = fnode_conn->transport;
	int param;

	if (attr == &dev_attr_fnode_is_fw_assigned_ipv6.attr) {
		param = ISCSI_FLASHNODE_IS_FW_ASSIGNED_IPV6;
	} else if (attr == &dev_attr_fnode_header_digest.attr) {
		param = ISCSI_FLASHNODE_HDR_DGST_EN;
	} else if (attr == &dev_attr_fnode_data_digest.attr) {
		param = ISCSI_FLASHNODE_DATA_DGST_EN;
	} else if (attr == &dev_attr_fnode_snack_req.attr) {
		param = ISCSI_FLASHNODE_SNACK_REQ_EN;
	} else if (attr == &dev_attr_fnode_tcp_timestamp_stat.attr) {
		param = ISCSI_FLASHNODE_TCP_TIMESTAMP_STAT;
	} else if (attr == &dev_attr_fnode_tcp_nagle_disable.attr) {
		param = ISCSI_FLASHNODE_TCP_NAGLE_DISABLE;
	} else if (attr == &dev_attr_fnode_tcp_wsf_disable.attr) {
		param = ISCSI_FLASHNODE_TCP_WSF_DISABLE;
	} else if (attr == &dev_attr_fnode_tcp_timer_scale.attr) {
		param = ISCSI_FLASHNODE_TCP_TIMER_SCALE;
	} else if (attr == &dev_attr_fnode_tcp_timestamp_enable.attr) {
		param = ISCSI_FLASHNODE_TCP_TIMESTAMP_EN;
	} else if (attr == &dev_attr_fnode_fragment_disable.attr) {
		param = ISCSI_FLASHNODE_IP_FRAG_DISABLE;
	} else if (attr == &dev_attr_fnode_max_recv_dlength.attr) {
		param = ISCSI_FLASHNODE_MAX_RECV_DLENGTH;
	} else if (attr == &dev_attr_fnode_max_xmit_dlength.attr) {
		param = ISCSI_FLASHNODE_MAX_XMIT_DLENGTH;
	} else if (attr == &dev_attr_fnode_keepalive_tmo.attr) {
		param = ISCSI_FLASHNODE_KEEPALIVE_TMO;
	} else if (attr == &dev_attr_fnode_port.attr) {
		param = ISCSI_FLASHNODE_PORT;
	} else if (attr == &dev_attr_fnode_ipaddress.attr) {
		param = ISCSI_FLASHNODE_IPADDR;
	} else if (attr == &dev_attr_fnode_redirect_ipaddr.attr) {
		param = ISCSI_FLASHNODE_REDIRECT_IPADDR;
	} else if (attr == &dev_attr_fnode_max_segment_size.attr) {
		param = ISCSI_FLASHNODE_MAX_SEGMENT_SIZE;
	} else if (attr == &dev_attr_fnode_local_port.attr) {
		param = ISCSI_FLASHNODE_LOCAL_PORT;
	} else if (attr == &dev_attr_fnode_ipv4_tos.attr) {
		param = ISCSI_FLASHNODE_IPV4_TOS;
	} else if (attr == &dev_attr_fnode_ipv6_traffic_class.attr) {
		param = ISCSI_FLASHNODE_IPV6_TC;
	} else if (attr == &dev_attr_fnode_ipv6_flow_label.attr) {
		param = ISCSI_FLASHNODE_IPV6_FLOW_LABEL;
	} else if (attr == &dev_attr_fnode_link_local_ipv6.attr) {
		param = ISCSI_FLASHNODE_LINK_LOCAL_IPV6;
	} else if (attr == &dev_attr_fnode_tcp_xmit_wsf.attr) {
		param = ISCSI_FLASHNODE_TCP_XMIT_WSF;
	} else if (attr == &dev_attr_fnode_tcp_recv_wsf.attr) {
		param = ISCSI_FLASHNODE_TCP_RECV_WSF;
	} else if (attr == &dev_attr_fnode_statsn.attr) {
		param = ISCSI_FLASHNODE_STATSN;
	} else if (attr == &dev_attr_fnode_exp_statsn.attr) {
		param = ISCSI_FLASHNODE_EXP_STATSN;
	} else {
		WARN_ONCE(1, "Invalid flashnode connection attr");
		return 0;
	}


EXPORT_SYMBOL_GPL(iscsi_ping_comp_event);

static int
iscsi_if_send_reply(u32 portid, int type, void *payload, int size)
{
	struct sk_buff	*skb;
	struct nlmsghdr	*nlh;
	int len = nlmsg_total_size(size);

	skb = alloc_skb(len, GFP_ATOMIC);
	if (!skb) {
		printk(KERN_ERR "Could not allocate skb to send reply.\n");
		return -ENOMEM;
	}

	nlh = __nlmsg_put(skb, 0, 0, type, (len - sizeof(*nlh)), 0);

static int dbg_session;

static int dbg_conn;

struct iscsi_internal {
	struct scsi_transport_template t;
	struct iscsi_transport *iscsi_transport;
	struct list_head list;
	struct device dev;

	struct transport_container conn_cont;
	struct transport_container session_cont;
}

static void stop_conn_work_fn(struct work_struct *work);

static DECLARE_WORK(stop_conn_work, stop_conn_work_fn);

static atomic_t iscsi_session_nr;

static struct workqueue_struct *iscsi_eh_timer_workq;

static struct workqueue_struct *iscsi_destroy_workq;

static DEFINE_IDA(iscsi_sess_ida);

static LIST_HEAD(iscsi_transports);

static DEFINE_SPINLOCK(iscsi_transport_lock);

#define to_iscsi_internal(tmpl) \
	container_of(tmpl, struct iscsi_internal, t)

#define dev_to_iscsi_internal(_dev) \
	container_of(_dev, struct iscsi_internal, dev)

static struct class iscsi_transport_class = {
	.name = "iscsi_transport",
	.dev_release = iscsi_transport_release,
};

static DEVICE_ATTR(handle, S_IRUGO, show_transport_handle, NULL);

#define show_transport_attr(name, format)				\
static ssize_t								\
show_transport_##name(struct device *dev, 				\
		      struct device_attribute *attr,char *buf)		\
{									\
	struct iscsi_internal *priv = dev_to_iscsi_internal(dev);	\
	return sprintf(buf, format"\n", priv->iscsi_transport->name);	\
}									\
static DEVICE_ATTR(name, S_IRUGO, show_transport_##name, NULL);

static struct attribute *iscsi_transport_attrs[] = {
	&dev_attr_handle.attr,
	&dev_attr_caps.attr,
	NULL,
};

static struct attribute_group iscsi_transport_group = {
	.attrs = iscsi_transport_attrs,
};

#define iscsi_dev_to_endpoint(_dev) \
	container_of(_dev, struct iscsi_endpoint, dev)

#define ISCSI_ATTR(_prefix,_name,_mode,_show,_store)	\
struct device_attribute dev_attr_##_prefix##_##_name =	\
        __ATTR(_name,_mode,_show,_store)

static struct class iscsi_endpoint_class = {
	.name = "iscsi_endpoint",
	.dev_release = iscsi_endpoint_release,
};

static ISCSI_ATTR(ep, handle, S_IRUGO, show_ep_handle, NULL);

static struct attribute *iscsi_endpoint_attrs[] = {
	&dev_attr_ep_handle.attr,
	NULL,
};

static struct attribute_group iscsi_endpoint_group = {
	.attrs = iscsi_endpoint_attrs,
};

static struct class iscsi_iface_class = {
	.name = "iscsi_iface",
	.dev_release = iscsi_iface_release,
};

#define ISCSI_IFACE_ATTR(_prefix, _name, _mode, _show, _store)	\
struct device_attribute dev_attr_##_prefix##_##_name =		\
	__ATTR(_name, _mode, _show, _store)

#define iscsi_iface_attr_show(type, name, param_type, param)		\
static ssize_t								\
show_##type##_##name(struct device *dev, struct device_attribute *attr,	\
		     char *buf)						\
{									\
	struct iscsi_iface *iface = iscsi_dev_to_iface(dev);		\
	struct iscsi_transport *t = iface->transport;			\
	return t->get_iface_param(iface, param_type, param, buf);	\
}									\

#define iscsi_iface_net_attr(type, name, param)				\
	iscsi_iface_attr_show(type, name, ISCSI_NET_PARAM, param)	\
static ISCSI_IFACE_ATTR(type, name, S_IRUGO, show_##type##_##name, NULL);


	return dev->release == iscsi_conn_release;
}

static int iscsi_iter_destroy_conn_fn(struct device *dev, void *data)
{
	if (!iscsi_is_conn_dev(dev))


 * recovery timer fires then FAST_IO_FAIL is returned. The caller
 * should pass this error value to the scsi eh.
 */
int iscsi_block_scsi_eh(struct scsi_cmnd *cmd)
{
	struct iscsi_cls_session *session =
			starget_to_session(scsi_target(cmd->device));
	unsigned long flags;
	int ret = 0;

	spin_lock_irqsave(&session->lock, flags);
	while (session->state != ISCSI_SESSION_LOGGED_IN) {
		if (session->state == ISCSI_SESSION_FREE) {
			ret = FAST_IO_FAIL;
			break;
		}
		spin_unlock_irqrestore(&session->lock, flags);
		msleep(1000);
		spin_lock_irqsave(&session->lock, flags);
	}

static int dbg_session;

static int dbg_conn;

#define ISCSI_DBG_TRANS_SESSION(_session, dbg_fmt, arg...)		\
	do {								\
		if (dbg_session)					\
			iscsi_cls_session_printk(KERN_INFO, _session,	\
						 "%s: " dbg_fmt,	\
						 __func__, ##arg);	\
		iscsi_dbg_trace(trace_iscsi_dbg_trans_session,		\
				&(_session)->dev,			\
				"%s " dbg_fmt, __func__, ##arg);	\
	} while (0);

#define ISCSI_DBG_TRANS_CONN(_conn, dbg_fmt, arg...)			\
	do {								\
		if (dbg_conn)						\
			iscsi_cls_conn_printk(KERN_INFO, _conn,		\
					      "%s: " dbg_fmt,		\
					      __func__, ##arg);		\
		iscsi_dbg_trace(trace_iscsi_dbg_trans_conn,		\
				&(_conn)->dev,				\
				"%s " dbg_fmt, __func__, ##arg);	\
	} while (0);

struct iscsi_internal {
	struct scsi_transport_template t;
	struct iscsi_transport *iscsi_transport;
	struct list_head list;
	struct device dev;

	struct transport_container conn_cont;
	struct transport_container session_cont;
}

static void stop_conn_work_fn(struct work_struct *work);

static struct workqueue_struct *iscsi_eh_timer_workq;

static struct workqueue_struct *iscsi_destroy_workq;

#define to_iscsi_internal(tmpl) \
	container_of(tmpl, struct iscsi_internal, t)

#define dev_to_iscsi_internal(_dev) \
	container_of(_dev, struct iscsi_internal, dev)

static struct class iscsi_transport_class = {
	.name = "iscsi_transport",
	.dev_release = iscsi_transport_release,
};

#define show_transport_attr(name, format)				\
static ssize_t								\
show_transport_##name(struct device *dev, 				\
		      struct device_attribute *attr,char *buf)		\
{									\
	struct iscsi_internal *priv = dev_to_iscsi_internal(dev);	\
	return sprintf(buf, format"\n", priv->iscsi_transport->name);	\
}									\
static DEVICE_ATTR(name, S_IRUGO, show_transport_##name, NULL);

static struct attribute *iscsi_transport_attrs[] = {
	&dev_attr_handle.attr,
	&dev_attr_caps.attr,
	NULL,
};

static struct attribute_group iscsi_transport_group = {
	.attrs = iscsi_transport_attrs,
};

#define iscsi_dev_to_endpoint(_dev) \
	container_of(_dev, struct iscsi_endpoint, dev)

#define ISCSI_ATTR(_prefix,_name,_mode,_show,_store)	\
struct device_attribute dev_attr_##_prefix##_##_name =	\
        __ATTR(_name,_mode,_show,_store)

static struct class iscsi_endpoint_class = {
	.name = "iscsi_endpoint",
	.dev_release = iscsi_endpoint_release,
};

static struct attribute *iscsi_endpoint_attrs[] = {
	&dev_attr_ep_handle.attr,
	NULL,
};

static struct attribute_group iscsi_endpoint_group = {
	.attrs = iscsi_endpoint_attrs,
};

static struct class iscsi_iface_class = {
	.name = "iscsi_iface",
	.dev_release = iscsi_iface_release,
};

#define ISCSI_IFACE_ATTR(_prefix, _name, _mode, _show, _store)	\
struct device_attribute dev_attr_##_prefix##_##_name =		\
	__ATTR(_name, _mode, _show, _store)


	return err;
}

static int iscsi_logout_flashnode(struct iscsi_transport *transport,
				  struct iscsi_uevent *ev)
{
	struct Scsi_Host *shost;
	struct iscsi_bus_flash_session *fnode_sess;
	struct iscsi_bus_flash_conn *fnode_conn;
	struct device *dev;
	uint32_t idx;
	int err = 0;

	if (!transport->logout_flashnode) {
		err = -ENOSYS;
		goto exit_logout_fnode;
	}

	shost = scsi_host_lookup(ev->u.logout_flashnode.host_no);
	if (!shost) {
		pr_err("%s could not find host no %u\n",
		       __func__, ev->u.logout_flashnode.host_no);
		err = -ENODEV;
		goto put_host;
	}

	idx = ev->u.logout_flashnode.flashnode_idx;
	fnode_sess = iscsi_get_flashnode_by_index(shost, idx);
	if (!fnode_sess) {
		pr_err("%s could not find flashnode %u for host no %u\n",
		       __func__, idx, ev->u.logout_flashnode.host_no);
		err = -ENODEV;
		goto put_host;
	}

	dev = iscsi_find_flashnode_conn(fnode_sess);
	if (!dev) {
		err = -ENODEV;
		goto put_sess;
	}

	fnode_conn = iscsi_dev_to_flash_conn(dev);

	err = transport->logout_flashnode(fnode_sess, fnode_conn);
	put_device(dev);

put_sess:
	put_device(&fnode_sess->dev);

put_host:
	scsi_host_put(shost);


 * non-zero.
 */
struct iscsi_cls_conn *
iscsi_create_conn(struct iscsi_cls_session *session, int dd_size, uint32_t cid)
{
	struct iscsi_transport *transport = session->transport;
	struct iscsi_cls_conn *conn;
	unsigned long flags;
	int err;

	conn = kzalloc(sizeof(*conn) + dd_size, GFP_KERNEL);
	if (!conn)
		return NULL;
	if (dd_size)
		conn->dd_data = &conn[1];

	mutex_init(&conn->ep_mutex);
	INIT_LIST_HEAD(&conn->conn_list);
	INIT_LIST_HEAD(&conn->conn_list_err);
	conn->transport = transport;
	conn->cid = cid;
	conn->state = ISCSI_CONN_DOWN;

	/* this is released in the dev's release function */
	if (!get_device(&session->dev))
		goto free_conn;

	dev_set_name(&conn->dev, "connection%d:%u", session->sid, cid);
	conn->dev.parent = &session->dev;
	conn->dev.release = iscsi_conn_release;
	err = device_register(&conn->dev);
	if (err) {
		iscsi_cls_session_printk(KERN_ERR, session, "could not "
					 "register connection's dev\n");
		goto release_parent_ref;
	}
	err = transport_register_device(&conn->dev);
	if (err) {
		iscsi_cls_session_printk(KERN_ERR, session, "could not "
					 "register transport's dev\n");
		goto release_conn_ref;
	}

	spin_lock_irqsave(&connlock, flags);
	list_add(&conn->conn_list, &connlist);
	spin_unlock_irqrestore(&connlock, flags);

	ISCSI_DBG_TRANS_CONN(conn, "Completed conn creation\n");
	return conn;

release_conn_ref:
	device_unregister(&conn->dev);
	put_device(&session->dev);
	return NULL;
release_parent_ref:
	put_device(&session->dev);
free_conn:

static int dbg_session;

static int dbg_conn;

#define ISCSI_DBG_TRANS_SESSION(_session, dbg_fmt, arg...)		\
	do {								\
		if (dbg_session)					\
			iscsi_cls_session_printk(KERN_INFO, _session,	\
						 "%s: " dbg_fmt,	\
						 __func__, ##arg);	\
		iscsi_dbg_trace(trace_iscsi_dbg_trans_session,		\
				&(_session)->dev,			\
				"%s " dbg_fmt, __func__, ##arg);	\
	} while (0);

#define ISCSI_DBG_TRANS_CONN(_conn, dbg_fmt, arg...)			\
	do {								\
		if (dbg_conn)						\
			iscsi_cls_conn_printk(KERN_INFO, _conn,		\
					      "%s: " dbg_fmt,		\
					      __func__, ##arg);		\
		iscsi_dbg_trace(trace_iscsi_dbg_trans_conn,		\
				&(_conn)->dev,				\
				"%s " dbg_fmt, __func__, ##arg);	\
	} while (0);

struct iscsi_internal {
	struct scsi_transport_template t;
	struct iscsi_transport *iscsi_transport;
	struct list_head list;
	struct device dev;

	struct transport_container conn_cont;
	struct transport_container session_cont;
}

static void stop_conn_work_fn(struct work_struct *work);

static struct workqueue_struct *iscsi_eh_timer_workq;

static struct workqueue_struct *iscsi_destroy_workq;

#define to_iscsi_internal(tmpl) \
	container_of(tmpl, struct iscsi_internal, t)

#define dev_to_iscsi_internal(_dev) \
	container_of(_dev, struct iscsi_internal, dev)

static struct class iscsi_transport_class = {
	.name = "iscsi_transport",
	.dev_release = iscsi_transport_release,
};

static DEVICE_ATTR(handle, S_IRUGO, show_transport_handle, NULL);

#define show_transport_attr(name, format)				\
static ssize_t								\
show_transport_##name(struct device *dev, 				\
		      struct device_attribute *attr,char *buf)		\
{									\
	struct iscsi_internal *priv = dev_to_iscsi_internal(dev);	\
	return sprintf(buf, format"\n", priv->iscsi_transport->name);	\
}									\
static DEVICE_ATTR(name, S_IRUGO, show_transport_##name, NULL);

static struct attribute *iscsi_transport_attrs[] = {
	&dev_attr_handle.attr,
	&dev_attr_caps.attr,
	NULL,
};

static struct attribute_group iscsi_transport_group = {
	.attrs = iscsi_transport_attrs,
};

#define iscsi_dev_to_endpoint(_dev) \
	container_of(_dev, struct iscsi_endpoint, dev)

#define ISCSI_ATTR(_prefix,_name,_mode,_show,_store)	\
struct device_attribute dev_attr_##_prefix##_##_name =	\
        __ATTR(_name,_mode,_show,_store)

static struct class iscsi_endpoint_class = {
	.name = "iscsi_endpoint",
	.dev_release = iscsi_endpoint_release,
};

static ISCSI_ATTR(ep, handle, S_IRUGO, show_ep_handle, NULL);

static struct attribute *iscsi_endpoint_attrs[] = {
	&dev_attr_ep_handle.attr,
	NULL,
};

static struct attribute_group iscsi_endpoint_group = {
	.attrs = iscsi_endpoint_attrs,
};

static struct class iscsi_iface_class = {
	.name = "iscsi_iface",
	.dev_release = iscsi_iface_release,
};

#define ISCSI_IFACE_ATTR(_prefix, _name, _mode, _show, _store)	\
struct device_attribute dev_attr_##_prefix##_##_name =		\
	__ATTR(_name, _mode, _show, _store)


}
EXPORT_SYMBOL_GPL(iscsi_unregister_transport);

void iscsi_dbg_trace(void (*trace)(struct device *dev, struct va_format *),
		     struct device *dev, const char *fmt, ...)
{
	struct va_format vaf;
	va_list args;

	va_start(args, fmt);
	vaf.fmt = fmt;
	vaf.va = &args;

#define ISCSI_DBG_TRANS_SESSION(_session, dbg_fmt, arg...)		\
	do {								\
		if (dbg_session)					\
			iscsi_cls_session_printk(KERN_INFO, _session,	\
						 "%s: " dbg_fmt,	\
						 __func__, ##arg);	\
		iscsi_dbg_trace(trace_iscsi_dbg_trans_session,		\
				&(_session)->dev,			\
				"%s " dbg_fmt, __func__, ##arg);	\
	} while (0);

#define ISCSI_DBG_TRANS_CONN(_conn, dbg_fmt, arg...)			\
	do {								\
		if (dbg_conn)						\
			iscsi_cls_conn_printk(KERN_INFO, _conn,		\
					      "%s: " dbg_fmt,		\
					      __func__, ##arg);		\
		iscsi_dbg_trace(trace_iscsi_dbg_trans_conn,		\
				&(_conn)->dev,				\
				"%s " dbg_fmt, __func__, ##arg);	\
	} while (0);

struct iscsi_internal {
	struct scsi_transport_template t;
	struct iscsi_transport *iscsi_transport;
	struct list_head list;
	struct device dev;

	struct transport_container conn_cont;
	struct transport_container session_cont;
}

static void stop_conn_work_fn(struct work_struct *work);

static struct workqueue_struct *iscsi_eh_timer_workq;

static struct workqueue_struct *iscsi_destroy_workq;

#define to_iscsi_internal(tmpl) \
	container_of(tmpl, struct iscsi_internal, t)

#define dev_to_iscsi_internal(_dev) \
	container_of(_dev, struct iscsi_internal, dev)

static struct class iscsi_transport_class = {
	.name = "iscsi_transport",
	.dev_release = iscsi_transport_release,
};

#define show_transport_attr(name, format)				\
static ssize_t								\
show_transport_##name(struct device *dev, 				\
		      struct device_attribute *attr,char *buf)		\
{									\
	struct iscsi_internal *priv = dev_to_iscsi_internal(dev);	\
	return sprintf(buf, format"\n", priv->iscsi_transport->name);	\
}									\
static DEVICE_ATTR(name, S_IRUGO, show_transport_##name, NULL);

static struct attribute *iscsi_transport_attrs[] = {
	&dev_attr_handle.attr,
	&dev_attr_caps.attr,
	NULL,
};

static struct attribute_group iscsi_transport_group = {
	.attrs = iscsi_transport_attrs,
};

#define iscsi_dev_to_endpoint(_dev) \
	container_of(_dev, struct iscsi_endpoint, dev)

#define ISCSI_ATTR(_prefix,_name,_mode,_show,_store)	\
struct device_attribute dev_attr_##_prefix##_##_name =	\
        __ATTR(_name,_mode,_show,_store)

static struct class iscsi_endpoint_class = {
	.name = "iscsi_endpoint",
	.dev_release = iscsi_endpoint_release,
};

static struct attribute *iscsi_endpoint_attrs[] = {
	&dev_attr_ep_handle.attr,
	NULL,
};

static struct attribute_group iscsi_endpoint_group = {
	.attrs = iscsi_endpoint_attrs,
};

static struct class iscsi_iface_class = {
	.name = "iscsi_iface",
	.dev_release = iscsi_iface_release,
};

#define ISCSI_IFACE_ATTR(_prefix, _name, _mode, _show, _store)	\
struct device_attribute dev_attr_##_prefix##_##_name =		\
	__ATTR(_name, _mode, _show, _store)


	return dev->bus == &iscsi_flashnode_bus;
}

static int iscsi_destroy_flashnode_conn(struct iscsi_bus_flash_conn *fnode_conn)
{


}
EXPORT_SYMBOL_GPL(iscsi_get_discovery_parent_name);

static int iscsi_set_flashnode_param(struct iscsi_transport *transport,
				     struct iscsi_uevent *ev, uint32_t len)
{
	char *data = (char *)ev + sizeof(*ev);
	struct Scsi_Host *shost;
	struct iscsi_bus_flash_session *fnode_sess;
	struct iscsi_bus_flash_conn *fnode_conn;
	struct device *dev;
	uint32_t idx;
	int err = 0;

	if (!transport->set_flashnode_param) {
		err = -ENOSYS;
		goto exit_set_fnode;
	}

	shost = scsi_host_lookup(ev->u.set_flashnode.host_no);
	if (!shost) {
		pr_err("%s could not find host no %u\n",
		       __func__, ev->u.set_flashnode.host_no);
		err = -ENODEV;
		goto exit_set_fnode;
	}

	idx = ev->u.set_flashnode.flashnode_idx;
	fnode_sess = iscsi_get_flashnode_by_index(shost, idx);
	if (!fnode_sess) {
		pr_err("%s could not find flashnode %u for host no %u\n",
		       __func__, idx, ev->u.set_flashnode.host_no);
		err = -ENODEV;
		goto put_host;
	}

	dev = iscsi_find_flashnode_conn(fnode_sess);
	if (!dev) {
		err = -ENODEV;
		goto put_sess;
	}

	fnode_conn = iscsi_dev_to_flash_conn(dev);
	err = transport->set_flashnode_param(fnode_sess, fnode_conn, data, len);
	put_device(dev);

put_sess:
	put_device(&fnode_sess->dev);

put_host:
	scsi_host_put(shost);


	return err;
}

static int iscsi_session_has_conns(int sid)
{
	struct iscsi_cls_conn *conn;
	unsigned long flags;
	int found = 0;

	spin_lock_irqsave(&connlock, flags);
	list_for_each_entry(conn, &connlist, conn_list) {
		if (iscsi_conn_get_sid(conn) == sid) {
			found = 1;
			break;
		}
	}
	spin_unlock_irqrestore(&connlock, flags);


}

static int
iscsi_tgt_dscvr(struct iscsi_transport *transport,
		struct iscsi_uevent *ev)
{
	struct Scsi_Host *shost;
	struct sockaddr *dst_addr;
	int err;

	if (!transport->tgt_dscvr)
		return -EINVAL;

	shost = scsi_host_lookup(ev->u.tgt_dscvr.host_no);
	if (!shost) {
		printk(KERN_ERR "target discovery could not find host no %u\n",
		       ev->u.tgt_dscvr.host_no);
		return -ENODEV;
	}

	dst_addr = (struct sockaddr *)((char*)ev + sizeof(*ev));
	err = transport->tgt_dscvr(shost, ev->u.tgt_dscvr.type,
				   ev->u.tgt_dscvr.enable, dst_addr);


char *iscsi_get_router_state_name(enum iscsi_router_state router_state)
{
	int i;
	char *state = NULL;

	for (i = 0; i < ARRAY_SIZE(iscsi_router_state_names); i++) {
		if (iscsi_router_state_names[i].value == router_state) {
			state = iscsi_router_state_names[i].name;
			break;
		}
	}
	return state;

static int dbg_session;

static int dbg_conn;

static struct class iscsi_transport_class = {
	.name = "iscsi_transport",
	.dev_release = iscsi_transport_release,
};

static DEVICE_ATTR(handle, S_IRUGO, show_transport_handle, NULL);

#define show_transport_attr(name, format)				\
static ssize_t								\
show_transport_##name(struct device *dev, 				\
		      struct device_attribute *attr,char *buf)		\
{									\
	struct iscsi_internal *priv = dev_to_iscsi_internal(dev);	\
	return sprintf(buf, format"\n", priv->iscsi_transport->name);	\
}									\
static DEVICE_ATTR(name, S_IRUGO, show_transport_##name, NULL);

static struct attribute *iscsi_transport_attrs[] = {
	&dev_attr_handle.attr,
	&dev_attr_caps.attr,
	NULL,
};

static struct class iscsi_endpoint_class = {
	.name = "iscsi_endpoint",
	.dev_release = iscsi_endpoint_release,
};

static ISCSI_ATTR(ep, handle, S_IRUGO, show_ep_handle, NULL);

static struct attribute *iscsi_endpoint_attrs[] = {
	&dev_attr_ep_handle.attr,
	NULL,
};

static struct class iscsi_iface_class = {
	.name = "iscsi_iface",
	.dev_release = iscsi_iface_release,
};

#define iscsi_iface_attr_show(type, name, param_type, param)		\
static ssize_t								\
show_##type##_##name(struct device *dev, struct device_attribute *attr,	\
		     char *buf)						\
{									\
	struct iscsi_iface *iface = iscsi_dev_to_iface(dev);		\
	struct iscsi_transport *t = iface->transport;			\
	return t->get_iface_param(iface, param_type, param, buf);	\
}									\

#define iscsi_iface_net_attr(type, name, param)				\
	iscsi_iface_attr_show(type, name, ISCSI_NET_PARAM, param)	\
static ISCSI_IFACE_ATTR(type, name, S_IRUGO, show_##type##_##name, NULL);

#define iscsi_iface_attr(type, name, param)				\
	iscsi_iface_attr_show(type, name, ISCSI_IFACE_PARAM, param)	\
static ISCSI_IFACE_ATTR(type, name, S_IRUGO, show_##type##_##name, NULL);


struct iscsi_endpoint *iscsi_lookup_endpoint(u64 handle)
{
	struct iscsi_endpoint *ep;
	struct device *dev;

	dev = class_find_device(&iscsi_endpoint_class, NULL, &handle,
				iscsi_match_epid);
	if (!dev)
		return NULL;

	ep = iscsi_dev_to_endpoint(dev);
	/*
	 * we can drop this now because the interface will prevent
	 * removals and lookups from racing.
	 */
	put_device(dev);
	return ep;
}

#define ISCSI_DBG_TRANS_SESSION(_session, dbg_fmt, arg...)		\
	do {								\
		if (dbg_session)					\
			iscsi_cls_session_printk(KERN_INFO, _session,	\
						 "%s: " dbg_fmt,	\
						 __func__, ##arg);	\
		iscsi_dbg_trace(trace_iscsi_dbg_trans_session,		\
				&(_session)->dev,			\
				"%s " dbg_fmt, __func__, ##arg);	\
	} while (0);

#define ISCSI_DBG_TRANS_CONN(_conn, dbg_fmt, arg...)			\
	do {								\
		if (dbg_conn)						\
			iscsi_cls_conn_printk(KERN_INFO, _conn,		\
					      "%s: " dbg_fmt,		\
					      __func__, ##arg);		\
		iscsi_dbg_trace(trace_iscsi_dbg_trans_conn,		\
				&(_conn)->dev,				\
				"%s " dbg_fmt, __func__, ##arg);	\
	} while (0);

struct iscsi_internal {
	struct scsi_transport_template t;
	struct iscsi_transport *iscsi_transport;
	struct list_head list;
	struct device dev;

	struct transport_container conn_cont;
	struct transport_container session_cont;
}

static void stop_conn_work_fn(struct work_struct *work);

static struct workqueue_struct *iscsi_eh_timer_workq;

static struct workqueue_struct *iscsi_destroy_workq;

#define to_iscsi_internal(tmpl) \
	container_of(tmpl, struct iscsi_internal, t)

#define dev_to_iscsi_internal(_dev) \
	container_of(_dev, struct iscsi_internal, dev)

static struct class iscsi_transport_class = {
	.name = "iscsi_transport",
	.dev_release = iscsi_transport_release,
};

static DEVICE_ATTR(handle, S_IRUGO, show_transport_handle, NULL);

#define show_transport_attr(name, format)				\
static ssize_t								\
show_transport_##name(struct device *dev, 				\
		      struct device_attribute *attr,char *buf)		\
{									\
	struct iscsi_internal *priv = dev_to_iscsi_internal(dev);	\
	return sprintf(buf, format"\n", priv->iscsi_transport->name);	\
}									\
static DEVICE_ATTR(name, S_IRUGO, show_transport_##name, NULL);

static struct attribute *iscsi_transport_attrs[] = {
	&dev_attr_handle.attr,
	&dev_attr_caps.attr,
	NULL,
};

static struct attribute_group iscsi_transport_group = {
	.attrs = iscsi_transport_attrs,
};

#define iscsi_dev_to_endpoint(_dev) \
	container_of(_dev, struct iscsi_endpoint, dev)

#define ISCSI_ATTR(_prefix,_name,_mode,_show,_store)	\
struct device_attribute dev_attr_##_prefix##_##_name =	\
        __ATTR(_name,_mode,_show,_store)

static struct class iscsi_endpoint_class = {
	.name = "iscsi_endpoint",
	.dev_release = iscsi_endpoint_release,
};

static ISCSI_ATTR(ep, handle, S_IRUGO, show_ep_handle, NULL);

static struct attribute *iscsi_endpoint_attrs[] = {
	&dev_attr_ep_handle.attr,
	NULL,
};

static struct attribute_group iscsi_endpoint_group = {
	.attrs = iscsi_endpoint_attrs,
};

static struct class iscsi_iface_class = {
	.name = "iscsi_iface",
	.dev_release = iscsi_iface_release,
};

#define ISCSI_IFACE_ATTR(_prefix, _name, _mode, _show, _store)	\
struct device_attribute dev_attr_##_prefix##_##_name =		\
	__ATTR(_name, _mode, _show, _store)


 * Destroys all the flashnode session entries and all corresponding children
 * flashnode connection entries from sysfs
 */
void iscsi_destroy_all_flashnode(struct Scsi_Host *shost)
{


}
EXPORT_SYMBOL_GPL(iscsi_session_chkready);

int iscsi_is_session_online(struct iscsi_cls_session *session)
{
	unsigned long flags;
	int ret = 0;

	spin_lock_irqsave(&session->lock, flags);
	if (session->state == ISCSI_SESSION_LOGGED_IN)
		ret = 1;

static int dbg_session;

static int dbg_conn;

struct iscsi_internal {
	struct scsi_transport_template t;
	struct iscsi_transport *iscsi_transport;
	struct list_head list;
	struct device dev;

	struct transport_container conn_cont;
	struct transport_container session_cont;
}

static void stop_conn_work_fn(struct work_struct *work);

static struct workqueue_struct *iscsi_eh_timer_workq;

static struct workqueue_struct *iscsi_destroy_workq;

#define to_iscsi_internal(tmpl) \
	container_of(tmpl, struct iscsi_internal, t)

#define dev_to_iscsi_internal(_dev) \
	container_of(_dev, struct iscsi_internal, dev)

static struct class iscsi_transport_class = {
	.name = "iscsi_transport",
	.dev_release = iscsi_transport_release,
};

#define show_transport_attr(name, format)				\
static ssize_t								\
show_transport_##name(struct device *dev, 				\
		      struct device_attribute *attr,char *buf)		\
{									\
	struct iscsi_internal *priv = dev_to_iscsi_internal(dev);	\
	return sprintf(buf, format"\n", priv->iscsi_transport->name);	\
}									\
static DEVICE_ATTR(name, S_IRUGO, show_transport_##name, NULL);

static struct attribute *iscsi_transport_attrs[] = {
	&dev_attr_handle.attr,
	&dev_attr_caps.attr,
	NULL,
};

static struct attribute_group iscsi_transport_group = {
	.attrs = iscsi_transport_attrs,
};

#define iscsi_dev_to_endpoint(_dev) \
	container_of(_dev, struct iscsi_endpoint, dev)

#define ISCSI_ATTR(_prefix,_name,_mode,_show,_store)	\
struct device_attribute dev_attr_##_prefix##_##_name =	\
        __ATTR(_name,_mode,_show,_store)

static struct class iscsi_endpoint_class = {
	.name = "iscsi_endpoint",
	.dev_release = iscsi_endpoint_release,
};

static struct attribute *iscsi_endpoint_attrs[] = {
	&dev_attr_ep_handle.attr,
	NULL,
};

static struct attribute_group iscsi_endpoint_group = {
	.attrs = iscsi_endpoint_attrs,
};

static struct class iscsi_iface_class = {
	.name = "iscsi_iface",
	.dev_release = iscsi_iface_release,
};

#define ISCSI_IFACE_ATTR(_prefix, _name, _mode, _show, _store)	\
struct device_attribute dev_attr_##_prefix##_##_name =		\
	__ATTR(_name, _mode, _show, _store)

#define iscsi_iface_attr_show(type, name, param_type, param)		\
static ssize_t								\
show_##type##_##name(struct device *dev, struct device_attribute *attr,	\
		     char *buf)						\
{									\
	struct iscsi_iface *iface = iscsi_dev_to_iface(dev);		\
	struct iscsi_transport *t = iface->transport;			\
	return t->get_iface_param(iface, param_type, param, buf);	\
}									\


iscsi_session_attr(discovery_parent_type, ISCSI_PARAM_DISCOVERY_PARENT_TYPE, 0);

static ssize_t
show_priv_session_state(struct device *dev, struct device_attribute *attr,
			char *buf)
{


 * @ihost: iscsi_cls_host adding the structures to
 */
static int
iscsi_bsg_host_add(struct Scsi_Host *shost, struct iscsi_cls_host *ihost)
{
	struct device *dev = &shost->shost_gendev;
	struct iscsi_internal *i = to_iscsi_internal(shost->transportt);
	struct request_queue *q;
	char bsg_name[20];

	if (!i->iscsi_transport->bsg_request)
		return -ENOTSUPP;

	snprintf(bsg_name, sizeof(bsg_name), "iscsi_host%d", shost->host_no);
	q = bsg_setup_queue(dev, bsg_name, iscsi_bsg_host_dispatch, NULL, 0);
	if (IS_ERR(q)) {
		shost_printk(KERN_ERR, shost, "bsg interface failed to "
			     "initialize - no request queue\n");
		return PTR_ERR(q);
	}
	__scsi_init_queue(shost, q);


static int iscsi_match_epid(struct device *dev, const void *data)
{
	struct iscsi_endpoint *ep = iscsi_dev_to_endpoint(dev);
	const uint64_t *epid = data;

	return *epid == ep->id;
}


	return 0;
}

static int iscsi_user_scan(struct Scsi_Host *shost, uint channel,
			   uint id, u64 lun)
{
	struct iscsi_scan_data scan_data;

	scan_data.channel = channel;
	scan_data.id = id;
	scan_data.lun = lun;
	scan_data.rescan = SCSI_SCAN_MANUAL;


}
EXPORT_SYMBOL_GPL(iscsi_find_flashnode_conn);

static int iscsi_iter_destroy_flashnode_conn_fn(struct device *dev, void *data)
{
	if (!iscsi_is_flashnode_conn_dev(dev, NULL))
		return 0;


}

static int
iscsi_send_ping(struct iscsi_transport *transport, struct iscsi_uevent *ev)
{
	struct Scsi_Host *shost;
	struct sockaddr *dst_addr;
	int err;

	if (!transport->send_ping)
		return -ENOSYS;

	shost = scsi_host_lookup(ev->u.iscsi_ping.host_no);
	if (!shost) {
		printk(KERN_ERR "iscsi_ping could not find host no %u\n",
		       ev->u.iscsi_ping.host_no);
		return -ENODEV;
	}

	dst_addr = (struct sockaddr *)((char *)ev + sizeof(*ev));
	err = transport->send_ping(shost, ev->u.iscsi_ping.iface_num,
				   ev->u.iscsi_ping.iface_type,
				   ev->u.iscsi_ping.payload_size,
				   ev->u.iscsi_ping.pid,
				   dst_addr);


	return nlmsg_unicast(nls, skb, portid);
}

int iscsi_recv_pdu(struct iscsi_cls_conn *conn, struct iscsi_hdr *hdr,
		   char *data, uint32_t data_size)
{
	struct nlmsghdr	*nlh;
	struct sk_buff *skb;
	struct iscsi_uevent *ev;
	char *pdu;
	struct iscsi_internal *priv;
	int len = nlmsg_total_size(sizeof(*ev) + sizeof(struct iscsi_hdr) +
				   data_size);

	priv = iscsi_if_transport_lookup(conn->transport);
	if (!priv)
		return -EINVAL;

	skb = alloc_skb(len, GFP_ATOMIC);
	if (!skb) {
		iscsi_conn_error_event(conn, ISCSI_ERR_CONN_FAILED);
		iscsi_cls_conn_printk(KERN_ERR, conn, "can not deliver "
				      "control PDU: OOM\n");
		return -ENOMEM;
	}

	nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0);
	ev = nlmsg_data(nlh);
	memset(ev, 0, sizeof(*ev));
	ev->transport_handle = iscsi_handle(conn->transport);
	ev->type = ISCSI_KEVENT_RECV_PDU;
	ev->r.recv_req.cid = conn->cid;
	ev->r.recv_req.sid = iscsi_conn_get_sid(conn);
	pdu = (char*)ev + sizeof(*ev);
	memcpy(pdu, hdr, sizeof(struct iscsi_hdr));
	memcpy(pdu + sizeof(struct iscsi_hdr), data, data_size);

static int dbg_session;

static int dbg_conn;

#define ISCSI_DBG_TRANS_CONN(_conn, dbg_fmt, arg...)			\
	do {								\
		if (dbg_conn)						\
			iscsi_cls_conn_printk(KERN_INFO, _conn,		\
					      "%s: " dbg_fmt,		\
					      __func__, ##arg);		\
		iscsi_dbg_trace(trace_iscsi_dbg_trans_conn,		\
				&(_conn)->dev,				\
				"%s " dbg_fmt, __func__, ##arg);	\
	} while (0);

struct iscsi_internal {
	struct scsi_transport_template t;
	struct iscsi_transport *iscsi_transport;
	struct list_head list;
	struct device dev;

	struct transport_container conn_cont;
	struct transport_container session_cont;
}

static void stop_conn_work_fn(struct work_struct *work);

static struct workqueue_struct *iscsi_eh_timer_workq;

static struct workqueue_struct *iscsi_destroy_workq;

#define to_iscsi_internal(tmpl) \
	container_of(tmpl, struct iscsi_internal, t)

#define dev_to_iscsi_internal(_dev) \
	container_of(_dev, struct iscsi_internal, dev)

static struct class iscsi_transport_class = {
	.name = "iscsi_transport",
	.dev_release = iscsi_transport_release,
};

#define show_transport_attr(name, format)				\
static ssize_t								\
show_transport_##name(struct device *dev, 				\
		      struct device_attribute *attr,char *buf)		\
{									\
	struct iscsi_internal *priv = dev_to_iscsi_internal(dev);	\
	return sprintf(buf, format"\n", priv->iscsi_transport->name);	\
}									\
static DEVICE_ATTR(name, S_IRUGO, show_transport_##name, NULL);

static struct attribute *iscsi_transport_attrs[] = {
	&dev_attr_handle.attr,
	&dev_attr_caps.attr,
	NULL,
};

static struct attribute_group iscsi_transport_group = {
	.attrs = iscsi_transport_attrs,
};

#define iscsi_dev_to_endpoint(_dev) \
	container_of(_dev, struct iscsi_endpoint, dev)

#define ISCSI_ATTR(_prefix,_name,_mode,_show,_store)	\
struct device_attribute dev_attr_##_prefix##_##_name =	\
        __ATTR(_name,_mode,_show,_store)

static struct class iscsi_endpoint_class = {
	.name = "iscsi_endpoint",
	.dev_release = iscsi_endpoint_release,
};

static struct attribute *iscsi_endpoint_attrs[] = {
	&dev_attr_ep_handle.attr,
	NULL,
};

static struct attribute_group iscsi_endpoint_group = {
	.attrs = iscsi_endpoint_attrs,
};

static struct class iscsi_iface_class = {
	.name = "iscsi_iface",
	.dev_release = iscsi_iface_release,
};

#define ISCSI_IFACE_ATTR(_prefix, _name, _mode, _show, _store)	\
struct device_attribute dev_attr_##_prefix##_##_name =		\
	__ATTR(_name, _mode, _show, _store)

#define iscsi_iface_attr_show(type, name, param_type, param)		\
static ssize_t								\
show_##type##_##name(struct device *dev, struct device_attribute *attr,	\
		     char *buf)						\
{									\
	struct iscsi_iface *iface = iscsi_dev_to_iface(dev);		\
	struct iscsi_transport *t = iface->transport;			\
	return t->get_iface_param(iface, param_type, param, buf);	\
}									\


	{ISCSI_DISC_PARENT_ISNS,	"isns" },
};

char *iscsi_get_discovery_parent_name(int parent_type)
{
	int i;
	char *state = "Unknown!";

	for (i = 0; i < ARRAY_SIZE(iscsi_discovery_parent_names); i++) {
		if (iscsi_discovery_parent_names[i].value & parent_type) {
			state = iscsi_discovery_parent_names[i].name;
			break;
		}

static int dbg_session;

static int dbg_conn;

static struct class iscsi_transport_class = {
	.name = "iscsi_transport",
	.dev_release = iscsi_transport_release,
};

#define show_transport_attr(name, format)				\
static ssize_t								\
show_transport_##name(struct device *dev, 				\
		      struct device_attribute *attr,char *buf)		\
{									\
	struct iscsi_internal *priv = dev_to_iscsi_internal(dev);	\
	return sprintf(buf, format"\n", priv->iscsi_transport->name);	\
}									\
static DEVICE_ATTR(name, S_IRUGO, show_transport_##name, NULL);

static struct class iscsi_endpoint_class = {
	.name = "iscsi_endpoint",
	.dev_release = iscsi_endpoint_release,
};

static struct class iscsi_iface_class = {
	.name = "iscsi_iface",
	.dev_release = iscsi_iface_release,
};

#define iscsi_iface_attr_show(type, name, param_type, param)		\
static ssize_t								\
show_##type##_##name(struct device *dev, struct device_attribute *attr,	\
		     char *buf)						\
{									\
	struct iscsi_iface *iface = iscsi_dev_to_iface(dev);		\
	struct iscsi_transport *t = iface->transport;			\
	return t->get_iface_param(iface, param_type, param, buf);	\
}									\

#define iscsi_iface_net_attr(type, name, param)				\
	iscsi_iface_attr_show(type, name, ISCSI_NET_PARAM, param)	\
static ISCSI_IFACE_ATTR(type, name, S_IRUGO, show_##type##_##name, NULL);

#define iscsi_iface_attr(type, name, param)				\
	iscsi_iface_attr_show(type, name, ISCSI_IFACE_PARAM, param)	\
static ISCSI_IFACE_ATTR(type, name, S_IRUGO, show_##type##_##name, NULL);

static const struct {
	enum iscsi_ipaddress_state	value;
	char				*name;
} iscsi_ipaddress_state_names[] = {
	{ISCSI_IPDDRESS_STATE_UNCONFIGURED,	"Unconfigured" },
	{ISCSI_IPDDRESS_STATE_ACQUIRING,	"Acquiring" },
	{ISCSI_IPDDRESS_STATE_TENTATIVE,	"Tentative" },
	{ISCSI_IPDDRESS_STATE_VALID,		"Valid" },
	{ISCSI_IPDDRESS_STATE_DISABLING,	"Disabling" },
	{ISCSI_IPDDRESS_STATE_INVALID,		"Invalid" },
	{ISCSI_IPDDRESS_STATE_DEPRECATED,	"Deprecated" },
};

static const struct {
	enum iscsi_router_state	value;
	char			*name;
} iscsi_router_state_names[] = {
	{ISCSI_ROUTER_STATE_UNKNOWN,		"Unknown" },
	{ISCSI_ROUTER_STATE_ADVERTISED,		"Advertised" },
	{ISCSI_ROUTER_STATE_MANUAL,		"Manual" },
	{ISCSI_ROUTER_STATE_STALE,		"Stale" },
};

#define iscsi_flashnode_sess_attr_show(type, name, param)		\
static ssize_t								\
show_##type##_##name(struct device *dev, struct device_attribute *attr,	\
		     char *buf)						\
{									\
	struct iscsi_bus_flash_session *fnode_sess =			\
					iscsi_dev_to_flash_session(dev);\
	struct iscsi_transport *t = fnode_sess->transport;		\
	return t->get_flashnode_param(fnode_sess, param, buf);		\
}									\


	return err;
}

static int iscsi_del_flashnode(struct iscsi_transport *transport,
			       struct iscsi_uevent *ev)
{
	struct Scsi_Host *shost;
	struct iscsi_bus_flash_session *fnode_sess;
	uint32_t idx;
	int err = 0;

	if (!transport->del_flashnode) {
		err = -ENOSYS;
		goto exit_del_fnode;
	}

	shost = scsi_host_lookup(ev->u.del_flashnode.host_no);
	if (!shost) {
		pr_err("%s could not find host no %u\n",
		       __func__, ev->u.del_flashnode.host_no);
		err = -ENODEV;
		goto put_host;
	}

	idx = ev->u.del_flashnode.flashnode_idx;
	fnode_sess = iscsi_get_flashnode_by_index(shost, idx);
	if (!fnode_sess) {
		pr_err("%s could not find flashnode %u for host no %u\n",
		       __func__, idx, ev->u.del_flashnode.host_no);
		err = -ENODEV;
		goto put_host;
	}

	err = transport->del_flashnode(fnode_sess);
	put_device(&fnode_sess->dev);

put_host:
	scsi_host_put(shost);


	}
}

void iscsi_conn_error_event(struct iscsi_cls_conn *conn, enum iscsi_err error)
{
	struct nlmsghdr	*nlh;
	struct sk_buff	*skb;
	struct iscsi_uevent *ev;
	struct iscsi_internal *priv;
	int len = nlmsg_total_size(sizeof(*ev));
	unsigned long flags;

	spin_lock_irqsave(&connlock, flags);
	list_add(&conn->conn_list_err, &connlist_err);
	spin_unlock_irqrestore(&connlock, flags);
	queue_work(system_unbound_wq, &stop_conn_work);

	priv = iscsi_if_transport_lookup(conn->transport);
	if (!priv)
		return;

	skb = alloc_skb(len, GFP_ATOMIC);
	if (!skb) {
		iscsi_cls_conn_printk(KERN_ERR, conn, "gracefully ignored "
				      "conn error (%d)\n", error);
		return;
	}

	nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0);
	ev = nlmsg_data(nlh);
	ev->transport_handle = iscsi_handle(conn->transport);
	ev->type = ISCSI_KEVENT_CONN_ERROR;
	ev->r.connerror.error = error;
	ev->r.connerror.cid = conn->cid;
	ev->r.connerror.sid = iscsi_conn_get_sid(conn);

	iscsi_multicast_skb(skb, ISCSI_NL_GRP_ISCSID, GFP_ATOMIC);

static int dbg_session;

static int dbg_conn;

#define ISCSI_DBG_TRANS_CONN(_conn, dbg_fmt, arg...)			\
	do {								\
		if (dbg_conn)						\
			iscsi_cls_conn_printk(KERN_INFO, _conn,		\
					      "%s: " dbg_fmt,		\
					      __func__, ##arg);		\
		iscsi_dbg_trace(trace_iscsi_dbg_trans_conn,		\
				&(_conn)->dev,				\
				"%s " dbg_fmt, __func__, ##arg);	\
	} while (0);

struct iscsi_internal {
	struct scsi_transport_template t;
	struct iscsi_transport *iscsi_transport;
	struct list_head list;
	struct device dev;

	struct transport_container conn_cont;
	struct transport_container session_cont;
}

static void stop_conn_work_fn(struct work_struct *work);

static DECLARE_WORK(stop_conn_work, stop_conn_work_fn);

static struct workqueue_struct *iscsi_eh_timer_workq;

static struct workqueue_struct *iscsi_destroy_workq;

#define to_iscsi_internal(tmpl) \
	container_of(tmpl, struct iscsi_internal, t)

#define dev_to_iscsi_internal(_dev) \
	container_of(_dev, struct iscsi_internal, dev)

static struct class iscsi_transport_class = {
	.name = "iscsi_transport",
	.dev_release = iscsi_transport_release,
};

#define show_transport_attr(name, format)				\
static ssize_t								\
show_transport_##name(struct device *dev, 				\
		      struct device_attribute *attr,char *buf)		\
{									\
	struct iscsi_internal *priv = dev_to_iscsi_internal(dev);	\
	return sprintf(buf, format"\n", priv->iscsi_transport->name);	\
}									\
static DEVICE_ATTR(name, S_IRUGO, show_transport_##name, NULL);

static struct attribute *iscsi_transport_attrs[] = {
	&dev_attr_handle.attr,
	&dev_attr_caps.attr,
	NULL,
};

static struct attribute_group iscsi_transport_group = {
	.attrs = iscsi_transport_attrs,
};

#define iscsi_dev_to_endpoint(_dev) \
	container_of(_dev, struct iscsi_endpoint, dev)

#define ISCSI_ATTR(_prefix,_name,_mode,_show,_store)	\
struct device_attribute dev_attr_##_prefix##_##_name =	\
        __ATTR(_name,_mode,_show,_store)

static struct class iscsi_endpoint_class = {
	.name = "iscsi_endpoint",
	.dev_release = iscsi_endpoint_release,
};

static struct attribute *iscsi_endpoint_attrs[] = {
	&dev_attr_ep_handle.attr,
	NULL,
};

static struct attribute_group iscsi_endpoint_group = {
	.attrs = iscsi_endpoint_attrs,
};

static struct class iscsi_iface_class = {
	.name = "iscsi_iface",
	.dev_release = iscsi_iface_release,
};

#define ISCSI_IFACE_ATTR(_prefix, _name, _mode, _show, _store)	\
struct device_attribute dev_attr_##_prefix##_##_name =		\
	__ATTR(_name, _mode, _show, _store)

#define iscsi_iface_attr_show(type, name, param_type, param)		\
static ssize_t								\
show_##type##_##name(struct device *dev, struct device_attribute *attr,	\
		     char *buf)						\
{									\
	struct iscsi_iface *iface = iscsi_dev_to_iface(dev);		\
	struct iscsi_transport *t = iface->transport;			\
	return t->get_iface_param(iface, param_type, param, buf);	\
}									\


}

static int
iscsi_set_iface_params(struct iscsi_transport *transport,
		       struct iscsi_uevent *ev, uint32_t len)
{
	char *data = (char *)ev + sizeof(*ev);
	struct Scsi_Host *shost;
	int err;

	if (!transport->set_iface_param)
		return -ENOSYS;

	shost = scsi_host_lookup(ev->u.set_iface_params.host_no);
	if (!shost) {
		printk(KERN_ERR "set_iface_params could not find host no %u\n",
		       ev->u.set_iface_params.host_no);
		return -ENODEV;
	}

	err = transport->set_iface_param(shost, data, len);


	return &priv->conn_cont.ac == cont;
}

static int iscsi_host_match(struct attribute_container *cont,
			    struct device *dev)
{
	struct Scsi_Host *shost;
	struct iscsi_internal *priv;

	if (!scsi_is_host_device(dev))
		return 0;

	shost = dev_to_shost(dev);
	if (!shost->transportt  ||
	    shost->transportt->host_attrs.ac.class != &iscsi_host_class.class)
		return 0;


static ISCSI_CLASS_ATTR(priv_sess, creator, S_IRUGO, show_priv_session_creator,
			NULL);
static ssize_t
show_priv_session_target_id(struct device *dev, struct device_attribute *attr,
			    char *buf)
{


}

static int
iscsi_if_transport_ep(struct iscsi_transport *transport,
		      struct iscsi_uevent *ev, int msg_type)
{
	struct iscsi_endpoint *ep;
	int rc = 0;

	switch (msg_type) {
	case ISCSI_UEVENT_TRANSPORT_EP_CONNECT_THROUGH_HOST:
	case ISCSI_UEVENT_TRANSPORT_EP_CONNECT:
		rc = iscsi_if_ep_connect(transport, ev, msg_type);
		break;
	case ISCSI_UEVENT_TRANSPORT_EP_POLL:
		if (!transport->ep_poll)
			return -EINVAL;

		ep = iscsi_lookup_endpoint(ev->u.ep_poll.ep_handle);
		if (!ep)
			return -EINVAL;

		ev->r.retcode = transport->ep_poll(ep,
						   ev->u.ep_poll.timeout_ms);
		break;
	case ISCSI_UEVENT_TRANSPORT_EP_DISCONNECT:
		rc = iscsi_if_ep_disconnect(transport,
					    ev->u.ep_disconnect.ep_handle);
		break;


}

struct iscsi_cls_session *
iscsi_alloc_session(struct Scsi_Host *shost, struct iscsi_transport *transport,
		    int dd_size)
{
	struct iscsi_cls_session *session;

	session = kzalloc(sizeof(*session) + dd_size,
			  GFP_KERNEL);
	if (!session)
		return NULL;

	session->transport = transport;
	session->creator = -1;
	session->recovery_tmo = 120;
	session->recovery_tmo_sysfs_override = false;
	session->state = ISCSI_SESSION_FREE;
	INIT_DELAYED_WORK(&session->recovery_work, session_recovery_timedout);
	INIT_LIST_HEAD(&session->sess_list);
	INIT_WORK(&session->unblock_work, __iscsi_unblock_session);
	INIT_WORK(&session->block_work, __iscsi_block_session);
	INIT_WORK(&session->unbind_work, __iscsi_unbind_session);
	INIT_WORK(&session->scan_work, iscsi_scan_session);
	INIT_WORK(&session->destroy_work, __iscsi_destroy_session);
	spin_lock_init(&session->lock);

	/* this is released in the dev's release function */
	scsi_host_get(shost);
	session->dev.parent = &shost->shost_gendev;
	session->dev.release = iscsi_session_release;
	device_initialize(&session->dev);
	if (dd_size)
		session->dd_data = &session[1];


static void iscsi_transport_release(struct device *dev)
{
	struct iscsi_internal *priv = dev_to_iscsi_internal(dev);
	kfree(priv);
}


	return err;
}

static int iscsi_delete_chap(struct iscsi_transport *transport,
			     struct iscsi_uevent *ev)
{
	struct Scsi_Host *shost;
	int err = 0;

	if (!transport->delete_chap)
		return -ENOSYS;

	shost = scsi_host_lookup(ev->u.delete_chap.host_no);
	if (!shost) {
		printk(KERN_ERR "%s could not find host no %u\n",
		       __func__, ev->u.delete_chap.host_no);
		return -ENODEV;
	}

	err = transport->delete_chap(shost, ev->u.delete_chap.chap_tbl_idx);


	{ ISCSI_SESSION_FREE,		"FREE" },
};

static const char *iscsi_session_state_name(int state)
{
	int i;
	char *name = NULL;

	for (i = 0; i < ARRAY_SIZE(iscsi_session_state_names); i++) {
		if (iscsi_session_state_names[i].value == state) {
			name = iscsi_session_state_names[i].name;
			break;
		}

static int dbg_session;

static int dbg_conn;

static void stop_conn_work_fn(struct work_struct *work);

static DECLARE_WORK(stop_conn_work, stop_conn_work_fn);

static atomic_t iscsi_session_nr;

static struct workqueue_struct *iscsi_eh_timer_workq;

static struct workqueue_struct *iscsi_destroy_workq;

static DEFINE_IDA(iscsi_sess_ida);

static LIST_HEAD(iscsi_transports);

static DEFINE_SPINLOCK(iscsi_transport_lock);

static struct class iscsi_transport_class = {
	.name = "iscsi_transport",
	.dev_release = iscsi_transport_release,
};

static DEVICE_ATTR(handle, S_IRUGO, show_transport_handle, NULL);

#define show_transport_attr(name, format)				\
static ssize_t								\
show_transport_##name(struct device *dev, 				\
		      struct device_attribute *attr,char *buf)		\
{									\
	struct iscsi_internal *priv = dev_to_iscsi_internal(dev);	\
	return sprintf(buf, format"\n", priv->iscsi_transport->name);	\
}									\
static DEVICE_ATTR(name, S_IRUGO, show_transport_##name, NULL);

static struct attribute *iscsi_transport_attrs[] = {
	&dev_attr_handle.attr,
	&dev_attr_caps.attr,
	NULL,
};

static struct attribute_group iscsi_transport_group = {
	.attrs = iscsi_transport_attrs,
};

static struct class iscsi_endpoint_class = {
	.name = "iscsi_endpoint",
	.dev_release = iscsi_endpoint_release,
};

static ISCSI_ATTR(ep, handle, S_IRUGO, show_ep_handle, NULL);

static struct attribute *iscsi_endpoint_attrs[] = {
	&dev_attr_ep_handle.attr,
	NULL,
};

static struct attribute_group iscsi_endpoint_group = {
	.attrs = iscsi_endpoint_attrs,
};

static struct class iscsi_iface_class = {
	.name = "iscsi_iface",
	.dev_release = iscsi_iface_release,
};

#define iscsi_iface_attr_show(type, name, param_type, param)		\
static ssize_t								\
show_##type##_##name(struct device *dev, struct device_attribute *attr,	\
		     char *buf)						\
{									\
	struct iscsi_iface *iface = iscsi_dev_to_iface(dev);		\
	struct iscsi_transport *t = iface->transport;			\
	return t->get_iface_param(iface, param_type, param, buf);	\
}									\

#define iscsi_iface_net_attr(type, name, param)				\
	iscsi_iface_attr_show(type, name, ISCSI_NET_PARAM, param)	\
static ISCSI_IFACE_ATTR(type, name, S_IRUGO, show_##type##_##name, NULL);

#define iscsi_iface_attr(type, name, param)				\
	iscsi_iface_attr_show(type, name, ISCSI_IFACE_PARAM, param)	\
static ISCSI_IFACE_ATTR(type, name, S_IRUGO, show_##type##_##name, NULL);


	NULL,
};

static umode_t iscsi_conn_attr_is_visible(struct kobject *kobj,
					 struct attribute *attr, int i)
{
	struct device *cdev = container_of(kobj, struct device, kobj);
	struct iscsi_cls_conn *conn = transport_class_to_conn(cdev);
	struct iscsi_transport *t = conn->transport;
	int param;

	if (attr == &dev_attr_conn_max_recv_dlength.attr)
		param = ISCSI_PARAM_MAX_RECV_DLENGTH;
	else if (attr == &dev_attr_conn_max_xmit_dlength.attr)
		param = ISCSI_PARAM_MAX_XMIT_DLENGTH;
	else if (attr == &dev_attr_conn_header_digest.attr)
		param = ISCSI_PARAM_HDRDGST_EN;
	else if (attr == &dev_attr_conn_data_digest.attr)
		param = ISCSI_PARAM_DATADGST_EN;
	else if (attr == &dev_attr_conn_ifmarker.attr)
		param = ISCSI_PARAM_IFMARKER_EN;
	else if (attr == &dev_attr_conn_ofmarker.attr)
		param = ISCSI_PARAM_OFMARKER_EN;
	else if (attr == &dev_attr_conn_address.attr)
		param = ISCSI_PARAM_CONN_ADDRESS;
	else if (attr == &dev_attr_conn_port.attr)
		param = ISCSI_PARAM_CONN_PORT;
	else if (attr == &dev_attr_conn_exp_statsn.attr)
		param = ISCSI_PARAM_EXP_STATSN;
	else if (attr == &dev_attr_conn_persistent_address.attr)
		param = ISCSI_PARAM_PERSISTENT_ADDRESS;
	else if (attr == &dev_attr_conn_persistent_port.attr)
		param = ISCSI_PARAM_PERSISTENT_PORT;
	else if (attr == &dev_attr_conn_ping_tmo.attr)
		param = ISCSI_PARAM_PING_TMO;
	else if (attr == &dev_attr_conn_recv_tmo.attr)
		param = ISCSI_PARAM_RECV_TMO;
	else if (attr == &dev_attr_conn_local_port.attr)
		param = ISCSI_PARAM_LOCAL_PORT;
	else if (attr == &dev_attr_conn_statsn.attr)
		param = ISCSI_PARAM_STATSN;
	else if (attr == &dev_attr_conn_keepalive_tmo.attr)
		param = ISCSI_PARAM_KEEPALIVE_TMO;
	else if (attr == &dev_attr_conn_max_segment_size.attr)
		param = ISCSI_PARAM_MAX_SEGMENT_SIZE;
	else if (attr == &dev_attr_conn_tcp_timestamp_stat.attr)
		param = ISCSI_PARAM_TCP_TIMESTAMP_STAT;
	else if (attr == &dev_attr_conn_tcp_wsf_disable.attr)
		param = ISCSI_PARAM_TCP_WSF_DISABLE;
	else if (attr == &dev_attr_conn_tcp_nagle_disable.attr)
		param = ISCSI_PARAM_TCP_NAGLE_DISABLE;
	else if (attr == &dev_attr_conn_tcp_timer_scale.attr)
		param = ISCSI_PARAM_TCP_TIMER_SCALE;
	else if (attr == &dev_attr_conn_tcp_timestamp_enable.attr)
		param = ISCSI_PARAM_TCP_TIMESTAMP_EN;
	else if (attr == &dev_attr_conn_fragment_disable.attr)
		param = ISCSI_PARAM_IP_FRAGMENT_DISABLE;
	else if (attr == &dev_attr_conn_ipv4_tos.attr)
		param = ISCSI_PARAM_IPV4_TOS;
	else if (attr == &dev_attr_conn_ipv6_traffic_class.attr)
		param = ISCSI_PARAM_IPV6_TC;
	else if (attr == &dev_attr_conn_ipv6_flow_label.attr)
		param = ISCSI_PARAM_IPV6_FLOW_LABEL;
	else if (attr == &dev_attr_conn_is_fw_assigned_ipv6.attr)
		param = ISCSI_PARAM_IS_FW_ASSIGNED_IPV6;
	else if (attr == &dev_attr_conn_tcp_xmit_wsf.attr)
		param = ISCSI_PARAM_TCP_XMIT_WSF;
	else if (attr == &dev_attr_conn_tcp_recv_wsf.attr)
		param = ISCSI_PARAM_TCP_RECV_WSF;
	else if (attr == &dev_attr_conn_local_ipaddr.attr)
		param = ISCSI_PARAM_LOCAL_IPADDR;
	else if (attr == &dev_attr_conn_state.attr)
		return S_IRUGO;
	else {
		WARN_ONCE(1, "Invalid conn attr");
		return 0;
	}


void iscsi_destroy_endpoint(struct iscsi_endpoint *ep)
{
	sysfs_remove_group(&ep->dev.kobj, &iscsi_endpoint_group);
	device_unregister(&ep->dev);
}


 *  %NULL on failure
 */
struct iscsi_bus_flash_session *
iscsi_create_flashnode_sess(struct Scsi_Host *shost, int index,
			    struct iscsi_transport *transport,
			    int dd_size)
{
	struct iscsi_bus_flash_session *fnode_sess;
	int err;

	fnode_sess = kzalloc(sizeof(*fnode_sess) + dd_size, GFP_KERNEL);
	if (!fnode_sess)
		return NULL;

	fnode_sess->transport = transport;
	fnode_sess->target_id = index;
	fnode_sess->dev.type = &iscsi_flashnode_sess_dev_type;
	fnode_sess->dev.bus = &iscsi_flashnode_bus;
	fnode_sess->dev.parent = &shost->shost_gendev;
	dev_set_name(&fnode_sess->dev, "flashnode_sess-%u:%u",
		     shost->host_no, index);

	err = device_register(&fnode_sess->dev);
	if (err)
		goto free_fnode_sess;

	if (dd_size)
		fnode_sess->dd_data = &fnode_sess[1];

	return fnode_sess;

free_fnode_sess:


}

static int
iscsi_set_host_param(struct iscsi_transport *transport,
		     struct iscsi_uevent *ev)
{
	char *data = (char*)ev + sizeof(*ev);
	struct Scsi_Host *shost;
	int err;

	if (!transport->set_host_param)
		return -ENOSYS;

	if (ev->u.set_host_param.len > PAGE_SIZE)
		return -EINVAL;

	shost = scsi_host_lookup(ev->u.set_host_param.host_no);
	if (!shost) {
		printk(KERN_ERR "set_host_param could not find host no %u\n",
		       ev->u.set_host_param.host_no);
		return -ENODEV;
	}

	err = transport->set_host_param(shost, ev->u.set_host_param.param,
					data, ev->u.set_host_param.len);

static int dbg_session;

static int dbg_conn;

struct iscsi_internal {
	struct scsi_transport_template t;
	struct iscsi_transport *iscsi_transport;
	struct list_head list;
	struct device dev;

	struct transport_container conn_cont;
	struct transport_container session_cont;
}

static void stop_conn_work_fn(struct work_struct *work);

static DECLARE_WORK(stop_conn_work, stop_conn_work_fn);

static atomic_t iscsi_session_nr;

static struct workqueue_struct *iscsi_eh_timer_workq;

static struct workqueue_struct *iscsi_destroy_workq;

static DEFINE_IDA(iscsi_sess_ida);

static LIST_HEAD(iscsi_transports);

static DEFINE_SPINLOCK(iscsi_transport_lock);

#define to_iscsi_internal(tmpl) \
	container_of(tmpl, struct iscsi_internal, t)

#define dev_to_iscsi_internal(_dev) \
	container_of(_dev, struct iscsi_internal, dev)

static struct class iscsi_transport_class = {
	.name = "iscsi_transport",
	.dev_release = iscsi_transport_release,
};

static DEVICE_ATTR(handle, S_IRUGO, show_transport_handle, NULL);

#define show_transport_attr(name, format)				\
static ssize_t								\
show_transport_##name(struct device *dev, 				\
		      struct device_attribute *attr,char *buf)		\
{									\
	struct iscsi_internal *priv = dev_to_iscsi_internal(dev);	\
	return sysfs_emit(buf, format"\n", priv->iscsi_transport->name);\
}									\
static DEVICE_ATTR(name, S_IRUGO, show_transport_##name, NULL);

static struct attribute *iscsi_transport_attrs[] = {
	&dev_attr_handle.attr,
	&dev_attr_caps.attr,
	NULL,
};

static struct attribute_group iscsi_transport_group = {
	.attrs = iscsi_transport_attrs,
};

#define iscsi_dev_to_endpoint(_dev) \
	container_of(_dev, struct iscsi_endpoint, dev)

#define ISCSI_ATTR(_prefix,_name,_mode,_show,_store)	\
struct device_attribute dev_attr_##_prefix##_##_name =	\
        __ATTR(_name,_mode,_show,_store)

static struct class iscsi_endpoint_class = {
	.name = "iscsi_endpoint",
	.dev_release = iscsi_endpoint_release,
};

static ISCSI_ATTR(ep, handle, S_IRUGO, show_ep_handle, NULL);

static struct attribute *iscsi_endpoint_attrs[] = {
	&dev_attr_ep_handle.attr,
	NULL,
};

static struct attribute_group iscsi_endpoint_group = {
	.attrs = iscsi_endpoint_attrs,
};

static struct class iscsi_iface_class = {
	.name = "iscsi_iface",
	.dev_release = iscsi_iface_release,
};

#define ISCSI_IFACE_ATTR(_prefix, _name, _mode, _show, _store)	\
struct device_attribute dev_attr_##_prefix##_##_name =		\
	__ATTR(_name, _mode, _show, _store)

#define iscsi_iface_attr_show(type, name, param_type, param)		\
static ssize_t								\
show_##type##_##name(struct device *dev, struct device_attribute *attr,	\
		     char *buf)						\
{									\
	struct iscsi_iface *iface = iscsi_dev_to_iface(dev);		\
	struct iscsi_transport *t = iface->transport;			\
	return t->get_iface_param(iface, param_type, param, buf);	\
}									\

#define iscsi_iface_net_attr(type, name, param)				\
	iscsi_iface_attr_show(type, name, ISCSI_NET_PARAM, param)	\
static ISCSI_IFACE_ATTR(type, name, S_IRUGO, show_##type##_##name, NULL);


static void iscsi_iface_release(struct device *dev)
{
	struct iscsi_iface *iface = iscsi_dev_to_iface(dev);
	struct device *parent = iface->dev.parent;

	kfree(iface);
	put_device(parent);
}