isc_cam.c revision 185289
1272578Sbrd/*-
2272578Sbrd * Copyright (c) 2005-2008 Daniel Braniss <danny@cs.huji.ac.il>
3272578Sbrd * All rights reserved.
4272578Sbrd *
5272578Sbrd * Redistribution and use in source and binary forms, with or without
6272578Sbrd * modification, are permitted provided that the following conditions
7272578Sbrd * are met:
8272578Sbrd * 1. Redistributions of source code must retain the above copyright
9272578Sbrd *    notice, this list of conditions and the following disclaimer.
10272578Sbrd * 2. Redistributions in binary form must reproduce the above copyright
11274727Sbrd *    notice, this list of conditions and the following disclaimer in the
12274727Sbrd *    documentation and/or other materials provided with the distribution.
13272578Sbrd *
14272578Sbrd * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15274727Sbrd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16272578Sbrd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17272578Sbrd * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18272578Sbrd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19272578Sbrd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20272578Sbrd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21272578Sbrd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22272578Sbrd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23272578Sbrd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24272578Sbrd * SUCH DAMAGE.
25274727Sbrd *
26274727Sbrd */
27274727Sbrd
28272578Sbrd#include <sys/cdefs.h>
29272578Sbrd__FBSDID("$FreeBSD: head/sys/dev/iscsi/initiator/isc_cam.c 185289 2008-11-25 07:17:11Z scottl $");
30273772Sbapt
31273772Sbapt#include "opt_iscsi_initiator.h"
32273772Sbapt
33273772Sbapt#include <sys/param.h>
34273772Sbapt#include <sys/kernel.h>
35273772Sbapt#include <sys/callout.h>
36274727Sbrd#if __FreeBSD_version >= 700000
37274727Sbrd#include <sys/lock.h>
38274727Sbrd#include <sys/mutex.h>
39273772Sbapt#endif
40272578Sbrd#include <sys/conf.h>
41273779Sbapt#include <sys/systm.h>
42273779Sbapt#include <sys/malloc.h>
43273779Sbapt#include <sys/mbuf.h>
44273779Sbapt#include <sys/uio.h>
45273779Sbapt#include <sys/sysctl.h>
46273779Sbapt
47273779Sbapt#include <cam/cam.h>
48274727Sbrd#include <cam/cam_ccb.h>
49274727Sbrd#include <cam/cam_sim.h>
50274727Sbrd#include <cam/cam_xpt_sim.h>
51274727Sbrd#include <cam/cam_periph.h>
52274727Sbrd
53274727Sbrd#include <dev/iscsi/initiator/iscsi.h>
54274727Sbrd#include <dev/iscsi/initiator/iscsivar.h>
55274727Sbrd
56274727Sbrd// XXX: untested/incomplete
57274727Sbrdvoid
58274727Sbrdic_freeze(isc_session_t *sp)
59273779Sbapt{
60273779Sbapt     debug_called(8);
61273791Sbapt#if 0
62273791Sbapt     sdebug(2, "freezing path=%p", sp->cam_path == NULL? 0: sp->cam_path);
63273791Sbapt     if((sp->cam_path != NULL) && !(sp->flags & ISC_FROZEN)) {
64273791Sbapt	  xpt_freeze_devq(sp->cam_path, 1);
65273791Sbapt     }
66273791Sbapt#endif
67273791Sbapt     sp->flags |= ISC_FROZEN;
68274727Sbrd}
69274727Sbrd
70273791Sbapt// XXX: untested/incomplete
71273791Sbaptvoid
72273791Sbaptic_release(isc_session_t *sp)
73273791Sbapt{
74285092Sbapt     debug_called(8);
75285092Sbapt#if 0
76285092Sbapt     sdebug(2, "release path=%p", sp->cam_path == NULL? 0: sp->cam_path);
77285092Sbapt     if((sp->cam_path != NULL) && (sp->flags & ISC_FROZEN)) {
78285092Sbapt	  xpt_release_devq(sp->cam_path, 1, TRUE);
79285092Sbapt     }
80285092Sbapt#endif
81285092Sbapt     sp->flags &= ~ISC_FROZEN;
82285092Sbapt}
83285092Sbapt
84287084Sbaptvoid
85287084Sbaptic_lost_target(isc_session_t *sp, int target)
86287084Sbapt{
87287084Sbapt     struct isc_softc   *isp = sp->isc;
88287084Sbapt
89287084Sbapt     debug_called(8);
90287084Sbapt     sdebug(2, "target=%d", target);
91287084Sbapt     if(sp->cam_path != NULL) {
92287084Sbapt	  mtx_lock(&isp->cam_mtx);
93287084Sbapt	  xpt_async(AC_LOST_DEVICE, sp->cam_path, NULL);
94287084Sbapt	  xpt_free_path(sp->cam_path);
95287084Sbapt	  mtx_unlock(&isp->cam_mtx);
96287084Sbapt	  sp->cam_path = 0; // XXX
97287084Sbapt     }
98287084Sbapt}
99287084Sbapt
100287084Sbaptstatic void
101287084Sbapt_scan_callback(struct cam_periph *periph, union ccb *ccb)
102287084Sbapt{
103287084Sbapt     isc_session_t *sp = (isc_session_t *)ccb->ccb_h.spriv_ptr0;
104287084Sbapt
105287084Sbapt     debug_called(8);
106287084Sbapt
107287084Sbapt     free(ccb, M_TEMP);
108287084Sbapt
109287084Sbapt     if(sp->flags & ISC_FFPWAIT) {
110272578Sbrd	  sp->flags &= ~ISC_FFPWAIT;
111272578Sbrd	  wakeup(sp);
112272578Sbrd     }
113273772Sbapt}
114273779Sbapt
115273791Sbaptstatic void
116285092Sbapt_scan_target(isc_session_t *sp, int target)
117287084Sbapt{
118272578Sbrd     union ccb		*ccb;
119
120     debug_called(8);
121     sdebug(2, "target=%d", target);
122
123     if((ccb = malloc(sizeof(union ccb), M_TEMP, M_WAITOK | M_ZERO)) == NULL) {
124	  xdebug("scan failed (can't allocate CCB)");
125	  return;
126     }
127     CAM_LOCK(sp->isc);
128     xpt_setup_ccb(&ccb->ccb_h, sp->cam_path, 5/*priority (low)*/);
129     ccb->ccb_h.func_code	= XPT_SCAN_BUS;
130     ccb->ccb_h.cbfcnp		= _scan_callback;
131     ccb->crcn.flags		= CAM_FLAG_NONE;
132     ccb->ccb_h.spriv_ptr0	= sp;
133
134     xpt_action(ccb);
135     CAM_UNLOCK(sp->isc);
136}
137
138int
139ic_fullfeature(struct cdev *dev)
140{
141     struct isc_softc 	*isp = dev->si_drv1;
142     isc_session_t	*sp = (isc_session_t *)dev->si_drv2;
143
144     debug_called(8);
145     sdebug(3, "dev=%d sc=%p", dev2unit(dev), isp);
146
147     sp->flags &= ~ISC_FFPHASE;
148     sp->flags |= ISC_FFPWAIT;
149
150     CAM_LOCK(isp);
151     if(xpt_create_path(&sp->cam_path, xpt_periph, cam_sim_path(sp->isc->cam_sim),
152			sp->sid, CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
153	  xdebug("can't create cam path");
154	  CAM_UNLOCK(isp);
155	  return ENODEV; // XXX
156     }
157     CAM_UNLOCK(isp);
158
159     _scan_target(sp, sp->sid);
160
161     while(sp->flags & ISC_FFPWAIT)
162	  tsleep(sp, PRIBIO, "ffp", 5*hz); // the timeout time should
163					    // be configurable
164     if(sp->target_nluns > 0) {
165	  sp->flags |= ISC_FFPHASE;
166	  return 0;
167     }
168
169     return ENODEV;
170}
171
172static void
173_inq(struct cam_sim *sim, union ccb *ccb, int maxluns)
174{
175     struct ccb_pathinq *cpi = &ccb->cpi;
176
177     debug_called(4);
178
179     cpi->version_num = 1; /* XXX??? */
180     cpi->hba_inquiry = PI_SDTR_ABLE | PI_TAG_ABLE | PI_WIDE_32;
181     cpi->target_sprt = 0;
182     cpi->hba_misc = 0;
183     cpi->hba_eng_cnt = 0;
184     cpi->max_target = ISCSI_MAX_TARGETS - 1;
185     cpi->initiator_id = ISCSI_MAX_TARGETS;
186     cpi->max_lun = maxluns;
187     cpi->bus_id = cam_sim_bus(sim);
188     cpi->base_transfer_speed = 3300;
189     strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN);
190     strncpy(cpi->hba_vid, "iSCSI", HBA_IDLEN);
191     strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN);
192     cpi->unit_number = cam_sim_unit(sim);
193     cpi->ccb_h.status = CAM_REQ_CMP;
194}
195
196static __inline int
197_scsi_encap(struct cam_sim *sim, union ccb *ccb)
198{
199     int		ret;
200
201#if __FreeBSD_version < 700000
202     ret = scsi_encap(sim, ccb);
203#else
204     struct isc_softc	*isp = (struct isc_softc *)cam_sim_softc(sim);
205
206     mtx_unlock(&isp->cam_mtx);
207     ret = scsi_encap(sim, ccb);
208     mtx_lock(&isp->cam_mtx);
209#endif
210     return ret;
211}
212
213static void
214ic_action(struct cam_sim *sim, union ccb *ccb)
215{
216     struct ccb_hdr	*ccb_h = &ccb->ccb_h;
217     struct isc_softc	*isp = (struct isc_softc *)cam_sim_softc(sim);
218     isc_session_t	*sp;
219
220     debug_called(8);
221
222     if((ccb_h->target_id != CAM_TARGET_WILDCARD) && (ccb_h->target_id < MAX_SESSIONS))
223	  sp = isp->sessions[ccb_h->target_id];
224     else
225	  sp = NULL;
226
227     ccb_h->spriv_ptr0 = sp;
228
229     debug(4, "func_code=0x%x flags=0x%x status=0x%x target=%d lun=%d retry_count=%d timeout=%d",
230	   ccb_h->func_code, ccb->ccb_h.flags, ccb->ccb_h.status,
231	   ccb->ccb_h.target_id, ccb->ccb_h.target_lun,
232	   ccb->ccb_h.retry_count, ccb_h->timeout);
233     /*
234      | first quick check
235      */
236     switch(ccb_h->func_code) {
237     default:
238	  // XXX: maybe check something else?
239	  break;
240
241     case XPT_SCSI_IO:
242     case XPT_RESET_DEV:
243     case XPT_GET_TRAN_SETTINGS:
244     case XPT_SET_TRAN_SETTINGS:
245     case XPT_CALC_GEOMETRY:
246	  if(sp == NULL) {
247	       ccb->ccb_h.status = CAM_DEV_NOT_THERE;
248#if __FreeBSD_version < 700000
249	       XPT_DONE(isp, ccb);
250#else
251	       xpt_done(ccb);
252#endif
253	       return;
254	  }
255	  break;
256
257     case XPT_PATH_INQ:
258     case XPT_NOOP:
259	  if(sp == NULL && ccb->ccb_h.target_id != CAM_TARGET_WILDCARD) {
260	       ccb->ccb_h.status = CAM_DEV_NOT_THERE;
261#if __FreeBSD_version < 700000
262	       XPT_DONE(isp, ccb);
263#else
264	       xpt_done(ccb);
265#endif
266	       debug(4, "status = CAM_DEV_NOT_THERE");
267	       return;
268	  }
269     }
270
271     switch(ccb_h->func_code) {
272
273     case XPT_PATH_INQ:
274	  _inq(sim, ccb, (sp? sp->opt.maxluns: ISCSI_MAX_LUNS) - 1);
275	  break;
276
277     case XPT_RESET_BUS: // (can just be a stub that does nothing and completes)
278     {
279	  struct ccb_pathinq *cpi = &ccb->cpi;
280
281	  debug(3, "XPT_RESET_BUS");
282	  cpi->ccb_h.status = CAM_REQ_CMP;
283	  break;
284     }
285
286     case XPT_SCSI_IO:
287     {
288	  struct ccb_scsiio* csio = &ccb->csio;
289
290	  debug(4, "XPT_SCSI_IO cmd=0x%x", csio->cdb_io.cdb_bytes[0]);
291	  if(sp == NULL) {
292	       ccb_h->status = CAM_REQ_INVALID; //CAM_NO_NEXUS;
293	       debug(4, "xpt_done.status=%d", ccb_h->status);
294	       break;
295	  }
296	  if(ccb_h->target_lun == CAM_LUN_WILDCARD) {
297	       debug(3, "target=%d: bad lun (-1)", ccb_h->target_id);
298	       ccb_h->status = CAM_LUN_INVALID;
299	       break;
300	  }
301	  if(_scsi_encap(sim, ccb) != 0)
302	       return;
303	  break;
304     }
305
306     case XPT_CALC_GEOMETRY:
307     {
308	  struct	ccb_calc_geometry *ccg;
309
310	  ccg = &ccb->ccg;
311	  debug(6, "XPT_CALC_GEOMETRY vsize=%jd bsize=%d", ccg->volume_size, ccg->block_size);
312	  if(ccg->block_size == 0 ||
313	     (ccg->volume_size < ccg->block_size)) {
314	       // print error message  ...
315	       /* XXX: what error is appropiate? */
316	       break;
317	  } else
318	       cam_calc_geometry(ccg, /*extended*/1);
319	  break;
320     }
321
322     case XPT_GET_TRAN_SETTINGS:
323     default:
324	  ccb_h->status = CAM_REQ_INVALID;
325	  break;
326     }
327#if __FreeBSD_version < 700000
328     XPT_DONE(isp, ccb);
329#else
330     xpt_done(ccb);
331#endif
332     return;
333}
334
335static void
336ic_poll(struct cam_sim *sim)
337{
338     debug_called(8);
339
340}
341
342int
343ic_getCamVals(isc_session_t *sp, iscsi_cam_t *cp)
344{
345     int	i;
346
347     debug_called(8);
348
349     if(sp && sp->isc->cam_sim) {
350	  cp->path_id = cam_sim_path(sp->isc->cam_sim);
351	  cp->target_id = sp->sid;
352	  cp->target_nluns = sp->target_nluns; // XXX: -1?
353	  for(i = 0; i < cp->target_nluns; i++)
354	       cp->target_lun[i] = sp->target_lun[i];
355	  return 0;
356     }
357     return ENXIO;
358}
359
360void
361ic_destroy(struct isc_softc *isp)
362{
363     debug_called(8);
364
365     CAM_LOCK(isp); // can't harm :-)
366
367     xpt_async(AC_LOST_DEVICE, isp->cam_path, NULL);
368     xpt_free_path(isp->cam_path);
369
370     xpt_bus_deregister(cam_sim_path(isp->cam_sim));
371     cam_sim_free(isp->cam_sim, TRUE /*free_devq*/);
372
373     CAM_UNLOCK(isp);
374}
375
376int
377ic_init(struct isc_softc *isp)
378{
379     struct cam_sim	*sim;
380     struct cam_devq	*devq;
381     struct cam_path	*path;
382
383     if((devq = cam_simq_alloc(256)) == NULL)
384	  return ENOMEM;
385
386#if __FreeBSD_version >= 700000
387     mtx_init(&isp->cam_mtx, "isc-cam", NULL, MTX_DEF);
388#else
389     isp->cam_mtx = Giant;
390#endif
391     sim = cam_sim_alloc(ic_action, ic_poll,
392			 "iscsi", isp, 0/*unit*/,
393#if __FreeBSD_version >= 700000
394			 &isp->cam_mtx,
395#endif
396			 1/*max_dev_transactions*/,
397			 100/*max_tagged_dev_transactions*/,
398			 devq);
399     if(sim == NULL) {
400	  cam_simq_free(devq);
401#if __FreeBSD_version >= 700000
402	  mtx_destroy(&isp->cam_mtx);
403#endif
404	  return ENXIO;
405     }
406     CAM_LOCK(isp);
407     if(xpt_bus_register(sim,
408#if __FreeBSD_version >= 700000
409			 NULL,
410#endif
411			 0/*bus_number*/) != CAM_SUCCESS)
412	  goto bad;
413
414     if(xpt_create_path(&path, xpt_periph, cam_sim_path(sim),
415			CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
416	  xpt_bus_deregister(cam_sim_path(sim));
417	  goto bad;
418     }
419
420     CAM_UNLOCK(isp);
421
422     isp->cam_sim = sim;
423     isp->cam_path = path;
424
425     debug(2, "cam subsystem initialized"); // XXX: add dev ...
426     debug(4, "sim=%p path=%p", sim, path);
427     return 0;
428
429 bad:
430     cam_sim_free(sim, /*free_devq*/TRUE);
431     CAM_UNLOCK(isp);
432#if __FreeBSD_version >= 700000
433     mtx_destroy(&isp->cam_mtx);
434#endif
435     return ENXIO;
436}
437