linux/drivers/target/tcm_fc/tfc_sess.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2010 Cisco Systems, Inc.
   3 *
   4 * This program is free software; you can redistribute it and/or modify it
   5 * under the terms and conditions of the GNU General Public License,
   6 * version 2, as published by the Free Software Foundation.
   7 *
   8 * This program is distributed in the hope it will be useful, but WITHOUT
   9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  10 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  11 * more details.
  12 *
  13 * You should have received a copy of the GNU General Public License along with
  14 * this program; if not, write to the Free Software Foundation, Inc.,
  15 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
  16 */
  17
  18/* XXX TBD some includes may be extraneous */
  19
  20#include <linux/module.h>
  21#include <linux/moduleparam.h>
  22#include <linux/utsname.h>
  23#include <linux/init.h>
  24#include <linux/slab.h>
  25#include <linux/kthread.h>
  26#include <linux/types.h>
  27#include <linux/string.h>
  28#include <linux/configfs.h>
  29#include <linux/ctype.h>
  30#include <linux/hash.h>
  31#include <linux/rcupdate.h>
  32#include <linux/rculist.h>
  33#include <linux/kref.h>
  34#include <asm/unaligned.h>
  35#include <scsi/libfc.h>
  36
  37#include <target/target_core_base.h>
  38#include <target/target_core_fabric.h>
  39
  40#include "tcm_fc.h"
  41
  42#define TFC_SESS_DBG(lport, fmt, args...) \
  43        pr_debug("host%u: rport %6.6x: " fmt,      \
  44                 (lport)->host->host_no,           \
  45                 (lport)->port_id, ##args )
  46
  47static void ft_sess_delete_all(struct ft_tport *);
  48
  49/*
  50 * Lookup or allocate target local port.
  51 * Caller holds ft_lport_lock.
  52 */
  53static struct ft_tport *ft_tport_get(struct fc_lport *lport)
  54{
  55        struct ft_tpg *tpg;
  56        struct ft_tport *tport;
  57        int i;
  58
  59        tport = rcu_dereference_protected(lport->prov[FC_TYPE_FCP],
  60                                          lockdep_is_held(&ft_lport_lock));
  61        if (tport && tport->tpg)
  62                return tport;
  63
  64        tpg = ft_lport_find_tpg(lport);
  65        if (!tpg)
  66                return NULL;
  67
  68        if (tport) {
  69                tport->tpg = tpg;
  70                tpg->tport = tport;
  71                return tport;
  72        }
  73
  74        tport = kzalloc(sizeof(*tport), GFP_KERNEL);
  75        if (!tport)
  76                return NULL;
  77
  78        tport->lport = lport;
  79        tport->tpg = tpg;
  80        tpg->tport = tport;
  81        for (i = 0; i < FT_SESS_HASH_SIZE; i++)
  82                INIT_HLIST_HEAD(&tport->hash[i]);
  83
  84        rcu_assign_pointer(lport->prov[FC_TYPE_FCP], tport);
  85        return tport;
  86}
  87
  88/*
  89 * Delete a target local port.
  90 * Caller holds ft_lport_lock.
  91 */
  92static void ft_tport_delete(struct ft_tport *tport)
  93{
  94        struct fc_lport *lport;
  95        struct ft_tpg *tpg;
  96
  97        ft_sess_delete_all(tport);
  98        lport = tport->lport;
  99        lport->service_params &= ~FCP_SPPF_TARG_FCN;
 100        BUG_ON(tport != lport->prov[FC_TYPE_FCP]);
 101        RCU_INIT_POINTER(lport->prov[FC_TYPE_FCP], NULL);
 102
 103        tpg = tport->tpg;
 104        if (tpg) {
 105                tpg->tport = NULL;
 106                tport->tpg = NULL;
 107        }
 108        kfree_rcu(tport, rcu);
 109}
 110
 111/*
 112 * Add local port.
 113 * Called thru fc_lport_iterate().
 114 */
 115void ft_lport_add(struct fc_lport *lport, void *arg)
 116{
 117        mutex_lock(&ft_lport_lock);
 118        ft_tport_get(lport);
 119        lport->service_params |= FCP_SPPF_TARG_FCN;
 120        mutex_unlock(&ft_lport_lock);
 121}
 122
 123/*
 124 * Delete local port.
 125 * Called thru fc_lport_iterate().
 126 */
 127void ft_lport_del(struct fc_lport *lport, void *arg)
 128{
 129        struct ft_tport *tport;
 130
 131        mutex_lock(&ft_lport_lock);
 132        tport = lport->prov[FC_TYPE_FCP];
 133        if (tport)
 134                ft_tport_delete(tport);
 135        mutex_unlock(&ft_lport_lock);
 136}
 137
 138/*
 139 * Notification of local port change from libfc.
 140 * Create or delete local port and associated tport.
 141 */
 142int ft_lport_notify(struct notifier_block *nb, unsigned long event, void *arg)
 143{
 144        struct fc_lport *lport = arg;
 145
 146        switch (event) {
 147        case FC_LPORT_EV_ADD:
 148                ft_lport_add(lport, NULL);
 149                break;
 150        case FC_LPORT_EV_DEL:
 151                ft_lport_del(lport, NULL);
 152                break;
 153        }
 154        return NOTIFY_DONE;
 155}
 156
 157/*
 158 * Hash function for FC_IDs.
 159 */
 160static u32 ft_sess_hash(u32 port_id)
 161{
 162        return hash_32(port_id, FT_SESS_HASH_BITS);
 163}
 164
 165/*
 166 * Find session in local port.
 167 * Sessions and hash lists are RCU-protected.
 168 * A reference is taken which must be eventually freed.
 169 */
 170static struct ft_sess *ft_sess_get(struct fc_lport *lport, u32 port_id)
 171{
 172        struct ft_tport *tport;
 173        struct hlist_head *head;
 174        struct ft_sess *sess;
 175        char *reason = "no session created";
 176
 177        rcu_read_lock();
 178        tport = rcu_dereference(lport->prov[FC_TYPE_FCP]);
 179        if (!tport) {
 180                reason = "not an FCP port";
 181                goto out;
 182        }
 183
 184        head = &tport->hash[ft_sess_hash(port_id)];
 185        hlist_for_each_entry_rcu(sess, head, hash) {
 186                if (sess->port_id == port_id) {
 187                        kref_get(&sess->kref);
 188                        rcu_read_unlock();
 189                        TFC_SESS_DBG(lport, "port_id %x found %p\n",
 190                                     port_id, sess);
 191                        return sess;
 192                }
 193        }
 194out:
 195        rcu_read_unlock();
 196        TFC_SESS_DBG(lport, "port_id %x not found, %s\n",
 197                     port_id, reason);
 198        return NULL;
 199}
 200
 201static int ft_sess_alloc_cb(struct se_portal_group *se_tpg,
 202                            struct se_session *se_sess, void *p)
 203{
 204        struct ft_sess *sess = p;
 205        struct ft_tport *tport = sess->tport;
 206        struct hlist_head *head = &tport->hash[ft_sess_hash(sess->port_id)];
 207
 208        TFC_SESS_DBG(tport->lport, "port_id %x sess %p\n", sess->port_id, sess);
 209        hlist_add_head_rcu(&sess->hash, head);
 210        tport->sess_count++;
 211
 212        return 0;
 213}
 214
 215/*
 216 * Allocate session and enter it in the hash for the local port.
 217 * Caller holds ft_lport_lock.
 218 */
 219static struct ft_sess *ft_sess_create(struct ft_tport *tport, u32 port_id,
 220                                      struct fc_rport_priv *rdata)
 221{
 222        struct se_portal_group *se_tpg = &tport->tpg->se_tpg;
 223        struct ft_sess *sess;
 224        struct hlist_head *head;
 225        unsigned char initiatorname[TRANSPORT_IQN_LEN];
 226
 227        ft_format_wwn(&initiatorname[0], TRANSPORT_IQN_LEN, rdata->ids.port_name);
 228
 229        head = &tport->hash[ft_sess_hash(port_id)];
 230        hlist_for_each_entry_rcu(sess, head, hash)
 231                if (sess->port_id == port_id)
 232                        return sess;
 233
 234        sess = kzalloc(sizeof(*sess), GFP_KERNEL);
 235        if (!sess)
 236                return ERR_PTR(-ENOMEM);
 237
 238        kref_init(&sess->kref); /* ref for table entry */
 239        sess->tport = tport;
 240        sess->port_id = port_id;
 241
 242        sess->se_sess = target_alloc_session(se_tpg, TCM_FC_DEFAULT_TAGS,
 243                                             sizeof(struct ft_cmd),
 244                                             TARGET_PROT_NORMAL, &initiatorname[0],
 245                                             sess, ft_sess_alloc_cb);
 246        if (IS_ERR(sess->se_sess)) {
 247                int rc = PTR_ERR(sess->se_sess);
 248                kfree(sess);
 249                sess = ERR_PTR(rc);
 250        }
 251        return sess;
 252}
 253
 254/*
 255 * Unhash the session.
 256 * Caller holds ft_lport_lock.
 257 */
 258static void ft_sess_unhash(struct ft_sess *sess)
 259{
 260        struct ft_tport *tport = sess->tport;
 261
 262        hlist_del_rcu(&sess->hash);
 263        BUG_ON(!tport->sess_count);
 264        tport->sess_count--;
 265        sess->port_id = -1;
 266        sess->params = 0;
 267}
 268
 269/*
 270 * Delete session from hash.
 271 * Caller holds ft_lport_lock.
 272 */
 273static struct ft_sess *ft_sess_delete(struct ft_tport *tport, u32 port_id)
 274{
 275        struct hlist_head *head;
 276        struct ft_sess *sess;
 277
 278        head = &tport->hash[ft_sess_hash(port_id)];
 279        hlist_for_each_entry_rcu(sess, head, hash) {
 280                if (sess->port_id == port_id) {
 281                        ft_sess_unhash(sess);
 282                        return sess;
 283                }
 284        }
 285        return NULL;
 286}
 287
 288static void ft_close_sess(struct ft_sess *sess)
 289{
 290        transport_deregister_session_configfs(sess->se_sess);
 291        target_sess_cmd_list_set_waiting(sess->se_sess);
 292        target_wait_for_sess_cmds(sess->se_sess);
 293        ft_sess_put(sess);
 294}
 295
 296/*
 297 * Delete all sessions from tport.
 298 * Caller holds ft_lport_lock.
 299 */
 300static void ft_sess_delete_all(struct ft_tport *tport)
 301{
 302        struct hlist_head *head;
 303        struct ft_sess *sess;
 304
 305        for (head = tport->hash;
 306             head < &tport->hash[FT_SESS_HASH_SIZE]; head++) {
 307                hlist_for_each_entry_rcu(sess, head, hash) {
 308                        ft_sess_unhash(sess);
 309                        ft_close_sess(sess);    /* release from table */
 310                }
 311        }
 312}
 313
 314/*
 315 * TCM ops for sessions.
 316 */
 317
 318/*
 319 * Remove session and send PRLO.
 320 * This is called when the ACL is being deleted or queue depth is changing.
 321 */
 322void ft_sess_close(struct se_session *se_sess)
 323{
 324        struct ft_sess *sess = se_sess->fabric_sess_ptr;
 325        u32 port_id;
 326
 327        mutex_lock(&ft_lport_lock);
 328        port_id = sess->port_id;
 329        if (port_id == -1) {
 330                mutex_unlock(&ft_lport_lock);
 331                return;
 332        }
 333        TFC_SESS_DBG(sess->tport->lport, "port_id %x close session\n", port_id);
 334        ft_sess_unhash(sess);
 335        mutex_unlock(&ft_lport_lock);
 336        ft_close_sess(sess);
 337        /* XXX Send LOGO or PRLO */
 338        synchronize_rcu();              /* let transport deregister happen */
 339}
 340
 341u32 ft_sess_get_index(struct se_session *se_sess)
 342{
 343        struct ft_sess *sess = se_sess->fabric_sess_ptr;
 344
 345        return sess->port_id;   /* XXX TBD probably not what is needed */
 346}
 347
 348u32 ft_sess_get_port_name(struct se_session *se_sess,
 349                          unsigned char *buf, u32 len)
 350{
 351        struct ft_sess *sess = se_sess->fabric_sess_ptr;
 352
 353        return ft_format_wwn(buf, len, sess->port_name);
 354}
 355
 356/*
 357 * libfc ops involving sessions.
 358 */
 359
 360static int ft_prli_locked(struct fc_rport_priv *rdata, u32 spp_len,
 361                          const struct fc_els_spp *rspp, struct fc_els_spp *spp)
 362{
 363        struct ft_tport *tport;
 364        struct ft_sess *sess;
 365        u32 fcp_parm;
 366
 367        tport = ft_tport_get(rdata->local_port);
 368        if (!tport)
 369                goto not_target;        /* not a target for this local port */
 370
 371        if (!rspp)
 372                goto fill;
 373
 374        if (rspp->spp_flags & (FC_SPP_OPA_VAL | FC_SPP_RPA_VAL))
 375                return FC_SPP_RESP_NO_PA;
 376
 377        /*
 378         * If both target and initiator bits are off, the SPP is invalid.
 379         */
 380        fcp_parm = ntohl(rspp->spp_params);
 381        if (!(fcp_parm & (FCP_SPPF_INIT_FCN | FCP_SPPF_TARG_FCN)))
 382                return FC_SPP_RESP_INVL;
 383
 384        /*
 385         * Create session (image pair) only if requested by
 386         * EST_IMG_PAIR flag and if the requestor is an initiator.
 387         */
 388        if (rspp->spp_flags & FC_SPP_EST_IMG_PAIR) {
 389                spp->spp_flags |= FC_SPP_EST_IMG_PAIR;
 390                if (!(fcp_parm & FCP_SPPF_INIT_FCN))
 391                        return FC_SPP_RESP_CONF;
 392                sess = ft_sess_create(tport, rdata->ids.port_id, rdata);
 393                if (IS_ERR(sess)) {
 394                        if (PTR_ERR(sess) == -EACCES) {
 395                                spp->spp_flags &= ~FC_SPP_EST_IMG_PAIR;
 396                                return FC_SPP_RESP_CONF;
 397                        } else
 398                                return FC_SPP_RESP_RES;
 399                }
 400                if (!sess->params)
 401                        rdata->prli_count++;
 402                sess->params = fcp_parm;
 403                sess->port_name = rdata->ids.port_name;
 404                sess->max_frame = rdata->maxframe_size;
 405
 406                /* XXX TBD - clearing actions.  unit attn, see 4.10 */
 407        }
 408
 409        /*
 410         * OR in our service parameters with other provider (initiator), if any.
 411         */
 412fill:
 413        fcp_parm = ntohl(spp->spp_params);
 414        fcp_parm &= ~FCP_SPPF_RETRY;
 415        spp->spp_params = htonl(fcp_parm | FCP_SPPF_TARG_FCN);
 416        return FC_SPP_RESP_ACK;
 417
 418not_target:
 419        fcp_parm = ntohl(spp->spp_params);
 420        fcp_parm &= ~FCP_SPPF_TARG_FCN;
 421        spp->spp_params = htonl(fcp_parm);
 422        return 0;
 423}
 424
 425/**
 426 * tcm_fcp_prli() - Handle incoming or outgoing PRLI for the FCP target
 427 * @rdata: remote port private
 428 * @spp_len: service parameter page length
 429 * @rspp: received service parameter page (NULL for outgoing PRLI)
 430 * @spp: response service parameter page
 431 *
 432 * Returns spp response code.
 433 */
 434static int ft_prli(struct fc_rport_priv *rdata, u32 spp_len,
 435                   const struct fc_els_spp *rspp, struct fc_els_spp *spp)
 436{
 437        int ret;
 438
 439        mutex_lock(&ft_lport_lock);
 440        ret = ft_prli_locked(rdata, spp_len, rspp, spp);
 441        mutex_unlock(&ft_lport_lock);
 442        TFC_SESS_DBG(rdata->local_port, "port_id %x flags %x ret %x\n",
 443                     rdata->ids.port_id, rspp ? rspp->spp_flags : 0, ret);
 444        return ret;
 445}
 446
 447static void ft_sess_free(struct kref *kref)
 448{
 449        struct ft_sess *sess = container_of(kref, struct ft_sess, kref);
 450
 451        transport_deregister_session(sess->se_sess);
 452        kfree_rcu(sess, rcu);
 453}
 454
 455void ft_sess_put(struct ft_sess *sess)
 456{
 457        int sess_held = kref_read(&sess->kref);
 458
 459        BUG_ON(!sess_held);
 460        kref_put(&sess->kref, ft_sess_free);
 461}
 462
 463static void ft_prlo(struct fc_rport_priv *rdata)
 464{
 465        struct ft_sess *sess;
 466        struct ft_tport *tport;
 467
 468        mutex_lock(&ft_lport_lock);
 469        tport = rcu_dereference_protected(rdata->local_port->prov[FC_TYPE_FCP],
 470                                          lockdep_is_held(&ft_lport_lock));
 471
 472        if (!tport) {
 473                mutex_unlock(&ft_lport_lock);
 474                return;
 475        }
 476        sess = ft_sess_delete(tport, rdata->ids.port_id);
 477        if (!sess) {
 478                mutex_unlock(&ft_lport_lock);
 479                return;
 480        }
 481        mutex_unlock(&ft_lport_lock);
 482        ft_close_sess(sess);            /* release from table */
 483        rdata->prli_count--;
 484        /* XXX TBD - clearing actions.  unit attn, see 4.10 */
 485}
 486
 487/*
 488 * Handle incoming FCP request.
 489 * Caller has verified that the frame is type FCP.
 490 */
 491static void ft_recv(struct fc_lport *lport, struct fc_frame *fp)
 492{
 493        struct ft_sess *sess;
 494        u32 sid = fc_frame_sid(fp);
 495
 496        TFC_SESS_DBG(lport, "recv sid %x\n", sid);
 497
 498        sess = ft_sess_get(lport, sid);
 499        if (!sess) {
 500                TFC_SESS_DBG(lport, "sid %x sess lookup failed\n", sid);
 501                /* TBD XXX - if FCP_CMND, send PRLO */
 502                fc_frame_free(fp);
 503                return;
 504        }
 505        ft_recv_req(sess, fp);  /* must do ft_sess_put() */
 506}
 507
 508/*
 509 * Provider ops for libfc.
 510 */
 511struct fc4_prov ft_prov = {
 512        .prli = ft_prli,
 513        .prlo = ft_prlo,
 514        .recv = ft_recv,
 515        .module = THIS_MODULE,
 516};
 517