Print this page
8368 remove warlock leftovers from usr/src/uts

Split Close
Expand all
Collapse all
          --- old/usr/src/uts/common/io/ib/adapters/hermon/hermon_misc.c
          +++ new/usr/src/uts/common/io/ib/adapters/hermon/hermon_misc.c
↓ open down ↓ 504 lines elided ↑ open up ↑
 505  505  
 506  506          /*
 507  507           * Allocate the software structure for tracking the address handle
 508  508           * (i.e. the Hermon Address Handle struct).
 509  509           */
 510  510          status = hermon_rsrc_alloc(state, HERMON_AHHDL, 1, sleepflag, &rsrc);
 511  511          if (status != DDI_SUCCESS) {
 512  512                  return (IBT_INSUFF_RESOURCE);
 513  513          }
 514  514          ah = (hermon_ahhdl_t)rsrc->hr_addr;
 515      -        _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*ah))
 516  515  
 517  516          /* Increment the reference count on the protection domain (PD) */
 518  517          hermon_pd_refcnt_inc(pd);
 519  518  
 520  519          udav = (hermon_hw_udav_t *)kmem_zalloc(sizeof (hermon_hw_udav_t),
 521  520              KM_SLEEP);
 522      -        _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*udav))
 523  521  
 524  522          /*
 525  523           * Fill in the UDAV data. We first zero out the UDAV, then populate
 526  524           * it by then calling hermon_set_addr_path() to fill in the common
 527  525           * portions that can be pulled from the "ibt_adds_vect_t" passed in
 528  526           */
 529  527          status = hermon_set_addr_path(state, attr_p,
 530  528              (hermon_hw_addr_path_t *)udav, HERMON_ADDRPATH_UDAV);
 531  529          if (status != DDI_SUCCESS) {
 532  530                  hermon_pd_refcnt_dec(pd);
↓ open down ↓ 37 lines elided ↑ open up ↑
 570  568          /*
 571  569           * Pull all the necessary information from the Hermon Address Handle
 572  570           * struct.  This is necessary here because the resource for the
 573  571           * AH is going to be freed up as part of this operation.
 574  572           */
 575  573          ah    = *ahhdl;
 576  574          mutex_enter(&ah->ah_lock);
 577  575          rsrc  = ah->ah_rsrcp;
 578  576          pd    = ah->ah_pdhdl;
 579  577          mutex_exit(&ah->ah_lock);
 580      -        _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*ah))
 581  578  
 582  579          /* Free the UDAV memory */
 583  580          kmem_free(ah->ah_udav, sizeof (hermon_hw_udav_t));
 584  581  
 585  582          /* Decrement the reference count on the protection domain (PD) */
 586  583          hermon_pd_refcnt_dec(pd);
 587  584  
 588  585          /* Free the Hermon Address Handle structure */
 589  586          hermon_rsrc_free(state, &rsrc);
 590  587  
↓ open down ↓ 7 lines elided ↑ open up ↑
 598  595  /*
 599  596   * hermon_ah_query()
 600  597   *    Context: Can be called from interrupt or base context.
 601  598   */
 602  599  /* ARGSUSED */
 603  600  int
 604  601  hermon_ah_query(hermon_state_t *state, hermon_ahhdl_t ah, hermon_pdhdl_t *pd,
 605  602      ibt_adds_vect_t *attr_p)
 606  603  {
 607  604          mutex_enter(&ah->ah_lock);
 608      -        _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*attr_p))
 609  605  
 610  606          /*
 611  607           * Pull the PD and UDAV from the Hermon Address Handle structure
 612  608           */
 613  609          *pd = ah->ah_pdhdl;
 614  610  
 615  611          /*
 616  612           * Fill in "ibt_adds_vect_t".  We call hermon_get_addr_path() to fill
 617  613           * the common portions that can be pulled from the UDAV we pass in.
 618  614           *
↓ open down ↓ 46 lines elided ↑ open up ↑
 665  661           * is not always preserved. The reason for this is described in
 666  662           * hermon_set_addr_path().
 667  663           */
 668  664          status = hermon_set_addr_path(state, attr_p,
 669  665              (hermon_hw_addr_path_t *)ah->ah_udav, HERMON_ADDRPATH_UDAV);
 670  666          if (status != DDI_SUCCESS) {
 671  667                  mutex_exit(&ah->ah_lock);
 672  668                  return (status);
 673  669          }
 674  670          ah->ah_save_guid = attr_p->av_dgid.gid_guid;
 675      -        _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*(ah->ah_udav)))
 676  671          ah->ah_udav->sl  = attr_p->av_srvl;
 677  672  
 678  673          /*
 679  674           * Copy changes into the new UDAV.
 680  675           *    Note:  We copy in 64-bit chunks.  For the first two of these
 681  676           *    chunks it is necessary to read the current contents of the
 682  677           *    UDAV, mask off the modifiable portions (maintaining any
 683  678           *    of the "reserved" portions), and then mask on the new data.
 684  679           */
 685  680          size = sizeof (hermon_hw_udav_t) >> 3;
↓ open down ↓ 1126 lines elided ↑ open up ↑
1812 1807           * (i.e. the Hermon Protection Domain handle).  By default each PD
1813 1808           * structure will have a unique PD number assigned to it.  All that
1814 1809           * is necessary is for software to initialize the PD reference count
1815 1810           * (to zero) and return success.
1816 1811           */
1817 1812          status = hermon_rsrc_alloc(state, HERMON_PDHDL, 1, sleepflag, &rsrc);
1818 1813          if (status != DDI_SUCCESS) {
1819 1814                  return (IBT_INSUFF_RESOURCE);
1820 1815          }
1821 1816          pd = (hermon_pdhdl_t)rsrc->hr_addr;
1822      -        _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*pd))
1823 1817  
1824 1818          pd->pd_refcnt = 0;
1825 1819          *pdhdl = pd;
1826 1820  
1827 1821          return (DDI_SUCCESS);
1828 1822  }
1829 1823  
1830 1824  
1831 1825  /*
1832 1826   * hermon_pd_free()
↓ open down ↓ 4 lines elided ↑ open up ↑
1837 1831  {
1838 1832          hermon_rsrc_t   *rsrc;
1839 1833          hermon_pdhdl_t  pd;
1840 1834  
1841 1835          /*
1842 1836           * Pull all the necessary information from the Hermon Protection Domain
1843 1837           * handle.  This is necessary here because the resource for the
1844 1838           * PD is going to be freed up as part of this operation.
1845 1839           */
1846 1840          pd   = *pdhdl;
1847      -        _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*pd))
1848 1841          rsrc = pd->pd_rsrcp;
1849 1842  
1850 1843          /*
1851 1844           * Check the PD reference count.  If the reference count is non-zero,
1852 1845           * then it means that this protection domain is still referenced by
1853 1846           * some memory region, queue pair, address handle, or other IB object
1854 1847           * If it is non-zero, then return an error.  Otherwise, free the
1855 1848           * Hermon resource and return success.
1856 1849           */
1857 1850          if (pd->pd_refcnt != 0) {
↓ open down ↓ 43 lines elided ↑ open up ↑
1901 1894  {
1902 1895          sm_portinfo_t           portinfo;
1903 1896          sm_guidinfo_t           guidinfo;
1904 1897          sm_pkey_table_t         pkeytable;
1905 1898          ib_gid_t                *sgid;
1906 1899          uint_t                  sgid_max, pkey_max, tbl_size;
1907 1900          int                     i, j, indx, status;
1908 1901          ib_pkey_t               *pkeyp;
1909 1902          ib_guid_t               *guidp;
1910 1903  
1911      -        _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*pi))
1912      -        _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*state))
1913      -
1914 1904          /* Validate that specified port number is legal */
1915 1905          if (!hermon_portnum_is_valid(state, port)) {
1916 1906                  return (IBT_HCA_PORT_INVALID);
1917 1907          }
1918 1908          pkeyp = state->hs_pkey[port - 1];
1919 1909          guidp = state->hs_guid[port - 1];
1920 1910  
1921 1911          /*
1922 1912           * We use the Hermon MAD_IFC command to post a GetPortInfo MAD
1923 1913           * to the firmware (for the specified port number).  This returns
↓ open down ↓ 77 lines elided ↑ open up ↑
2001 1991                                      HCA_ERR_SRV_LOST);
2002 1992                          }
2003 1993                          return (ibc_get_ci_failure(0));
2004 1994                  }
2005 1995  
2006 1996                  /* Figure out how many of the entries are valid */
2007 1997                  sgid_max = min((pi->p_sgid_tbl_sz - i), 8);
2008 1998                  for (j = 0; j < sgid_max; j++) {
2009 1999                          indx = (i + j);
2010 2000                          sgid = &pi->p_sgid_tbl[indx];
2011      -                        _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*sgid))
2012 2001                          sgid->gid_prefix = portinfo.GidPrefix;
2013 2002                          guidp[indx] = sgid->gid_guid =
2014 2003                              guidinfo.GUIDBlocks[j];
2015 2004                  }
2016 2005          }
2017 2006  
2018 2007          /*
2019 2008           * Fill in the PKey table.  Just as for the GID tables above, the
2020 2009           * only access to the Hermon PKey tables is through the firmware's
2021 2010           * MAD_IFC interface.  We post as many GetPKeyTable MADs as necessary
↓ open down ↓ 145 lines elided ↑ open up ↑
2167 2156  
2168 2157  int hermon_srate_override = -1; /* allows ease of testing */
2169 2158  
2170 2159  int
2171 2160  hermon_set_addr_path(hermon_state_t *state, ibt_adds_vect_t *av,
2172 2161      hermon_hw_addr_path_t *path, uint_t type)
2173 2162  {
2174 2163          uint_t          gidtbl_sz;
2175 2164          hermon_hw_udav_t *udav;
2176 2165  
2177      -        _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*av))
2178      -        _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*path))
2179      -
2180 2166          udav = (hermon_hw_udav_t *)(void *)path;
2181      -        _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*udav))
2182 2167          path->mlid      = av->av_src_path;
2183 2168          path->rlid      = av->av_dlid;
2184 2169  
2185 2170          switch (av->av_srate) {
2186 2171          case IBT_SRATE_2:       /* 1xSDR-2.5Gb/s injection rate */
2187 2172                  path->max_stat_rate = 7; break;
2188 2173          case IBT_SRATE_10:      /* 4xSDR-10.0Gb/s injection rate */
2189 2174                  path->max_stat_rate = 8; break;
2190 2175          case IBT_SRATE_30:      /* 12xSDR-30Gb/s injection rate */
2191 2176                  path->max_stat_rate = 9; break;
↓ open down ↓ 90 lines elided ↑ open up ↑
2282 2267   * structures are similar, common fields can be read in here.  But because
2283 2268   * they are slightly different, we pass an additional flag to indicate which
2284 2269   * type is being read.
2285 2270   */
2286 2271  void
2287 2272  hermon_get_addr_path(hermon_state_t *state, hermon_hw_addr_path_t *path,
2288 2273      ibt_adds_vect_t *av, uint_t type)
2289 2274  {
2290 2275          uint_t          gidtbl_sz;
2291 2276  
2292      -        _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*path))
2293      -        _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*av))
2294      -
2295 2277          av->av_src_path = path->mlid;
2296 2278          av->av_dlid     = path->rlid;
2297 2279  
2298 2280          /* Set "av_ipd" value from max_stat_rate */
2299 2281          switch (path->max_stat_rate) {
2300 2282          case 7:                         /* 1xSDR-2.5Gb/s injection rate */
2301 2283                  av->av_srate = IBT_SRATE_2; break;
2302 2284          case 8:                         /* 4xSDR-10.0Gb/s injection rate */
2303 2285                  av->av_srate = IBT_SRATE_10; break;
2304 2286          case 9:                         /* 12xSDR-30Gb/s injection rate */
↓ open down ↓ 88 lines elided ↑ open up ↑
2393 2375   */
2394 2376  int
2395 2377  hermon_queue_alloc(hermon_state_t *state, hermon_qalloc_info_t *qa_info,
2396 2378      uint_t sleepflag)
2397 2379  {
2398 2380          ddi_dma_attr_t          dma_attr;
2399 2381          int                     (*callback)(caddr_t);
2400 2382          uint64_t                realsize, alloc_mask;
2401 2383          int                     flag, status;
2402 2384  
2403      -        _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*qa_info))
2404      -
2405 2385          /* Set the callback flag appropriately */
2406 2386          callback = (sleepflag == HERMON_SLEEP) ? DDI_DMA_SLEEP :
2407 2387              DDI_DMA_DONTWAIT;
2408 2388  
2409 2389          /*
2410 2390           * Initialize many of the default DMA attributes.  Then set additional
2411 2391           * alignment restrictions as necessary for the queue memory.  Also
2412 2392           * respect the configured value for IOMMU bypass
2413 2393           */
2414 2394          hermon_dma_attr_init(state, &dma_attr);
↓ open down ↓ 98 lines elided ↑ open up ↑
2513 2493  }
2514 2494  
2515 2495  
2516 2496  /*
2517 2497   * hermon_queue_free()
2518 2498   *    Context: Can be called from interrupt or base context.
2519 2499   */
2520 2500  void
2521 2501  hermon_queue_free(hermon_qalloc_info_t *qa_info)
2522 2502  {
2523      -        _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*qa_info))
2524      -
2525 2503          /*
2526 2504           * Depending on how (i.e. from where) we allocated the memory for
2527 2505           * this queue, we choose the appropriate method for releasing the
2528 2506           * resources.
2529 2507           */
2530 2508          if (qa_info->qa_location == HERMON_QUEUE_LOCATION_NORMAL) {
2531 2509  
2532 2510                  ddi_dma_mem_free(&qa_info->qa_acchdl);
2533 2511  
2534 2512          } else if (qa_info->qa_location == HERMON_QUEUE_LOCATION_USERLAND) {
↓ open down ↓ 27 lines elided ↑ open up ↑
2562 2540          if ((sleep == HERMON_SLEEP) &&
2563 2541              (sleep != HERMON_SLEEPFLAG_FOR_CONTEXT())) {
2564 2542                  return (IBT_INVALID_PARAM);
2565 2543          }
2566 2544  
2567 2545          fmrpool = (hermon_fmrhdl_t)kmem_zalloc(sizeof (*fmrpool), sleep);
2568 2546          if (fmrpool == NULL) {
2569 2547                  status = IBT_INSUFF_RESOURCE;
2570 2548                  goto fail;
2571 2549          }
2572      -        _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*fmrpool))
2573 2550  
2574 2551          mutex_init(&fmrpool->fmr_lock, NULL, MUTEX_DRIVER,
2575 2552              DDI_INTR_PRI(state->hs_intrmsi_pri));
2576 2553          mutex_init(&fmrpool->remap_lock, NULL, MUTEX_DRIVER,
2577 2554              DDI_INTR_PRI(state->hs_intrmsi_pri));
2578 2555          mutex_init(&fmrpool->dirty_lock, NULL, MUTEX_DRIVER,
2579 2556              DDI_INTR_PRI(state->hs_intrmsi_pri));
2580 2557  
2581 2558          fmrpool->fmr_state          = state;
2582 2559          fmrpool->fmr_flush_function = fmr_attr->fmr_func_hdlr;
↓ open down ↓ 19 lines elided ↑ open up ↑
2602 2579              fmr_attr->fmr_pool_size;
2603 2580  
2604 2581          for (i = 0; i < fmr_attr->fmr_pool_size; i++) {
2605 2582                  status = hermon_mr_alloc_fmr(state, pd, fmrpool, &mr);
2606 2583                  if (status != DDI_SUCCESS) {
2607 2584                          goto fail2;
2608 2585                  }
2609 2586  
2610 2587                  fmr = (hermon_fmr_list_t *)kmem_zalloc(
2611 2588                      sizeof (hermon_fmr_list_t), sleep);
2612      -                _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*fmr))
2613 2589  
2614 2590                  fmr->fmr = mr;
2615 2591                  fmr->fmr_remaps = 0;
2616 2592                  fmr->fmr_remap_gen = fmrpool->fmr_remap_gen;
2617 2593                  fmr->fmr_pool = fmrpool;
2618      -                _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*mr))
2619 2594                  mr->mr_fmr = fmr;
2620 2595  
2621 2596                  if (!i)         /* address of last entry's link */
2622 2597                          fmrpool->fmr_free_list_tail = &fmr->fmr_next;
2623 2598                  fmr->fmr_next = fmrpool->fmr_free_list;
2624 2599                  fmrpool->fmr_free_list = fmr;
2625 2600          }
2626 2601  
2627 2602          /* Set to return pool */
2628 2603          *fmrpoolp = fmrpool;
2629 2604  
2630 2605          IBTF_DPRINTF_L2("fmr", "create_fmr_pool SUCCESS");
2631 2606          return (IBT_SUCCESS);
2632 2607  fail2:
2633 2608          for (fmr = fmrpool->fmr_free_list; fmr != NULL; fmr = fmr_next) {
2634      -                _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*fmr))
2635 2609                  fmr_next = fmr->fmr_next;
2636 2610                  (void) hermon_mr_dealloc_fmr(state, &fmr->fmr);
2637 2611                  kmem_free(fmr, sizeof (hermon_fmr_list_t));
2638 2612          }
2639 2613          kmem_free(fmrpool, sizeof (*fmrpool));
2640 2614  fail:
2641 2615          *fmrpoolp = NULL;
2642 2616          IBTF_DPRINTF_L2("fmr", "create_fmr_pool FAILED");
2643 2617          if (status == DDI_FAILURE) {
2644 2618                  return (ibc_get_ci_failure(0));
↓ open down ↓ 108 lines elided ↑ open up ↑
2753 2727                  mutex_exit(&fmrpool->fmr_lock);
2754 2728                  return (IBT_INSUFF_RESOURCE);
2755 2729          }
2756 2730  
2757 2731          if ((fmrpool->fmr_free_list = fmr->fmr_next) == NULL)
2758 2732                  fmrpool->fmr_free_list_tail = &fmrpool->fmr_free_list;
2759 2733          fmr->fmr_next = NULL;
2760 2734          fmrpool->fmr_stat_register++;
2761 2735          mutex_exit(&fmrpool->fmr_lock);
2762 2736  
2763      -        _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*fmr))
2764 2737          status = hermon_mr_register_physical_fmr(state, mem_pattr, fmr->fmr,
2765 2738              mem_desc_p);
2766 2739          if (status != DDI_SUCCESS) {
2767 2740                  return (status);
2768 2741          }
2769      -        _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*fmr->fmr))
2770 2742          if (hermon_rdma_debug & 0x4)
2771 2743                  IBTF_DPRINTF_L2("fmr", "  reg: mr %p  key %x",
2772 2744                      fmr->fmr, fmr->fmr->mr_rkey);
2773      -        _NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*fmr->fmr))
2774 2745          if (fmr->fmr_remap_gen != fmrpool->fmr_remap_gen) {
2775 2746                  fmr->fmr_remap_gen = fmrpool->fmr_remap_gen;
2776 2747                  fmr->fmr_remaps = 0;
2777 2748          }
2778 2749  
2779 2750          fmr->fmr_remaps++;
2780 2751  
2781 2752          *mr = (hermon_mrhdl_t)fmr->fmr;
2782 2753  
2783 2754          return (DDI_SUCCESS);
↓ open down ↓ 5 lines elided ↑ open up ↑
2789 2760   *    Context: Can be called from kernel context only.
2790 2761   */
2791 2762  int
2792 2763  hermon_deregister_fmr(hermon_state_t *state, hermon_mrhdl_t mr)
2793 2764  {
2794 2765          hermon_fmrhdl_t         fmrpool;
2795 2766          hermon_fmr_list_t       *fmr, **fmrlast;
2796 2767          int                     len;
2797 2768  
2798 2769          fmr = mr->mr_fmr;
2799      -        _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*fmr))
2800 2770          fmrpool = fmr->fmr_pool;
2801 2771  
2802 2772          /* mark as owned by software */
2803      -        _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*(fmr->fmr)))
2804 2773          *(uint8_t *)(fmr->fmr->mr_mptrsrcp->hr_addr) = 0xF0;
2805 2774  
2806 2775          if (fmr->fmr_remaps <
2807 2776              state->hs_cfg_profile->cp_fmr_max_remaps) {
2808 2777                  /* add to remap list */
2809      -                _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*(fmr->fmr)))
2810 2778                  if (hermon_rdma_debug & 0x4)
2811 2779                          IBTF_DPRINTF_L2("fmr", "dereg: mr %p  key %x",
2812 2780                              fmr->fmr, fmr->fmr->mr_rkey);
2813      -                _NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*(fmr->fmr)))
2814 2781                  mutex_enter(&fmrpool->remap_lock);
2815 2782                  fmr->fmr_next = NULL;
2816 2783                  *(fmrpool->fmr_remap_list_tail) = fmr;
2817 2784                  fmrpool->fmr_remap_list_tail = &fmr->fmr_next;
2818 2785                  fmrpool->fmr_remap_len++;
2819 2786  
2820 2787                  /* conditionally add remap list back to free list */
2821 2788                  fmrlast = NULL;
2822 2789                  if (fmrpool->fmr_remap_len >=
2823 2790                      fmrpool->fmr_remap_watermark) {
↓ open down ↓ 8 lines elided ↑ open up ↑
2832 2799                  mutex_exit(&fmrpool->remap_lock);
2833 2800                  if (fmrlast) {
2834 2801                          mutex_enter(&fmrpool->fmr_lock);
2835 2802                          *(fmrpool->fmr_free_list_tail) = fmr;
2836 2803                          fmrpool->fmr_free_list_tail = fmrlast;
2837 2804                          fmrpool->fmr_free_len += len;
2838 2805                          mutex_exit(&fmrpool->fmr_lock);
2839 2806                  }
2840 2807          } else {
2841 2808                  /* add to dirty list */
2842      -                _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*(fmr->fmr)))
2843 2809                  if (hermon_rdma_debug & 0x4)
2844 2810                          IBTF_DPRINTF_L2("fmr", "dirty: mr %p  key %x",
2845 2811                              fmr->fmr, fmr->fmr->mr_rkey);
2846      -                _NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*(fmr->fmr)))
2847 2812  
2848 2813                  mutex_enter(&fmrpool->dirty_lock);
2849 2814                  fmr->fmr_next = NULL;
2850 2815                  *(fmrpool->fmr_dirty_list_tail) = fmr;
2851 2816                  fmrpool->fmr_dirty_list_tail = &fmr->fmr_next;
2852 2817                  fmrpool->fmr_dirty_len++;
2853 2818  
2854 2819                  if (fmrpool->fmr_dirty_len >=
2855 2820                      fmrpool->fmr_dirty_watermark) {
2856 2821                          mutex_exit(&fmrpool->dirty_lock);
↓ open down ↓ 70 lines elided ↑ open up ↑
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX