diff options
author | Jamie Wellnitz <Jamie.Wellnitz@emulex.com> | 2006-02-28 22:33:10 -0500 |
---|---|---|
committer | James Bottomley <jejb@mulgrave.il.steeleye.com> | 2006-03-06 10:47:31 -0500 |
commit | 66a9ed66000d186933892ca5121e68a071d624ac (patch) | |
tree | c5aa2b1e2e819876a980e59f3cbd8d0a7761aeae /drivers/scsi/lpfc/lpfc_hbadisc.c | |
parent | 5fe9f5119378e75986ad90c783a7e085bf67703a (diff) |
[PATCH] lpfc 8.1.3: Protect NPL lists with host lock
Protect NPL lists with host lock
Symptoms: lpfc_findnode_rpi and lpfc_findnode_did can be called
outside of the discovery thread context. We have to iterate
through the NPL lists under the host lock and all add/del
operations on those lists have to be done under host lock.
Signed-off-by: Jamie Wellnitz <Jamie.Wellnitz@emulex.com>
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
Diffstat (limited to 'drivers/scsi/lpfc/lpfc_hbadisc.c')
-rw-r--r-- | drivers/scsi/lpfc/lpfc_hbadisc.c | 53 |
1 files changed, 24 insertions, 29 deletions
diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c index aa58ff0e3218..e7664a6bd251 100644 --- a/drivers/scsi/lpfc/lpfc_hbadisc.c +++ b/drivers/scsi/lpfc/lpfc_hbadisc.c | |||
@@ -1097,6 +1097,7 @@ lpfc_nlp_list(struct lpfc_hba * phba, struct lpfc_nodelist * nlp, int list) | |||
1097 | if (list != NLP_NO_LIST) | 1097 | if (list != NLP_NO_LIST) |
1098 | return 0; | 1098 | return 0; |
1099 | 1099 | ||
1100 | spin_lock_irq(phba->host->host_lock); | ||
1100 | switch (nlp->nlp_flag & NLP_LIST_MASK) { | 1101 | switch (nlp->nlp_flag & NLP_LIST_MASK) { |
1101 | case NLP_NO_LIST: /* Not on any list */ | 1102 | case NLP_NO_LIST: /* Not on any list */ |
1102 | break; | 1103 | break; |
@@ -1123,10 +1124,8 @@ lpfc_nlp_list(struct lpfc_hba * phba, struct lpfc_nodelist * nlp, int list) | |||
1123 | case NLP_UNMAPPED_LIST: | 1124 | case NLP_UNMAPPED_LIST: |
1124 | phba->fc_unmap_cnt--; | 1125 | phba->fc_unmap_cnt--; |
1125 | list_del(&nlp->nlp_listp); | 1126 | list_del(&nlp->nlp_listp); |
1126 | spin_lock_irq(phba->host->host_lock); | ||
1127 | nlp->nlp_flag &= ~NLP_TGT_NO_SCSIID; | 1127 | nlp->nlp_flag &= ~NLP_TGT_NO_SCSIID; |
1128 | nlp->nlp_type &= ~NLP_FC_NODE; | 1128 | nlp->nlp_type &= ~NLP_FC_NODE; |
1129 | spin_unlock_irq(phba->host->host_lock); | ||
1130 | phba->nport_event_cnt++; | 1129 | phba->nport_event_cnt++; |
1131 | if (nlp->rport) | 1130 | if (nlp->rport) |
1132 | rport_del = unmapped; | 1131 | rport_del = unmapped; |
@@ -1144,20 +1143,18 @@ lpfc_nlp_list(struct lpfc_hba * phba, struct lpfc_nodelist * nlp, int list) | |||
1144 | /* Stop delay tmo if taking node off NPR list */ | 1143 | /* Stop delay tmo if taking node off NPR list */ |
1145 | if ((nlp->nlp_flag & NLP_DELAY_TMO) && | 1144 | if ((nlp->nlp_flag & NLP_DELAY_TMO) && |
1146 | (list != NLP_NPR_LIST)) { | 1145 | (list != NLP_NPR_LIST)) { |
1147 | spin_lock_irq(phba->host->host_lock); | ||
1148 | nlp->nlp_flag &= ~NLP_DELAY_TMO; | 1146 | nlp->nlp_flag &= ~NLP_DELAY_TMO; |
1149 | spin_unlock_irq(phba->host->host_lock); | ||
1150 | nlp->nlp_last_elscmd = 0; | 1147 | nlp->nlp_last_elscmd = 0; |
1148 | spin_unlock_irq(phba->host->host_lock); | ||
1151 | del_timer_sync(&nlp->nlp_delayfunc); | 1149 | del_timer_sync(&nlp->nlp_delayfunc); |
1150 | spin_lock_irq(phba->host->host_lock); | ||
1152 | if (!list_empty(&nlp->els_retry_evt.evt_listp)) | 1151 | if (!list_empty(&nlp->els_retry_evt.evt_listp)) |
1153 | list_del_init(&nlp->els_retry_evt.evt_listp); | 1152 | list_del_init(&nlp->els_retry_evt.evt_listp); |
1154 | } | 1153 | } |
1155 | break; | 1154 | break; |
1156 | } | 1155 | } |
1157 | 1156 | ||
1158 | spin_lock_irq(phba->host->host_lock); | ||
1159 | nlp->nlp_flag &= ~NLP_LIST_MASK; | 1157 | nlp->nlp_flag &= ~NLP_LIST_MASK; |
1160 | spin_unlock_irq(phba->host->host_lock); | ||
1161 | 1158 | ||
1162 | /* Add NPort <did> to <num> list */ | 1159 | /* Add NPort <did> to <num> list */ |
1163 | lpfc_printf_log(phba, | 1160 | lpfc_printf_log(phba, |
@@ -1169,46 +1166,38 @@ lpfc_nlp_list(struct lpfc_hba * phba, struct lpfc_nodelist * nlp, int list) | |||
1169 | 1166 | ||
1170 | switch (list) { | 1167 | switch (list) { |
1171 | case NLP_NO_LIST: /* No list, just remove it */ | 1168 | case NLP_NO_LIST: /* No list, just remove it */ |
1169 | spin_unlock_irq(phba->host->host_lock); | ||
1172 | lpfc_nlp_remove(phba, nlp); | 1170 | lpfc_nlp_remove(phba, nlp); |
1171 | spin_lock_irq(phba->host->host_lock); | ||
1173 | /* as node removed - stop further transport calls */ | 1172 | /* as node removed - stop further transport calls */ |
1174 | rport_del = none; | 1173 | rport_del = none; |
1175 | break; | 1174 | break; |
1176 | case NLP_UNUSED_LIST: | 1175 | case NLP_UNUSED_LIST: |
1177 | spin_lock_irq(phba->host->host_lock); | ||
1178 | nlp->nlp_flag |= list; | 1176 | nlp->nlp_flag |= list; |
1179 | spin_unlock_irq(phba->host->host_lock); | ||
1180 | /* Put it at the end of the unused list */ | 1177 | /* Put it at the end of the unused list */ |
1181 | list_add_tail(&nlp->nlp_listp, &phba->fc_unused_list); | 1178 | list_add_tail(&nlp->nlp_listp, &phba->fc_unused_list); |
1182 | phba->fc_unused_cnt++; | 1179 | phba->fc_unused_cnt++; |
1183 | break; | 1180 | break; |
1184 | case NLP_PLOGI_LIST: | 1181 | case NLP_PLOGI_LIST: |
1185 | spin_lock_irq(phba->host->host_lock); | ||
1186 | nlp->nlp_flag |= list; | 1182 | nlp->nlp_flag |= list; |
1187 | spin_unlock_irq(phba->host->host_lock); | ||
1188 | /* Put it at the end of the plogi list */ | 1183 | /* Put it at the end of the plogi list */ |
1189 | list_add_tail(&nlp->nlp_listp, &phba->fc_plogi_list); | 1184 | list_add_tail(&nlp->nlp_listp, &phba->fc_plogi_list); |
1190 | phba->fc_plogi_cnt++; | 1185 | phba->fc_plogi_cnt++; |
1191 | break; | 1186 | break; |
1192 | case NLP_ADISC_LIST: | 1187 | case NLP_ADISC_LIST: |
1193 | spin_lock_irq(phba->host->host_lock); | ||
1194 | nlp->nlp_flag |= list; | 1188 | nlp->nlp_flag |= list; |
1195 | spin_unlock_irq(phba->host->host_lock); | ||
1196 | /* Put it at the end of the adisc list */ | 1189 | /* Put it at the end of the adisc list */ |
1197 | list_add_tail(&nlp->nlp_listp, &phba->fc_adisc_list); | 1190 | list_add_tail(&nlp->nlp_listp, &phba->fc_adisc_list); |
1198 | phba->fc_adisc_cnt++; | 1191 | phba->fc_adisc_cnt++; |
1199 | break; | 1192 | break; |
1200 | case NLP_REGLOGIN_LIST: | 1193 | case NLP_REGLOGIN_LIST: |
1201 | spin_lock_irq(phba->host->host_lock); | ||
1202 | nlp->nlp_flag |= list; | 1194 | nlp->nlp_flag |= list; |
1203 | spin_unlock_irq(phba->host->host_lock); | ||
1204 | /* Put it at the end of the reglogin list */ | 1195 | /* Put it at the end of the reglogin list */ |
1205 | list_add_tail(&nlp->nlp_listp, &phba->fc_reglogin_list); | 1196 | list_add_tail(&nlp->nlp_listp, &phba->fc_reglogin_list); |
1206 | phba->fc_reglogin_cnt++; | 1197 | phba->fc_reglogin_cnt++; |
1207 | break; | 1198 | break; |
1208 | case NLP_PRLI_LIST: | 1199 | case NLP_PRLI_LIST: |
1209 | spin_lock_irq(phba->host->host_lock); | ||
1210 | nlp->nlp_flag |= list; | 1200 | nlp->nlp_flag |= list; |
1211 | spin_unlock_irq(phba->host->host_lock); | ||
1212 | /* Put it at the end of the prli list */ | 1201 | /* Put it at the end of the prli list */ |
1213 | list_add_tail(&nlp->nlp_listp, &phba->fc_prli_list); | 1202 | list_add_tail(&nlp->nlp_listp, &phba->fc_prli_list); |
1214 | phba->fc_prli_cnt++; | 1203 | phba->fc_prli_cnt++; |
@@ -1217,19 +1206,17 @@ lpfc_nlp_list(struct lpfc_hba * phba, struct lpfc_nodelist * nlp, int list) | |||
1217 | rport_add = unmapped; | 1206 | rport_add = unmapped; |
1218 | /* ensure all vestiges of "mapped" significance are gone */ | 1207 | /* ensure all vestiges of "mapped" significance are gone */ |
1219 | nlp->nlp_type &= ~(NLP_FCP_TARGET | NLP_FCP_INITIATOR); | 1208 | nlp->nlp_type &= ~(NLP_FCP_TARGET | NLP_FCP_INITIATOR); |
1220 | spin_lock_irq(phba->host->host_lock); | ||
1221 | nlp->nlp_flag |= list; | 1209 | nlp->nlp_flag |= list; |
1222 | spin_unlock_irq(phba->host->host_lock); | ||
1223 | /* Put it at the end of the unmap list */ | 1210 | /* Put it at the end of the unmap list */ |
1224 | list_add_tail(&nlp->nlp_listp, &phba->fc_nlpunmap_list); | 1211 | list_add_tail(&nlp->nlp_listp, &phba->fc_nlpunmap_list); |
1225 | phba->fc_unmap_cnt++; | 1212 | phba->fc_unmap_cnt++; |
1226 | phba->nport_event_cnt++; | 1213 | phba->nport_event_cnt++; |
1227 | /* stop nodev tmo if running */ | 1214 | /* stop nodev tmo if running */ |
1228 | if (nlp->nlp_flag & NLP_NODEV_TMO) { | 1215 | if (nlp->nlp_flag & NLP_NODEV_TMO) { |
1229 | spin_lock_irq(phba->host->host_lock); | ||
1230 | nlp->nlp_flag &= ~NLP_NODEV_TMO; | 1216 | nlp->nlp_flag &= ~NLP_NODEV_TMO; |
1231 | spin_unlock_irq(phba->host->host_lock); | 1217 | spin_unlock_irq(phba->host->host_lock); |
1232 | del_timer_sync(&nlp->nlp_tmofunc); | 1218 | del_timer_sync(&nlp->nlp_tmofunc); |
1219 | spin_lock_irq(phba->host->host_lock); | ||
1233 | if (!list_empty(&nlp->nodev_timeout_evt.evt_listp)) | 1220 | if (!list_empty(&nlp->nodev_timeout_evt.evt_listp)) |
1234 | list_del_init(&nlp->nodev_timeout_evt. | 1221 | list_del_init(&nlp->nodev_timeout_evt. |
1235 | evt_listp); | 1222 | evt_listp); |
@@ -1239,9 +1226,7 @@ lpfc_nlp_list(struct lpfc_hba * phba, struct lpfc_nodelist * nlp, int list) | |||
1239 | break; | 1226 | break; |
1240 | case NLP_MAPPED_LIST: | 1227 | case NLP_MAPPED_LIST: |
1241 | rport_add = mapped; | 1228 | rport_add = mapped; |
1242 | spin_lock_irq(phba->host->host_lock); | ||
1243 | nlp->nlp_flag |= list; | 1229 | nlp->nlp_flag |= list; |
1244 | spin_unlock_irq(phba->host->host_lock); | ||
1245 | /* Put it at the end of the map list */ | 1230 | /* Put it at the end of the map list */ |
1246 | list_add_tail(&nlp->nlp_listp, &phba->fc_nlpmap_list); | 1231 | list_add_tail(&nlp->nlp_listp, &phba->fc_nlpmap_list); |
1247 | phba->fc_map_cnt++; | 1232 | phba->fc_map_cnt++; |
@@ -1249,7 +1234,9 @@ lpfc_nlp_list(struct lpfc_hba * phba, struct lpfc_nodelist * nlp, int list) | |||
1249 | /* stop nodev tmo if running */ | 1234 | /* stop nodev tmo if running */ |
1250 | if (nlp->nlp_flag & NLP_NODEV_TMO) { | 1235 | if (nlp->nlp_flag & NLP_NODEV_TMO) { |
1251 | nlp->nlp_flag &= ~NLP_NODEV_TMO; | 1236 | nlp->nlp_flag &= ~NLP_NODEV_TMO; |
1237 | spin_unlock_irq(phba->host->host_lock); | ||
1252 | del_timer_sync(&nlp->nlp_tmofunc); | 1238 | del_timer_sync(&nlp->nlp_tmofunc); |
1239 | spin_lock_irq(phba->host->host_lock); | ||
1253 | if (!list_empty(&nlp->nodev_timeout_evt.evt_listp)) | 1240 | if (!list_empty(&nlp->nodev_timeout_evt.evt_listp)) |
1254 | list_del_init(&nlp->nodev_timeout_evt. | 1241 | list_del_init(&nlp->nodev_timeout_evt. |
1255 | evt_listp); | 1242 | evt_listp); |
@@ -1257,9 +1244,7 @@ lpfc_nlp_list(struct lpfc_hba * phba, struct lpfc_nodelist * nlp, int list) | |||
1257 | } | 1244 | } |
1258 | break; | 1245 | break; |
1259 | case NLP_NPR_LIST: | 1246 | case NLP_NPR_LIST: |
1260 | spin_lock_irq(phba->host->host_lock); | ||
1261 | nlp->nlp_flag |= list; | 1247 | nlp->nlp_flag |= list; |
1262 | spin_unlock_irq(phba->host->host_lock); | ||
1263 | /* Put it at the end of the npr list */ | 1248 | /* Put it at the end of the npr list */ |
1264 | list_add_tail(&nlp->nlp_listp, &phba->fc_npr_list); | 1249 | list_add_tail(&nlp->nlp_listp, &phba->fc_npr_list); |
1265 | phba->fc_npr_cnt++; | 1250 | phba->fc_npr_cnt++; |
@@ -1268,15 +1253,15 @@ lpfc_nlp_list(struct lpfc_hba * phba, struct lpfc_nodelist * nlp, int list) | |||
1268 | mod_timer(&nlp->nlp_tmofunc, | 1253 | mod_timer(&nlp->nlp_tmofunc, |
1269 | jiffies + HZ * phba->cfg_nodev_tmo); | 1254 | jiffies + HZ * phba->cfg_nodev_tmo); |
1270 | 1255 | ||
1271 | spin_lock_irq(phba->host->host_lock); | ||
1272 | nlp->nlp_flag |= NLP_NODEV_TMO; | 1256 | nlp->nlp_flag |= NLP_NODEV_TMO; |
1273 | nlp->nlp_flag &= ~NLP_RCV_PLOGI; | 1257 | nlp->nlp_flag &= ~NLP_RCV_PLOGI; |
1274 | spin_unlock_irq(phba->host->host_lock); | ||
1275 | break; | 1258 | break; |
1276 | case NLP_JUST_DQ: | 1259 | case NLP_JUST_DQ: |
1277 | break; | 1260 | break; |
1278 | } | 1261 | } |
1279 | 1262 | ||
1263 | spin_unlock_irq(phba->host->host_lock); | ||
1264 | |||
1280 | /* | 1265 | /* |
1281 | * We make all the calls into the transport after we have | 1266 | * We make all the calls into the transport after we have |
1282 | * moved the node between lists. This so that we don't | 1267 | * moved the node between lists. This so that we don't |
@@ -1681,6 +1666,7 @@ lpfc_findnode_did(struct lpfc_hba * phba, uint32_t order, uint32_t did) | |||
1681 | struct lpfc_nodelist *ndlp, *next_ndlp; | 1666 | struct lpfc_nodelist *ndlp, *next_ndlp; |
1682 | uint32_t data1; | 1667 | uint32_t data1; |
1683 | 1668 | ||
1669 | spin_lock_irq(phba->host->host_lock); | ||
1684 | if (order & NLP_SEARCH_UNMAPPED) { | 1670 | if (order & NLP_SEARCH_UNMAPPED) { |
1685 | list_for_each_entry_safe(ndlp, next_ndlp, | 1671 | list_for_each_entry_safe(ndlp, next_ndlp, |
1686 | &phba->fc_nlpunmap_list, nlp_listp) { | 1672 | &phba->fc_nlpunmap_list, nlp_listp) { |
@@ -1696,6 +1682,7 @@ lpfc_findnode_did(struct lpfc_hba * phba, uint32_t order, uint32_t did) | |||
1696 | phba->brd_no, | 1682 | phba->brd_no, |
1697 | ndlp, ndlp->nlp_DID, | 1683 | ndlp, ndlp->nlp_DID, |
1698 | ndlp->nlp_flag, data1); | 1684 | ndlp->nlp_flag, data1); |
1685 | spin_unlock_irq(phba->host->host_lock); | ||
1699 | return ndlp; | 1686 | return ndlp; |
1700 | } | 1687 | } |
1701 | } | 1688 | } |
@@ -1717,6 +1704,7 @@ lpfc_findnode_did(struct lpfc_hba * phba, uint32_t order, uint32_t did) | |||
1717 | phba->brd_no, | 1704 | phba->brd_no, |
1718 | ndlp, ndlp->nlp_DID, | 1705 | ndlp, ndlp->nlp_DID, |
1719 | ndlp->nlp_flag, data1); | 1706 | ndlp->nlp_flag, data1); |
1707 | spin_unlock_irq(phba->host->host_lock); | ||
1720 | return ndlp; | 1708 | return ndlp; |
1721 | } | 1709 | } |
1722 | } | 1710 | } |
@@ -1739,6 +1727,7 @@ lpfc_findnode_did(struct lpfc_hba * phba, uint32_t order, uint32_t did) | |||
1739 | phba->brd_no, | 1727 | phba->brd_no, |
1740 | ndlp, ndlp->nlp_DID, | 1728 | ndlp, ndlp->nlp_DID, |
1741 | ndlp->nlp_flag, data1); | 1729 | ndlp->nlp_flag, data1); |
1730 | spin_unlock_irq(phba->host->host_lock); | ||
1742 | return ndlp; | 1731 | return ndlp; |
1743 | } | 1732 | } |
1744 | } | 1733 | } |
@@ -1783,6 +1772,7 @@ lpfc_findnode_did(struct lpfc_hba * phba, uint32_t order, uint32_t did) | |||
1783 | phba->brd_no, | 1772 | phba->brd_no, |
1784 | ndlp, ndlp->nlp_DID, | 1773 | ndlp, ndlp->nlp_DID, |
1785 | ndlp->nlp_flag, data1); | 1774 | ndlp->nlp_flag, data1); |
1775 | spin_unlock_irq(phba->host->host_lock); | ||
1786 | return ndlp; | 1776 | return ndlp; |
1787 | } | 1777 | } |
1788 | } | 1778 | } |
@@ -1827,6 +1817,7 @@ lpfc_findnode_did(struct lpfc_hba * phba, uint32_t order, uint32_t did) | |||
1827 | phba->brd_no, | 1817 | phba->brd_no, |
1828 | ndlp, ndlp->nlp_DID, | 1818 | ndlp, ndlp->nlp_DID, |
1829 | ndlp->nlp_flag, data1); | 1819 | ndlp->nlp_flag, data1); |
1820 | spin_unlock_irq(phba->host->host_lock); | ||
1830 | return ndlp; | 1821 | return ndlp; |
1831 | } | 1822 | } |
1832 | } | 1823 | } |
@@ -1849,11 +1840,14 @@ lpfc_findnode_did(struct lpfc_hba * phba, uint32_t order, uint32_t did) | |||
1849 | phba->brd_no, | 1840 | phba->brd_no, |
1850 | ndlp, ndlp->nlp_DID, | 1841 | ndlp, ndlp->nlp_DID, |
1851 | ndlp->nlp_flag, data1); | 1842 | ndlp->nlp_flag, data1); |
1843 | spin_unlock_irq(phba->host->host_lock); | ||
1852 | return ndlp; | 1844 | return ndlp; |
1853 | } | 1845 | } |
1854 | } | 1846 | } |
1855 | } | 1847 | } |
1856 | 1848 | ||
1849 | spin_unlock_irq(phba->host->host_lock); | ||
1850 | |||
1857 | /* FIND node did <did> NOT FOUND */ | 1851 | /* FIND node did <did> NOT FOUND */ |
1858 | lpfc_printf_log(phba, | 1852 | lpfc_printf_log(phba, |
1859 | KERN_INFO, | 1853 | KERN_INFO, |
@@ -1895,9 +1889,7 @@ lpfc_setup_disc_node(struct lpfc_hba * phba, uint32_t did) | |||
1895 | */ | 1889 | */ |
1896 | if (ndlp->nlp_flag & NLP_DELAY_TMO) { | 1890 | if (ndlp->nlp_flag & NLP_DELAY_TMO) { |
1897 | ndlp->nlp_flag &= ~NLP_DELAY_TMO; | 1891 | ndlp->nlp_flag &= ~NLP_DELAY_TMO; |
1898 | spin_unlock_irq(phba->host->host_lock); | ||
1899 | del_timer_sync(&ndlp->nlp_delayfunc); | 1892 | del_timer_sync(&ndlp->nlp_delayfunc); |
1900 | spin_lock_irq(phba->host->host_lock); | ||
1901 | if (!list_empty(&ndlp->els_retry_evt. | 1893 | if (!list_empty(&ndlp->els_retry_evt. |
1902 | evt_listp)) | 1894 | evt_listp)) |
1903 | list_del_init(&ndlp->els_retry_evt. | 1895 | list_del_init(&ndlp->els_retry_evt. |
@@ -2513,11 +2505,14 @@ lpfc_findnode_rpi(struct lpfc_hba * phba, uint16_t rpi) | |||
2513 | &phba->fc_reglogin_list}; | 2505 | &phba->fc_reglogin_list}; |
2514 | int i; | 2506 | int i; |
2515 | 2507 | ||
2508 | spin_lock_irq(phba->host->host_lock); | ||
2516 | for (i = 0; i < ARRAY_SIZE(lists); i++ ) | 2509 | for (i = 0; i < ARRAY_SIZE(lists); i++ ) |
2517 | list_for_each_entry(ndlp, lists[i], nlp_listp) | 2510 | list_for_each_entry(ndlp, lists[i], nlp_listp) |
2518 | if (ndlp->nlp_rpi == rpi) | 2511 | if (ndlp->nlp_rpi == rpi) { |
2512 | spin_unlock_irq(phba->host->host_lock); | ||
2519 | return ndlp; | 2513 | return ndlp; |
2520 | 2514 | } | |
2515 | spin_unlock_irq(phba->host->host_lock); | ||
2521 | return NULL; | 2516 | return NULL; |
2522 | } | 2517 | } |
2523 | 2518 | ||