Emulex Logo
OneCoreā„¢ Storage SDK Release 11.2
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
ocs_ramdisc.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2011-2015, Emulex
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice,
9  * this list of conditions and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright notice,
12  * this list of conditions and the following disclaimer in the documentation
13  * and/or other materials provided with the distribution.
14  *
15  * 3. Neither the name of the copyright holder nor the names of its contributors
16  * may be used to endorse or promote products derived from this software
17  * without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  *
31  */
32 
33 /**
34  * @file
35  *
36  */
37 
38 /**
39  * @defgroup ramdisc RAMdisc
40  *
41  */
42 
43 #include "ocs.h"
44 #include "ocs_ramdisc.h"
45 #include "scsi_asc_ascq.h"
46 #include "ocs_dif.h"
47 
48 /* Set to 1 to format the disk with a pattern */
49 #define ENABLE_SEED_WITH_PATTERN 0
50 
51 #if ENABLE_DIF_VERIFICATION == 1 || ENABLE_SEED_WITH_PATTERN == 1
52 #include "ocs_dif.h"
53 #endif
54 
55 #if defined(OCS_INCLUDE_DEBUG)
56 #include "ocs_debug.h"
57 #endif
58 
59 /**
60  * @ingroup ramdisc
61  * @brief Create a new ramdisc instance (LUN).
62  *
63  * @param ocs Pointer to the ocs structure.
64  * @param ramdisc Pointer to the ramdisc instance to initialize.
65  * @param block_size Ramdisc block size.
66  * @param blocks_per_segment Ramdisc blocks per storage segment.
67  * @param block_count Ramdisc total device block count.
68  *
69  * @return Returns 0 on success, or a negative error code value on failure.
70  */
71 
72 int32_t ocs_new_ramdisc(ocs_t *ocs, ocs_ramdisc_t *ramdisc, uint32_t block_size,
73  uint32_t blocks_per_segment, uint64_t block_count)
74 {
75  ramdisc_segment_t *seg;
76  uint32_t i;
77  int32_t rc;
78 
79  ocs_assert(ocs, -1);
80  ocs_assert(ramdisc, -1);
81  ocs_assert(blocks_per_segment, -1);
82 
83  ocs_memset(ramdisc, 0, sizeof(*ramdisc));
84  ramdisc->data_block_size = block_size;
85  ramdisc->phys_block_size = block_size;
86  ramdisc->segment_block_size = block_size;
87  ramdisc->blocks_per_segment = blocks_per_segment;
88  ramdisc->block_count = block_count;
89  ramdisc->pending_block_size = 0;
91  ramdisc->p_type = 0;
92  ramdisc->prot_en = 0;
93 
94  ramdisc->segment_size = blocks_per_segment * block_size;
95  ramdisc->segment_count = (block_count + blocks_per_segment - 1) / blocks_per_segment;
96 
97  ramdisc->segment_pool = ocs_pool_alloc(ocs, sizeof(ramdisc_segment_t), ramdisc->segment_count, FALSE);
98  if (ramdisc->segment_pool == NULL) {
99  ocs_log_err(ocs, "ocs_pool_alloc(%lu, %u) failed\n",
100  sizeof(ramdisc_segment_t), ramdisc->segment_count);
101  return -1;
102  }
103 
104 #if !defined(OCS_USPACE)
105  if (ocs->ocs_os.hw_cmpl_context != OCS_HW_CMPL_CONTEXT_THREAD)
106  ramdisc->segments = ocs_kmalloc(ocs, ramdisc->segment_count * sizeof(ramdisc_segment_t *), OCS_M_NOWAIT | OCS_M_ZERO);
107  else
108 #endif
109  ramdisc->segments = ocs_malloc(ocs, ramdisc->segment_count * sizeof(ramdisc_segment_t *), OCS_M_NOWAIT | OCS_M_ZERO);
110 
111  if (ramdisc->segments == NULL) {
112  ocs_log_err(ocs, "ocs_malloc(%u * %lu) segments failed\n",
113  ramdisc->segment_count, sizeof(ramdisc_segment_t *));
114  return -1;
115  }
116 
117  ocs_list_init(&ramdisc->dif_pool_list, dif_pool_t, link);
118 
119  for (i = 0; i < ramdisc->segment_count; i ++) {
120  ramdisc->segments[i] = ocs_pool_get(ramdisc->segment_pool);
121  seg = ramdisc->segments[i];
122  if (seg == NULL) {
123  ocs_log_err(ocs, "unable to get segment %u\n", i);
124  return -1;
125  }
126 
127  rc = ocs_dma_alloc(ocs, &seg->data_segment, ramdisc->segment_size, 4096);
128  if (rc) {
129  ocs_log_err(ocs, "ocs_dma_alloc failed creating segment %u\n", i);
130  ocs_del_ramdisc(ocs, ramdisc);
131  return -1;
132  }
133  }
134 
135  return 0;
136 }
137 
138 /**
139  * @ingroup ramdisc
140  * @brief Delete a ramdisc instance.
141  *
142  * @par Description
143  * The resources associated with a previously-initialized ramdisc instance are freed.
144  *
145  * @param ocs Pointer to the ocs structure.
146  * @param ramdisc Pointer to the ramdisc instance to free.
147  *
148  * @return Returns 0 on success, or a negative error code value on failure.
149  */
150 
151 int32_t ocs_del_ramdisc(ocs_t *ocs, ocs_ramdisc_t *ramdisc)
152 {
153  uint64_t i;
154  ramdisc_segment_t *seg;
155 
156  ocs_assert(ocs, -1);
157  ocs_assert(ramdisc, -1);
158  if (ramdisc) {
159  if (ramdisc->segments) {
160  for (i = 0; i < ramdisc->segment_count; i ++) {
161  seg = ramdisc->segments[i];
162  if (seg->data_segment.size != 0) {
163  ocs_dma_free(ocs, &seg->data_segment);
164  } else {
165  break;
166  }
167  if (seg->dif_segment.virt != NULL) {
168  dif_pool_free(ocs, &ramdisc->dif_pool_list);
169  }
170  }
171 
172 #if !defined(OCS_USPACE)
173  if (ocs->ocs_os.hw_cmpl_context != OCS_HW_CMPL_CONTEXT_THREAD)
174  ocs_kfree(ocs, ramdisc->segments, ramdisc->segment_count * sizeof(ramdisc_segment_t *));
175  else
176 #endif
177  ocs_free(ocs, ramdisc->segments, ramdisc->segment_count * sizeof(ramdisc_segment_t *));
178 
179  ramdisc->segments = NULL;
180  }
181 
182  if (ramdisc->segment_pool != NULL) {
183  ocs_pool_free(ramdisc->segment_pool);
184  ramdisc->segment_pool = NULL;
185  }
186  }
187  return 0;
188 }
189 
190 /**
191  * @ingroup ramdisc
192  * @brief Build an SGL list for blocks in a ramdisc.
193  *
194  * @par Description
195  * A starting block address and count are used to build a generic scatter gather list.
196  *
197  * @param ocs Pointer to the ocs structure.
198  * @param ramdisc Pointer to a ramdisc instance.
199  * @param sgl Pointer to the SGL.
200  * @param max_sgl Maximum number of scatter gather entries in an SGL.
201  * @param max_sge_size Maximum size of an SGE, in bytes.
202  * @param lba Starting block address.
203  * @param block_count The number of blocks to transfer for the IO.
204  * May be updated to contain the number of blocks in the SGLs,
205  * which may be less if the @c max_sgl count or @c max_xfer_size is reached.
206  * @param req_flags SGL flags indicating the type of SGL to be built.
207  * If the SGL_REQ_FLAGS_PHYS (1U << 0) bit is set, then the SGL includes
208  * the block data and DIF. Otherwise, the SGL builds a DIF or non-DIF SGL.<br>
209  * @b Note: In the case of a separate DIF, an SGL_REQ_FLAGS_PHYS results
210  * in a one block data SGE followed by an 8-byte SGE containing the DIF data, repeating.
211  *
212  * @param dump_buffer If TRUE and debug is enabled, the memory pointed to by
213  * the SGE is dumped to the log.
214  *
215  * @return Returns the number of SGLs, or a negative error value on failure.
216  */
217 
218 int32_t ocs_ramdisc_sgl(ocs_t *ocs, ocs_ramdisc_t *ramdisc, ocs_ramdisc_sgl_t *sgl,
219  uint32_t max_sgl, uint32_t max_sge_size,
220  uint32_t lba, uint32_t *block_count, uint32_t req_flags, uint8_t dump_buffer)
221 {
222  uint32_t s0; // index first segment in this SGL
223  uint32_t l0; // first lba of current segment
224  uint32_t lba_off; // lba offset into segment
225  uint32_t dif_off; // lba offset into segment
226  uint32_t off; // byte offset into segment
227  ramdisc_segment_t *seg;
228  uint32_t sgl_count = 0;
229  uint32_t blocks_remaining = *block_count;
230  uint32_t i;
231  uint32_t dif_separate = ramdisc->dif_separate && ramdisc->prot_en;
232  uint32_t multi_dif_separate = 1;
233 
234  ocs_assert(ocs, -1);
235  ocs_assert(ramdisc, -1);
236  ocs_assert(ramdisc->segments, -1);
237  ocs_assert(ramdisc->segment_count > 0, -1);
238  ocs_assert(sgl, -1);
239 
240  /* if this request falls off the end of the ramdisc, then return an error */
241  if ((lba + blocks_remaining) > ramdisc->block_count) {
242  return -1;
243  }
244 
245  s0 = lba / ramdisc->blocks_per_segment;
246  l0 = s0 * ramdisc->blocks_per_segment;
247  lba_off = lba - l0;
248  off = (lba_off * ramdisc->segment_block_size);
249  dif_off = (lba_off * DIF_SIZE);
250 
251  /* See if multiple DIF separate SGEs are supported by the calling adapter */
252  if (dif_separate) {
253  multi_dif_separate = ocs_scsi_get_property(ocs, OCS_SCSI_DIF_MULTI_SEPARATE);
254  }
255 
256  while ((blocks_remaining > 0) &&
257  (sgl_count < max_sgl)) {
258  uint32_t n;
259 
260  n = MIN(blocks_remaining, (ramdisc->blocks_per_segment - lba_off));
261  seg = ramdisc->segments[s0];
262 
263 #if defined(OCS_INCLUDE_DEBUG)
264  /* Dump the Data */
265  if (dump_buffer) {
266  uintptr_t phys = seg->data_segment.phys + off;
267  char desc[64];
268 
269  sprintf(desc, "Phy addr: 0x%x 0x%08x size: 0x%x",
270  ocs_addr32_hi(phys),
271  ocs_addr32_lo(phys),
272  n*ramdisc->segment_block_size);
273 
274  ocs_dump32(OCS_DEBUG_ALWAYS, ocs, desc,
275  (uint8_t*)seg->data_segment.virt + off, n*ramdisc->segment_block_size);
276 
277  /* dump the DIF separates */
278  if (dif_separate) {
279  phys = seg->dif_segment.phys + dif_off;
280 
281  sprintf(desc, "Dif Separate Phy addr: 0x%x 0x%08x size: 0x%x",
282  ocs_addr32_hi(phys),
283  ocs_addr32_lo(phys),
284  n * DIF_SIZE);
285 
286  ocs_dump32(OCS_DEBUG_ALWAYS, ocs, desc,
287  (uint8_t*)seg->dif_segment.virt + dif_off,
288  n * DIF_SIZE);
289  }
290  }
291 #endif
292  if (dif_separate && (req_flags & SGL_REQ_FLAGS_PHYS)) {
293  /* Limit to 1 block if dif mutiple separates is not supported */
294  if (!multi_dif_separate) {
295  n = 1;
296  }
297 
298  /* Physical SGL is requested, with DIF separate, we need to generate
299  * an sgl for the data block and an sgl for the DIF block
300  */
301  for (i = 0; i < n; i ++) {
302  if ((sgl_count + 1) < max_sgl) {
303  sgl[0].addr = seg->data_segment.phys + off;
304  sgl[0].dif_addr = 0;
305  sgl[0].len = ramdisc->segment_block_size;
306  sgl[1].addr = seg->dif_segment.phys + dif_off;
307  sgl[1].dif_addr = 0;
308  sgl[1].len = DIF_SIZE;
309  sgl_count += 2;
310  sgl += 2;
311  off += ramdisc->segment_block_size;
312  dif_off += DIF_SIZE;
313  }
314  }
315 
316  } else {
317  sgl->addr = seg->data_segment.phys + off;
318  sgl->len = n*ramdisc->segment_block_size;
319  if (dif_separate) {
320  sgl->dif_addr = seg->dif_segment.phys + dif_off;
321  } else {
322  sgl->dif_addr = 0;
323  }
324  sgl_count++;
325  sgl++;
326  s0++;
327  }
328 
329  lba_off = 0;
330  off = 0;
331  dif_off = 0;
332  blocks_remaining -= n;
333 
334  if (dif_separate && !multi_dif_separate) {
335  break;
336  }
337  }
338 
339  *block_count -= blocks_remaining;
340 
341  return sgl_count;
342 }
343 
344 
345 /**
346  * @ingroup ramdisc
347  * @brief Reformat an existing ramdisc, possibly the changing block size and/or protection.
348  *
349  * @param ocs Pointer to the ocs structure.
350  * @param ramdisc Pointer to the ramdisc instance.
351  * @param block_size Requested block size
352  * @param p_type Requested new protection type.
353  * @param prot_en Requested new protection enabled flag.
354  * @param dif_separate Requested DIF separates
355  *
356  * @return Returns a non-zero value if the format changed, or 0 if the format did not change.
357  */
358 
359 int32_t ocs_format_ramdisc(ocs_t *ocs, ocs_ramdisc_t *ramdisc, uint32_t block_size, uint8_t p_type, uint8_t prot_en, uint8_t dif_separate)
360 {
361  uint32_t i;
362  uint32_t j;
363  int format_changed = 0;
364  ramdisc_segment_t *seg;
365  uint16_t crc = 0;
366  uint32_t blkCount;
367 
368  if (block_size == 0) {
369  block_size = 512;
370  }
371 
372  if (ramdisc->force_dif_separate) {
373  dif_separate = TRUE;
374  }
375 
376  format_changed = (block_size != ramdisc->data_block_size) || (p_type != ramdisc->p_type) ||
377  (prot_en != ramdisc->prot_en);
378 
379  /*
380  * Segment size will stay the same
381  * Block size may change
382  * If the block size changes, there will be a different number of blocks in each segment.
383  * If the blocks per segment changes, the overall block count will change.
384  *
385  * If prot_en is true, then the actual block size needs to be 8 bytes bigger than the
386  * requested size of the block.
387  */
388 
389  ramdisc->p_type = p_type;
390  ramdisc->prot_en = prot_en;
391  ramdisc->dif_separate = dif_separate;
392 
393  ramdisc->data_block_size = block_size;
394  ramdisc->phys_block_size = block_size;
395  ramdisc->segment_block_size = block_size;
396 
397  if (prot_en) {
398  ramdisc->phys_block_size += DIF_SIZE;
399  if (!ramdisc->dif_separate) {
400  ramdisc->segment_block_size += DIF_SIZE;
401  }
402  }
403 
404  if (prot_en) {
405  block_size += DIF_SIZE;
406  }
407 
408  ramdisc->blocks_per_segment = ramdisc->segment_size / ramdisc->segment_block_size;
409  ramdisc->block_count = ramdisc->blocks_per_segment * ramdisc->segment_count;
410 
411  ocs_log_info(ocs, "Formating LUN %d with %" PRIu64 " blocks of %d/%d bytes, p_type is %d, prot_en is %d dif_separate %d\n",
412  ramdisc->lun, ramdisc->block_count, ramdisc->data_block_size, ramdisc->phys_block_size,
413  ramdisc->p_type, ramdisc->prot_en, ramdisc->dif_separate);
414 
415  /* Seed the blocks with zeroes or a pattern */
416  blkCount = 0;
417  for (i = 0; i < ramdisc->segment_count; i++) {
418  seg = ramdisc->segments[i];
419 #if ENABLE_SEED_WITH_PATTERN==1
420  uint32_t *blk_dword = seg->data_segment.virt;
421  uint32_t dwords_per_blk = ramdisc->data_block_size / sizeof(uint32_t);
422  /*
423  * Seed each block with the LBA in the first 4 bytes followed
424  * by a simple incrementing dword pattern.
425  */
426  for (j = 0; j < ramdisc->blocks_per_segment; j++) {
427  uint32_t k;
428 
429  blk_dword[0] = blkCount;
430  for (k = 1; k < dwords_per_blk - 1; k++) {
431  blk_dword[k] = blkCount + k;
432  }
433  blkCount++;
434  blk_dword += dwords_per_blk;
435  if (!ramdisc->dif_separate) {
436  /* skip over the inline DIF bytes */
437  blk_dword += 2;
438  }
439  }
440 #else
441  ocs_memset(seg->data_segment.virt, 0, ramdisc->segment_size);
442 #endif
443 
444  /* If DIF separates, then initialize separates */
445  if (ramdisc->dif_separate) {
446  /* If not previously allocated, then allocate DIF separate segment */
447  if (seg->dif_segment.virt == NULL) {
448  int32_t rc;
449 
450  rc = dif_pool_alloc(ocs, &ramdisc->dif_pool_list,
452  &seg->dif_segment);
453  if (rc) {
454  ocs_log_err(ocs, "dif_pool_alloc failed: %d\n", rc);
455  return -1;
456  }
457  }
458  ocs_memset(seg->dif_segment.virt, 0, seg->dif_segment.size);
459  }
460  }
461 
462 
463  if (ocs->ctrlmask & OCS_CTRLMASK_TGT_SET_DIF_REF_TAG_CRC) {
464  /* Calculate the CRC for a zeroed block */
465  crc = 0;
466  crc = ocs_scsi_dif_calc_crc(ramdisc->segments[0]->data_segment.virt,
467  ramdisc->segment_block_size,
468  crc);
469  }
470 
471  /* If protection is enabled initialize DIF block to FF's */
472  blkCount = 0;
473  if ((ramdisc->prot_en == 1) && (ramdisc->p_type != 0)) {
474  for (i = 0; i < ramdisc->segment_count; i++) {
475  uint8_t *dif_block;
476  uint32_t dif_incr;
477  seg = ramdisc->segments[i];
478  if (ramdisc->dif_separate) {
479  dif_block = seg->dif_segment.virt;
480  dif_incr = DIF_SIZE;
481  } else {
482  dif_block = ((uint8_t*)seg->data_segment.virt) + ramdisc->data_block_size;
483  dif_incr = ramdisc->segment_block_size;
484  }
485  for (j = 0; j < ramdisc->blocks_per_segment; j++) {
486  if (ocs->ctrlmask & OCS_CTRLMASK_TGT_SET_DIF_REF_TAG_CRC) {
487 #if ENABLE_SEED_WITH_PATTERN==1
488  uint8_t* blk_start;
489 
490  blk_start = ((uint8_t*)seg->data_segment.virt);
491  if (ramdisc->dif_separate) {
492  blk_start += (ramdisc->data_block_size * j);
493  } else {
494  blk_start += (ramdisc->phys_block_size * j);
495  }
496 
497  crc = 0;
498  crc = ocs_scsi_dif_calc_crc(blk_start,
499  ramdisc->data_block_size,
500  crc);
501 #endif
502  dif_block[0] = (crc >> 8);
503  dif_block[1] = (crc & 0xff);
504  dif_block[2] = 0xff;
505  dif_block[3] = 0xff;
506  dif_block[4] = blkCount >> 24;
507  dif_block[5] = (blkCount >> 16) & 0xff;
508  dif_block[6] = (blkCount >> 8) & 0xff;
509  dif_block[7] = blkCount & 0xff;
510  } else {
511  ocs_memset(dif_block, 0xff, DIF_SIZE);
512  }
513  dif_block += dif_incr;
514  blkCount++;
515  }
516  }
517 
518  }
519  return format_changed;
520 
521 }
522 
523 /**
524  * @ingroup ramdisc
525  * @brief Set the ramdisc block size.
526  *
527  * @par Description
528  * The block size is set for a ramdisc instance.
529  *
530  * @param ocs Pointer to the ocs structure.
531  * @param ramdisc Pointer to the ramdisc object to initialize.
532  * @param block_size Ramdisc device block size.
533  *
534  * @return None.
535  */
536 
537 void ocs_set_ramdisc_blocksize(ocs_t *ocs, ocs_ramdisc_t *ramdisc, uint32_t block_size)
538 {
539  ramdisc->pending_block_size = block_size;
540 }
541 
542 /**
543  * @ingroup ramdisc
544  * @brief Get the ramdisc block size.
545  *
546  * @par Description
547  * Return the block size for a ramdisc instance.
548  *
549  * @param ocs Pointer to the ocs structure.
550  * @param ramdisc Pointer to the ramdisc object.
551  *
552  * @return Returns the block size of the ramdisc.
553  */
554 
555 uint32_t ocs_get_ramdisc_blocksize(ocs_t *ocs, ocs_ramdisc_t *ramdisc)
556 {
557  ocs_assert(ocs, 0);
558  ocs_assert(ramdisc, 0);
559 
560  return ramdisc->segment_block_size;
561 }
562 
563 /**
564  * @ingroup ramdisc
565  * @brief Return the address of a ramdisc logical block.
566  *
567  * @par Description
568  * Given a pointer to a ramdisc and a logical block address, return
569  * the address in memory of the first byte of the block.
570  *
571  * @param ramdisc Pointer to the ramdisc structure.
572  * @param lba Logical Block Address.
573  *
574  * @return Returns the pointer to the first block of the LBA, or NULL if the LBA is invalid.
575  */
576 
577 uint8_t* ocs_ramdisc_lba_address(ocs_ramdisc_t *ramdisc, uint64_t lba)
578 {
579  ramdisc_segment_t *seg;
580 
581  if (lba >= ramdisc->block_count) {
582  return NULL;
583  }
584 
585  seg = ramdisc->segments[lba / ramdisc->blocks_per_segment];
586 
587  return ((uint8_t*)seg->data_segment.virt) + ((lba % ramdisc->blocks_per_segment) * ramdisc->segment_block_size);
588 
589 }
590 
591 /**
592  * @ingroup ramdisc
593  * @brief Return the address of the DIF block if protection is enabled.
594  *
595  * @par Description
596  * Given a pointer to a ramdisc and a logical block address, return
597  * the address in memory of the DIF block if protection is enabled.
598  *
599  * @param ramdisc Pointer to the ramdisc structure.
600  * @param lba Logical Block Address.
601  *
602  * @return Returns the pointer to the first block of the LBA, or NULL if the LBA is invalid.
603  */
604 
606 {
607  ramdisc_segment_t *seg;
608 
609  if (lba >= ramdisc->block_count) {
610  return NULL;
611  }
612 
613  seg = ramdisc->segments[lba / ramdisc->blocks_per_segment];
614 
615  if (ramdisc->dif_separate) {
616  return (dif_t*) (((uint8_t*)seg->dif_segment.virt) + (lba % ramdisc->blocks_per_segment)*DIF_SIZE);
617  } else {
618  return (dif_t*) (ocs_ramdisc_lba_address(ramdisc, lba) + ramdisc->segment_block_size - sizeof(dif_t));
619  }
620 }
621 
622 /**
623  * @brief initialize the DIF memory pool
624  *
625  * @par Description
626  * Clears all previous allocations of DIF memory. Leaves all the previously-allocated
627  * memory buffers in the pool.
628  *
629  * @param ocs Pointer to the ocs structure.
630  * @param head Head of the dif_pool_t linked list.
631  *
632  * @return None.
633  */
634 
635 void
636 dif_pool_init(ocs_t *ocs, ocs_list_t *head)
637 {
638  dif_pool_t *difpool;
639 
640  if (!ocs_list_empty(head)) {
641  ocs_list_foreach(head, difpool) {
642  difpool->inuse_count = 0;
643  }
644  }
645 }
646 
647 /**
648  * @brief Free the DIF memory pool.
649  *
650  * @par Description
651  * Frees all of the DIF memory pool buffers.
652  *
653  * @param ocs Pointer to the ocs structure.
654  * @param head Pointer to the head of DIF pool entries list.
655  *
656  * @return None.
657  */
658 
659 void
660 dif_pool_free(ocs_t *ocs, ocs_list_t *head)
661 {
662  dif_pool_t *difpool;
663  dif_pool_t *next;
664 
665  if (!ocs_list_empty(head)) {
666  ocs_list_foreach_safe(head, difpool, next) {
667  ocs_list_remove(head, difpool);
668 
669  ocs_dma_free(ocs, &difpool->dmabuf);
670  ocs_free(ocs, difpool, sizeof(*difpool));
671  }
672  }
673 }
674 
675 /**
676  * @brief Allocate a new dif_pool_t entry.
677  *
678  * @par Description
679  * Allocates a new dif_pool_t entry.
680  *
681  * @param ocs Pointer to the ocs_t structure.
682  * @param head Pointer to the head of DIF buffer entry linked list.
683  *
684  * @return Returns 0 on success, a negative error code value on failure.
685  */
686 
687 static int32_t
688 dif_pool_new(ocs_t *ocs, ocs_list_t *head)
689 {
690  dif_pool_t *difpool;
691  int32_t rc;
692 
693  difpool = ocs_malloc(ocs, sizeof(*difpool), OCS_M_NOWAIT | OCS_M_ZERO);
694  if (difpool == NULL) {
695  ocs_log_err(ocs, "ocs_malloc failed\n");
696  return -1;
697  }
698  rc = ocs_dma_alloc(ocs, &difpool->dmabuf, DIF_POOL_SIZE, OCS_MIN_DMA_ALIGNMENT);
699  if (rc) {
700  ocs_log_err(ocs, "ocs_dma_alloc() failed: %d\n", rc);
701  ocs_free(ocs, difpool, sizeof(*difpool));
702  return -1;
703  }
704  ocs_list_add_tail(head, difpool);
705 
706  return 0;
707 }
708 
709 /**
710  * @brief Allocate memory for the DIF memory pool.
711  *
712  * @par Description
713  * Allocates a buffer of @c size from a DIF memory pool. Returns a physical address.
714  *
715  * @param ocs Pointer to the ocs_t structure.
716  * @param head Pointer to the head of a DIF pool entry linked list.
717  * @param size Size of the memory to allocate.
718  * @param difdma Returns the address of the requested DIF segment.
719  *
720  * @return Returns 0 on success.
721  */
722 
723 int32_t
724 dif_pool_alloc(ocs_t *ocs, ocs_list_t *head, uint32_t size, ocs_difdma_t *difdma)
725 {
726  dif_pool_t *difpool;
727  int32_t rc = -1;
728 
729  /* Roundup request size to be a multiple of something nice */
730  size = ocs_roundup(size, 16);
731 
732  ocs_assert(size <= DIF_POOL_SIZE, -1);
733 
734  if (ocs_list_empty(head)) {
735  rc = dif_pool_new(ocs, head);
736  if (rc) {
737  return -1;
738  }
739  }
740 
741  difpool = ocs_list_get_tail(head);
742  if ((difpool->dmabuf.size - difpool->inuse_count) < size) {
743  rc = dif_pool_new(ocs, head);
744  if (rc) {
745  return -1;
746  }
747  difpool = ocs_list_get_tail(head);
748  }
749  ocs_assert((difpool->dmabuf.size - difpool->inuse_count) >= size, -1);
750  difdma->phys = difpool->dmabuf.phys + difpool->inuse_count;
751  difdma->virt = ((uint8_t*)difpool->dmabuf.virt) + difpool->inuse_count;
752  difdma->size = size;
753  difpool->inuse_count += size;
754 
755  return 0;
756 }
757 
758 void
760 {
761 #if defined(ENABLE_DDUMP_RAMDISC_SEGMENTS)
762  ramdisc_segment_t *seg;
763  uint64_t i;
764 #endif
765 
766  ocs_ddump_section(textbuf, "ramdisc", ramdisc->lun);
767  ocs_ddump_value(textbuf, "lun", "%d", ramdisc->lun);
768  ocs_ddump_value(textbuf, "data_block_size", "%d", ramdisc->data_block_size);
769  ocs_ddump_value(textbuf, "phys_block_size", "%d", ramdisc->phys_block_size);
770  ocs_ddump_value(textbuf, "segment_block_size", "%d", ramdisc->segment_block_size);
771  ocs_ddump_value(textbuf, "blocks_per_segment", "%d", ramdisc->blocks_per_segment);
772  ocs_ddump_value(textbuf, "block_count", "%" PRIu64, ramdisc->block_count);
773  ocs_ddump_value(textbuf, "pending_block_size", "%d", ramdisc->pending_block_size);
774  ocs_ddump_value(textbuf, "prot_capable", "%d", ramdisc->prot_capable);
775  ocs_ddump_value(textbuf, "p_type", "%d", ramdisc->p_type);
776  ocs_ddump_value(textbuf, "prot_en", "%d", ramdisc->prot_en);
777  ocs_ddump_value(textbuf, "segment_count", "%d", ramdisc->segment_count);
778  ocs_ddump_value(textbuf, "segment_size", "%d", ramdisc->segment_size);
779  ocs_ddump_value(textbuf, "dif_separate", "%d", ramdisc->dif_separate);
780 #if defined(ENABLE_DDUMP_RAMDISC_SEGMENTS)
781  for (i = 0; i < ramdisc->segment_count; i++) {
782  seg = ramdisc->segments[i];
783  ocs_ddump_section(textbuf, "ramdisc_segment", i);
784  ocs_ddump_section(textbuf, "data_segment", 0);
785  ocs_ddump_value(textbuf, "virt", "%p", seg->data_segment.virt);
786  ocs_ddump_value(textbuf, "phys", "%016lx", seg->data_segment.phys);
787  ocs_ddump_value(textbuf, "size", "%d", seg->data_segment.size);
788  ocs_ddump_endsection(textbuf, "data_segment", 0);
789  ocs_ddump_section(textbuf, "dif_segment", 0);
790  ocs_ddump_value(textbuf, "virt", "%p", seg->dif_segment.virt);
791  ocs_ddump_value(textbuf, "phys", "%016lx", seg->dif_segment.phys);
792  ocs_ddump_value(textbuf, "size", "%d", seg->dif_segment.size);
793  ocs_ddump_endsection(textbuf, "dif_segment", 0);
794  ocs_ddump_endsection(textbuf, "ramdisc_segment", i);
795  }
796 #endif
797  ocs_ddump_endsection(textbuf, "ramdisc", ramdisc->lun);
798 }
799 
800 #if ENABLE_DIF_VERIFICATION == 1
801 /**
802  * @brief Verify the DIF information on an LBA range - Used to verify chip operations
803  *
804  * @par Description
805  * Calculate the DIF CRC, ref tag and app tag and verifies the values.
806  *
807  * @param ocs Pointer to the ocs_t structure.
808  * @param ramdisc Pointer to the ramdisc instance to initialize.
809  * @param lba Starting block address.
810  * @param block_count The number of blocks to transfer for the IO.
811  *
812  * @return Returns 0 on success.
813  */
814 int32_t
815 ocs_ramdisc_dif_verify(ocs_t *ocs, ocs_ramdisc_t *ramdisc,
816  uint64_t lba, uint32_t block_count)
817 {
818  uint32_t i;
819  uint16_t crc;
820  uint8_t *difbytes;
821  int32_t rc = 0;
822  uint16_t recv_crc;
823  uint16_t recv_apptag;
824  uint32_t recv_reftag;
825  uint32_t s0; // index first segment in this SGL
826  uint32_t l0; // first lba of current segment
827  uint32_t lba_off; // lba offset into segment
828  uint32_t dif_off; // lba offset into segment
829  uint32_t off; // byte offset into segment
830  ramdisc_segment_t *seg;
831  uint8_t errors_detected;
832 
833  /* return if DIF is not enabled - nothing to check */
834  if (ramdisc->prot_en == 0) {
835  return 0;
836  }
837 
838  for (i = 0; i < block_count; i++) {
839  /* if this request falls off the end of the ramdisc, then return an error */
840  if ((lba + block_count) > ramdisc->block_count) {
841  return -1;
842  }
843 
844  s0 = (lba + i) / ramdisc->blocks_per_segment;
845  l0 = s0 * ramdisc->blocks_per_segment;
846  lba_off = (lba + i) - l0;
847  off = (lba_off * ramdisc->segment_block_size);
848  dif_off = (lba_off * DIF_SIZE);
849  seg = ramdisc->segments[s0];
850 
851  crc = 0;
852  crc = ocs_scsi_dif_calc_crc(seg->data_segment.virt + off, ramdisc->data_block_size, crc);
853  if (ramdisc->dif_separate && ramdisc->prot_en) {
854  difbytes = (uint8_t*)seg->dif_segment.virt + dif_off;
855  } else {
856  difbytes = (uint8_t*)seg->data_segment.virt + off + ramdisc->data_block_size;
857  }
858 
859  errors_detected = FALSE;
860  /* verify the CRC */
861  recv_crc = ((uint16_t)difbytes[0]) << 8 | difbytes[1];
862 
863  if (crc != recv_crc && recv_crc != 0xFFFF) {
864  ocs_log_test(ocs, "miscompare (CRC) on LBA 0x%" PRIx64 ", "
865  "memory contains 0x%x but calculated 0x%x\n",
866  lba + i,
867  recv_crc,
868  crc);
869  errors_detected = TRUE;
870  rc = -1;
871  }
872 
873  /* verify the app tag */
874  recv_apptag = ((uint16_t)difbytes[2]) << 8 | difbytes[3];
875  if (0 != recv_apptag && recv_apptag != 0xFFFF) {
876  ocs_log_test(ocs, "miscompare (app_tag) on LBA 0x%" PRIx64 ", "
877  "memory contains 0x%x but calculated 0x%x\n",
878  lba + i,
879  recv_apptag,
880  0);
881  errors_detected = TRUE;
882  rc = -1;
883  }
884 
885  /* verify the ref tag */
886  recv_reftag = (((uint32_t)difbytes[4] << 24) |
887  ((uint32_t)difbytes[5] << 16) |
888  ((uint32_t)difbytes[6] << 8) |
889  difbytes[7]);
890 
891  if (lba + i != recv_reftag && recv_reftag != 0xFFFFFFFF) {
892  ocs_log_test(ocs, "miscompare (ref_tag) on LBA 0x%" PRIx64 ", "
893  "memory contains 0x%x but calculated 0x%" PRIx64 "\n",
894  lba + i,
895  recv_reftag,
896  lba + i);
897  errors_detected = TRUE;
898  rc = -1;
899  }
900 
901  /* Dump the data block if there are any errors */
902  if (errors_detected) {
903  char desc[64];
904 
905  sprintf(desc, "Lba 0x%" PRIx64, lba + i);
906 
907  ocs_dump32(OCS_DEBUG_ALWAYS, ocs, desc,
908  (uint8_t*)seg->data_segment.virt + off,
909  ramdisc->phys_block_size);
910  }
911  }
912 
913  return rc;
914 }
915 #endif
void dif_pool_init(ocs_t *ocs, ocs_list_t *head)
initialize the DIF memory pool
Definition: ocs_ramdisc.c:636
uint32_t data_block_size
Definition: ocs_ramdisc.h:90
int32_t ocs_new_ramdisc(ocs_t *ocs, ocs_ramdisc_t *ramdisc, uint32_t block_size, uint32_t blocks_per_segment, uint64_t block_count)
Create a new ramdisc instance (LUN).
Definition: ocs_ramdisc.c:72
void * ocs_pool_get(ocs_pool_t *pool)
Allocate a memory pool item.
Definition: ocs_pool.c:197
uint8_t * ocs_ramdisc_lba_address(ocs_ramdisc_t *ramdisc, uint64_t lba)
Return the address of a ramdisc logical block.
Definition: ocs_ramdisc.c:577
uint8_t prot_capable
Definition: ocs_ramdisc.h:97
int32_t dif_pool_alloc(ocs_t *ocs, ocs_list_t *head, uint32_t size, ocs_difdma_t *difdma)
Allocate memory for the DIF memory pool.
Definition: ocs_ramdisc.c:724
static int32_t ocs_list_empty(ocs_list_t *list)
Test if a list is empty.
Definition: ocs_list.h:125
uint32_t segment_size
Definition: ocs_ramdisc.h:108
uint32_t phys_block_size
Definition: ocs_ramdisc.h:91
int32_t ocs_ramdisc_sgl(ocs_t *ocs, ocs_ramdisc_t *ramdisc, ocs_ramdisc_sgl_t *sgl, uint32_t max_sgl, uint32_t max_sge_size, uint32_t lba, uint32_t *block_count, uint32_t req_flags, uint8_t dump_buffer)
Build an SGL list for blocks in a ramdisc.
Definition: ocs_ramdisc.c:218
#define DIF_SIZE
Definition: ocs_scsi.h:179
uint64_t block_count
Definition: ocs_ramdisc.h:95
uint32_t segment_count
Definition: ocs_ramdisc.h:107
uint32_t blocks_per_segment
Definition: ocs_ramdisc.h:94
void ocs_set_ramdisc_blocksize(ocs_t *ocs, ocs_ramdisc_t *ramdisc, uint32_t block_size)
Set the ramdisc block size.
Definition: ocs_ramdisc.c:537
uint8_t force_dif_separate
Definition: ocs_ramdisc.h:103
ramdisc instance structure
Definition: ocs_ramdisc.h:88
void ocs_ddump_section(ocs_textbuf_t *textbuf, const char *name, uint32_t instance)
Generate driver dump section start data.
Definition: ocs_ddumplib.c:90
ramdisc segment structure
Definition: ocs_ramdisc.h:79
ocs_pool_t * ocs_pool_alloc(ocs_os_handle_t os, uint32_t size, uint32_t count, uint32_t use_lock)
Allocate a memory pool.
Definition: ocs_pool.c:88
#define OCS_DEBUG_ALWAYS
Definition: ocs_debug.h:167
void ocs_pool_free(ocs_pool_t *pool)
Free a previously allocated memory pool.
Definition: ocs_pool.c:173
int32_t ocs_del_ramdisc(ocs_t *ocs, ocs_ramdisc_t *ramdisc)
Delete a ramdisc instance.
Definition: ocs_ramdisc.c:151
size_t size
Definition: ocs_ramdisc.h:73
static void * ocs_list_remove(ocs_list_t *list, void *item)
Remove an item from the list.
Definition: ocs_list.h:385
#define SGL_REQ_FLAGS_PHYS
Definition: ocs_ramdisc.h:129
static void * ocs_list_get_tail(ocs_list_t *list)
Return the first item in the list.
Definition: ocs_list.h:308
#define ocs_list_init(head, type, link)
Definition: ocs_list.h:113
void ocs_dump32(uint32_t mask, ocs_os_handle_t os, const char *label, void *buf, uint32_t buf_length)
Dump 32 bit hex/ascii data.
Definition: ocs_dump32.c:72
#define RAMDISC_MIN_BLOCK_SIZE
Definition: ocs_ramdisc.h:42
uint8_t prot_en
Definition: ocs_ramdisc.h:99
ramdisc_segment_t ** segments
Definition: ocs_ramdisc.h:106
#define ocs_list_foreach_safe(list, item, nxt)
Iterate a linked list safely.
Definition: ocs_list.h:446
#define OCS_CTRLMASK_TGT_SET_DIF_REF_TAG_CRC
generic ramdisc scatter-gather list
Definition: ocs_ramdisc.h:119
#define DIF_POOL_SIZE
Definition: ocs_ramdisc.h:41
uintptr_t dif_addr
Definition: ocs_ramdisc.h:121
static int32_t dif_pool_new(ocs_t *ocs, ocs_list_t *head)
Allocate a new dif_pool_t entry.
Definition: ocs_ramdisc.c:688
dif_t * ocs_ramdisc_dif_address(ocs_ramdisc_t *ramdisc, uint64_t lba)
Return the address of the DIF block if protection is enabled.
Definition: ocs_ramdisc.c:605
uint32_t lun
Definition: ocs_ramdisc.h:89
void * virt
Definition: ocs_ramdisc.h:71
void ocs_ddump_endsection(ocs_textbuf_t *textbuf, const char *name, uint32_t instance)
Generate driver dump section end data.
Definition: ocs_ddumplib.c:108
ocs_list_t dif_pool_list
Definition: ocs_ramdisc.h:111
static void ocs_list_add_tail(ocs_list_t *list, void *item)
Add an item to the tail of the list.
Definition: ocs_list.h:263
uint32_t pending_block_size
Definition: ocs_ramdisc.h:96
uint32_t segment_block_size
Definition: ocs_ramdisc.h:92
uint8_t dif_separate
Definition: ocs_ramdisc.h:93
uintptr_t phys
Definition: ocs_ramdisc.h:72
int32_t ocs_format_ramdisc(ocs_t *ocs, ocs_ramdisc_t *ramdisc, uint32_t block_size, uint8_t p_type, uint8_t prot_en, uint8_t dif_separate)
Reformat an existing ramdisc, possibly the changing block size and/or protection. ...
Definition: ocs_ramdisc.c:359
#define ocs_assert(cond,...)
Definition: ocs_debug.h:176
uint32_t ocs_get_ramdisc_blocksize(ocs_t *ocs, ocs_ramdisc_t *ramdisc)
Get the ramdisc block size.
Definition: ocs_ramdisc.c:555
ocs_pool_t * segment_pool
Definition: ocs_ramdisc.h:105
uint16_t ocs_scsi_dif_calc_crc(const uint8_t *buffer, uint32_t size, uint16_t crc)
Calculate the T10 PI CRC guard value for a block.
Definition: ocs_dif.c:54
uint8_t p_type
Definition: ocs_ramdisc.h:98
ocs_dma_t data_segment
Definition: ocs_ramdisc.h:80
#define ocs_list_foreach(list, item)
Iterate a linked list.
Definition: ocs_list.h:428
void ocs_ddump_ramdisc(ocs_textbuf_t *textbuf, ocs_ramdisc_t *ramdisc)
Definition: ocs_ramdisc.c:759
ocs_difdma_t dif_segment
Definition: ocs_ramdisc.h:81
void dif_pool_free(ocs_t *ocs, ocs_list_t *head)
Free the DIF memory pool.
Definition: ocs_ramdisc.c:660
uint32_t ocs_scsi_get_property(ocs_t *ocs, ocs_scsi_property_e prop)
Return SCSI API integer valued property.
Definition: ocs_scsi.c:2799
void ocs_ddump_value(ocs_textbuf_t *textbuf, const char *name, const char *fmt,...)
Generate driver dump data for a given value.
Definition: ocs_ddumplib.c:126