/*-1* Common functions for SCSI Interface Modules (SIMs).2*3* SPDX-License-Identifier: BSD-2-Clause4*5* Copyright (c) 1997 Justin T. Gibbs.6* All rights reserved.7*8* Redistribution and use in source and binary forms, with or without9* modification, are permitted provided that the following conditions10* are met:11* 1. Redistributions of source code must retain the above copyright12* notice, this list of conditions, and the following disclaimer,13* without modification, immediately at the beginning of the file.14* 2. The name of the author may not be used to endorse or promote products15* derived from this software without specific prior written permission.16*17* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND18* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE19* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE20* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR21* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL22* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS23* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)24* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT25* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY26* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF27* SUCH DAMAGE.28*/2930#include <sys/param.h>31#include <sys/bus.h>32#include <sys/kernel.h>33#include <sys/lock.h>34#include <sys/malloc.h>35#include <sys/mutex.h>3637#include <cam/cam.h>38#include <cam/cam_ccb.h>39#include <cam/cam_queue.h>40#include <cam/cam_sim.h>41#include <cam/cam_xpt.h>4243#define CAM_PATH_ANY (uint32_t)-14445static MALLOC_DEFINE(M_CAMSIM, "CAM SIM", "CAM SIM buffers");4647static struct mtx cam_sim_free_mtx;48MTX_SYSINIT(cam_sim_free_init, &cam_sim_free_mtx, "CAM SIM free lock", MTX_DEF);4950struct cam_devq *51cam_simq_alloc(uint32_t max_sim_transactions)52{53return (cam_devq_alloc(/*size*/0, max_sim_transactions));54}5556void57cam_simq_free(struct cam_devq *devq)58{59cam_devq_free(devq);60}61626364/**65* @brief allocate a new sim and fill in the details66*67* A Storage Interface Module (SIM) is the interface between CAM and68* hardware. SIM receives CCBs from CAM via @p sim_action callback and69* translates them into DMA or other hardware transactions. During system70* dumps, it can be polled with the @p sim_poll callback. CCB processing is71* terminated by calling @c xpt_done().72*73* The @p mtx acts as a perimeter lock for the SIM. All calls into the SIM's74* @p sim_action are made with this lock held. It is also used to hold/release75* a SIM, managing its reference count. When the lock is NULL, the SIM is 100%76* responsible for locking (and the reference counting is done with a shared77* lock.78*79* The cam_devq passed in (@c queue) is used to arbitrate the number of80* outstanding transactions to the SIM. For HBAs that have global limits shared81* between the different buses, the same devq should be specified for each bus82* attached to the SIM.83*84* @param sim_action Function to call to process CCBs85* @param sim_poll Function to poll the hardware for completions86* @param sim_name Name of SIM class87* @param softc Software context associated with the SIM88* @param unit Unit number of SIM89* @param mtx Mutex to lock while interacting with the SIM, or NULL90* for a SIM that handle its own locking to enable multi91* queue support.92* @param max_dev_transactions Maximum number of concurrent untagged93* transactions possible94* @param max_tagged_dev_transactions Maximum number of concurrent tagged95* transactions possible.96* @param queue The cam_devq to use for this SIM.97*/98struct cam_sim *99cam_sim_alloc(sim_action_func sim_action, sim_poll_func sim_poll,100const char *sim_name, void *softc, uint32_t unit,101struct mtx *mtx, int max_dev_transactions,102int max_tagged_dev_transactions, struct cam_devq *queue)103{104struct cam_sim *sim;105106sim = malloc(sizeof(struct cam_sim), M_CAMSIM, M_ZERO | M_NOWAIT);107if (sim == NULL)108return (NULL);109110sim->sim_action = sim_action;111sim->sim_poll = sim_poll;112sim->sim_name = sim_name;113sim->softc = softc;114sim->path_id = CAM_PATH_ANY;115sim->unit_number = unit;116sim->bus_id = 0; /* set in xpt_bus_register */117sim->max_tagged_dev_openings = max_tagged_dev_transactions;118sim->max_dev_openings = max_dev_transactions;119sim->flags = 0;120sim->refcount = 1;121sim->devq = queue;122sim->mtx = mtx;123return (sim);124}125126/**127* @brief frees up the sim128*129* Frees up the CAM @c sim and optionally the devq. If a mutex is associated130* with the sim, it must be locked on entry. It will remain locked on131* return.132*133* This function will wait for all outstanding reference to the sim to clear134* before returning.135*136* @param sim The sim to free137* @param free_devq Free the devq associated with the sim at creation.138*/139void140cam_sim_free(struct cam_sim *sim, int free_devq)141{142struct mtx *mtx;143int error __diagused;144145if (sim->mtx == NULL) {146mtx = &cam_sim_free_mtx;147mtx_lock(mtx);148} else {149mtx = sim->mtx;150mtx_assert(mtx, MA_OWNED);151}152KASSERT(sim->refcount >= 1, ("sim->refcount >= 1"));153sim->refcount--;154if (sim->refcount > 0) {155error = msleep(sim, mtx, PRIBIO, "simfree", 0);156KASSERT(error == 0, ("invalid error value for msleep(9)"));157}158KASSERT(sim->refcount == 0, ("sim->refcount == 0"));159if (mtx == &cam_sim_free_mtx) /* sim->mtx == NULL */160mtx_unlock(mtx);161162if (free_devq)163cam_simq_free(sim->devq);164free(sim, M_CAMSIM);165}166167void168cam_sim_release(struct cam_sim *sim)169{170struct mtx *mtx;171172if (sim->mtx == NULL)173mtx = &cam_sim_free_mtx;174else if (!mtx_owned(sim->mtx))175mtx = sim->mtx;176else177mtx = NULL; /* We hold the lock. */178if (mtx)179mtx_lock(mtx);180KASSERT(sim->refcount >= 1, ("sim->refcount >= 1"));181sim->refcount--;182if (sim->refcount == 0)183wakeup(sim);184if (mtx)185mtx_unlock(mtx);186}187188void189cam_sim_hold(struct cam_sim *sim)190{191struct mtx *mtx;192193if (sim->mtx == NULL)194mtx = &cam_sim_free_mtx;195else if (!mtx_owned(sim->mtx))196mtx = sim->mtx;197else198mtx = NULL; /* We hold the lock. */199if (mtx)200mtx_lock(mtx);201KASSERT(sim->refcount >= 1, ("sim->refcount >= 1"));202sim->refcount++;203if (mtx)204mtx_unlock(mtx);205}206207void208cam_sim_set_path(struct cam_sim *sim, uint32_t path_id)209{210sim->path_id = path_id;211}212213214