be_iscsi.c 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119
  1. /**
  2. * Copyright (C) 2005 - 2011 Emulex
  3. * All rights reserved.
  4. *
  5. * This program is free software; you can redistribute it and/or
  6. * modify it under the terms of the GNU General Public License version 2
  7. * as published by the Free Software Foundation. The full GNU General
  8. * Public License is included in this distribution in the file called COPYING.
  9. *
  10. * Written by: Jayamohan Kallickal (jayamohan.kallickal@emulex.com)
  11. *
  12. * Contact Information:
  13. * linux-drivers@emulex.com
  14. *
  15. * Emulex
  16. * 3333 Susan Street
  17. * Costa Mesa, CA 92626
  18. */
  19. #include <scsi/libiscsi.h>
  20. #include <scsi/scsi_transport_iscsi.h>
  21. #include <scsi/scsi_transport.h>
  22. #include <scsi/scsi_cmnd.h>
  23. #include <scsi/scsi_device.h>
  24. #include <scsi/scsi_host.h>
  25. #include <scsi/scsi_netlink.h>
  26. #include <net/netlink.h>
  27. #include <scsi/scsi.h>
  28. #include "be_iscsi.h"
  29. extern struct iscsi_transport beiscsi_iscsi_transport;
  30. /**
  31. * beiscsi_session_create - creates a new iscsi session
  32. * @cmds_max: max commands supported
  33. * @qdepth: max queue depth supported
  34. * @initial_cmdsn: initial iscsi CMDSN
  35. */
  36. struct iscsi_cls_session *beiscsi_session_create(struct iscsi_endpoint *ep,
  37. u16 cmds_max,
  38. u16 qdepth,
  39. u32 initial_cmdsn)
  40. {
  41. struct Scsi_Host *shost;
  42. struct beiscsi_endpoint *beiscsi_ep;
  43. struct iscsi_cls_session *cls_session;
  44. struct beiscsi_hba *phba;
  45. struct iscsi_session *sess;
  46. struct beiscsi_session *beiscsi_sess;
  47. struct beiscsi_io_task *io_task;
  48. SE_DEBUG(DBG_LVL_8, "In beiscsi_session_create\n");
  49. if (!ep) {
  50. SE_DEBUG(DBG_LVL_1, "beiscsi_session_create: invalid ep\n");
  51. return NULL;
  52. }
  53. beiscsi_ep = ep->dd_data;
  54. phba = beiscsi_ep->phba;
  55. shost = phba->shost;
  56. if (cmds_max > beiscsi_ep->phba->params.wrbs_per_cxn) {
  57. shost_printk(KERN_ERR, shost, "Cannot handle %d cmds."
  58. "Max cmds per session supported is %d. Using %d. "
  59. "\n", cmds_max,
  60. beiscsi_ep->phba->params.wrbs_per_cxn,
  61. beiscsi_ep->phba->params.wrbs_per_cxn);
  62. cmds_max = beiscsi_ep->phba->params.wrbs_per_cxn;
  63. }
  64. cls_session = iscsi_session_setup(&beiscsi_iscsi_transport,
  65. shost, cmds_max,
  66. sizeof(*beiscsi_sess),
  67. sizeof(*io_task),
  68. initial_cmdsn, ISCSI_MAX_TARGET);
  69. if (!cls_session)
  70. return NULL;
  71. sess = cls_session->dd_data;
  72. beiscsi_sess = sess->dd_data;
  73. beiscsi_sess->bhs_pool = pci_pool_create("beiscsi_bhs_pool",
  74. phba->pcidev,
  75. sizeof(struct be_cmd_bhs),
  76. 64, 0);
  77. if (!beiscsi_sess->bhs_pool)
  78. goto destroy_sess;
  79. return cls_session;
  80. destroy_sess:
  81. iscsi_session_teardown(cls_session);
  82. return NULL;
  83. }
  84. /**
  85. * beiscsi_session_destroy - destroys iscsi session
  86. * @cls_session: pointer to iscsi cls session
  87. *
  88. * Destroys iSCSI session instance and releases
  89. * resources allocated for it.
  90. */
  91. void beiscsi_session_destroy(struct iscsi_cls_session *cls_session)
  92. {
  93. struct iscsi_session *sess = cls_session->dd_data;
  94. struct beiscsi_session *beiscsi_sess = sess->dd_data;
  95. SE_DEBUG(DBG_LVL_8, "In beiscsi_session_destroy\n");
  96. pci_pool_destroy(beiscsi_sess->bhs_pool);
  97. iscsi_session_teardown(cls_session);
  98. }
  99. /**
  100. * beiscsi_conn_create - create an instance of iscsi connection
  101. * @cls_session: ptr to iscsi_cls_session
  102. * @cid: iscsi cid
  103. */
  104. struct iscsi_cls_conn *
  105. beiscsi_conn_create(struct iscsi_cls_session *cls_session, u32 cid)
  106. {
  107. struct beiscsi_hba *phba;
  108. struct Scsi_Host *shost;
  109. struct iscsi_cls_conn *cls_conn;
  110. struct beiscsi_conn *beiscsi_conn;
  111. struct iscsi_conn *conn;
  112. struct iscsi_session *sess;
  113. struct beiscsi_session *beiscsi_sess;
  114. SE_DEBUG(DBG_LVL_8, "In beiscsi_conn_create ,cid"
  115. "from iscsi layer=%d\n", cid);
  116. shost = iscsi_session_to_shost(cls_session);
  117. phba = iscsi_host_priv(shost);
  118. cls_conn = iscsi_conn_setup(cls_session, sizeof(*beiscsi_conn), cid);
  119. if (!cls_conn)
  120. return NULL;
  121. conn = cls_conn->dd_data;
  122. beiscsi_conn = conn->dd_data;
  123. beiscsi_conn->ep = NULL;
  124. beiscsi_conn->phba = phba;
  125. beiscsi_conn->conn = conn;
  126. sess = cls_session->dd_data;
  127. beiscsi_sess = sess->dd_data;
  128. beiscsi_conn->beiscsi_sess = beiscsi_sess;
  129. return cls_conn;
  130. }
  131. /**
  132. * beiscsi_bindconn_cid - Bind the beiscsi_conn with phba connection table
  133. * @beiscsi_conn: The pointer to beiscsi_conn structure
  134. * @phba: The phba instance
  135. * @cid: The cid to free
  136. */
  137. static int beiscsi_bindconn_cid(struct beiscsi_hba *phba,
  138. struct beiscsi_conn *beiscsi_conn,
  139. unsigned int cid)
  140. {
  141. if (phba->conn_table[cid]) {
  142. SE_DEBUG(DBG_LVL_1,
  143. "Connection table already occupied. Detected clash\n");
  144. return -EINVAL;
  145. } else {
  146. SE_DEBUG(DBG_LVL_8, "phba->conn_table[%d]=%p(beiscsi_conn)\n",
  147. cid, beiscsi_conn);
  148. phba->conn_table[cid] = beiscsi_conn;
  149. }
  150. return 0;
  151. }
  152. /**
  153. * beiscsi_conn_bind - Binds iscsi session/connection with TCP connection
  154. * @cls_session: pointer to iscsi cls session
  155. * @cls_conn: pointer to iscsi cls conn
  156. * @transport_fd: EP handle(64 bit)
  157. *
  158. * This function binds the TCP Conn with iSCSI Connection and Session.
  159. */
  160. int beiscsi_conn_bind(struct iscsi_cls_session *cls_session,
  161. struct iscsi_cls_conn *cls_conn,
  162. u64 transport_fd, int is_leading)
  163. {
  164. struct iscsi_conn *conn = cls_conn->dd_data;
  165. struct beiscsi_conn *beiscsi_conn = conn->dd_data;
  166. struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
  167. struct beiscsi_hba *phba = iscsi_host_priv(shost);
  168. struct beiscsi_endpoint *beiscsi_ep;
  169. struct iscsi_endpoint *ep;
  170. SE_DEBUG(DBG_LVL_8, "In beiscsi_conn_bind\n");
  171. ep = iscsi_lookup_endpoint(transport_fd);
  172. if (!ep)
  173. return -EINVAL;
  174. beiscsi_ep = ep->dd_data;
  175. if (iscsi_conn_bind(cls_session, cls_conn, is_leading))
  176. return -EINVAL;
  177. if (beiscsi_ep->phba != phba) {
  178. SE_DEBUG(DBG_LVL_8,
  179. "beiscsi_ep->hba=%p not equal to phba=%p\n",
  180. beiscsi_ep->phba, phba);
  181. return -EEXIST;
  182. }
  183. beiscsi_conn->beiscsi_conn_cid = beiscsi_ep->ep_cid;
  184. beiscsi_conn->ep = beiscsi_ep;
  185. beiscsi_ep->conn = beiscsi_conn;
  186. SE_DEBUG(DBG_LVL_8, "beiscsi_conn=%p conn=%p ep_cid=%d\n",
  187. beiscsi_conn, conn, beiscsi_ep->ep_cid);
  188. return beiscsi_bindconn_cid(phba, beiscsi_conn, beiscsi_ep->ep_cid);
  189. }
  190. static int beiscsi_create_ipv4_iface(struct beiscsi_hba *phba)
  191. {
  192. if (phba->ipv4_iface)
  193. return 0;
  194. phba->ipv4_iface = iscsi_create_iface(phba->shost,
  195. &beiscsi_iscsi_transport,
  196. ISCSI_IFACE_TYPE_IPV4,
  197. 0, 0);
  198. if (!phba->ipv4_iface) {
  199. shost_printk(KERN_ERR, phba->shost, "Could not "
  200. "create default IPv4 address.\n");
  201. return -ENODEV;
  202. }
  203. return 0;
  204. }
  205. static int beiscsi_create_ipv6_iface(struct beiscsi_hba *phba)
  206. {
  207. if (phba->ipv6_iface)
  208. return 0;
  209. phba->ipv6_iface = iscsi_create_iface(phba->shost,
  210. &beiscsi_iscsi_transport,
  211. ISCSI_IFACE_TYPE_IPV6,
  212. 0, 0);
  213. if (!phba->ipv6_iface) {
  214. shost_printk(KERN_ERR, phba->shost, "Could not "
  215. "create default IPv6 address.\n");
  216. return -ENODEV;
  217. }
  218. return 0;
  219. }
  220. void beiscsi_create_def_ifaces(struct beiscsi_hba *phba)
  221. {
  222. struct be_cmd_get_if_info_resp if_info;
  223. if (!mgmt_get_if_info(phba, BE2_IPV4, &if_info))
  224. beiscsi_create_ipv4_iface(phba);
  225. if (!mgmt_get_if_info(phba, BE2_IPV6, &if_info))
  226. beiscsi_create_ipv6_iface(phba);
  227. }
  228. void beiscsi_destroy_def_ifaces(struct beiscsi_hba *phba)
  229. {
  230. if (phba->ipv6_iface)
  231. iscsi_destroy_iface(phba->ipv6_iface);
  232. if (phba->ipv4_iface)
  233. iscsi_destroy_iface(phba->ipv4_iface);
  234. }
  235. static int
  236. beiscsi_set_static_ip(struct Scsi_Host *shost,
  237. struct iscsi_iface_param_info *iface_param,
  238. void *data, uint32_t dt_len)
  239. {
  240. struct beiscsi_hba *phba = iscsi_host_priv(shost);
  241. struct iscsi_iface_param_info *iface_ip = NULL;
  242. struct iscsi_iface_param_info *iface_subnet = NULL;
  243. struct nlattr *nla;
  244. int ret;
  245. switch (iface_param->param) {
  246. case ISCSI_NET_PARAM_IPV4_BOOTPROTO:
  247. nla = nla_find(data, dt_len, ISCSI_NET_PARAM_IPV4_ADDR);
  248. if (nla)
  249. iface_ip = nla_data(nla);
  250. nla = nla_find(data, dt_len, ISCSI_NET_PARAM_IPV4_SUBNET);
  251. if (nla)
  252. iface_subnet = nla_data(nla);
  253. break;
  254. case ISCSI_NET_PARAM_IPV4_ADDR:
  255. iface_ip = iface_param;
  256. nla = nla_find(data, dt_len, ISCSI_NET_PARAM_IPV4_SUBNET);
  257. if (nla)
  258. iface_subnet = nla_data(nla);
  259. break;
  260. case ISCSI_NET_PARAM_IPV4_SUBNET:
  261. iface_subnet = iface_param;
  262. nla = nla_find(data, dt_len, ISCSI_NET_PARAM_IPV4_ADDR);
  263. if (nla)
  264. iface_ip = nla_data(nla);
  265. break;
  266. default:
  267. shost_printk(KERN_ERR, shost, "Unsupported param %d\n",
  268. iface_param->param);
  269. }
  270. if (!iface_ip || !iface_subnet) {
  271. shost_printk(KERN_ERR, shost, "IP and Subnet Mask required\n");
  272. return -EINVAL;
  273. }
  274. ret = mgmt_set_ip(phba, iface_ip, iface_subnet,
  275. ISCSI_BOOTPROTO_STATIC);
  276. return ret;
  277. }
  278. static int
  279. beiscsi_set_ipv4(struct Scsi_Host *shost,
  280. struct iscsi_iface_param_info *iface_param,
  281. void *data, uint32_t dt_len)
  282. {
  283. struct beiscsi_hba *phba = iscsi_host_priv(shost);
  284. int ret = 0;
  285. /* Check the param */
  286. switch (iface_param->param) {
  287. case ISCSI_NET_PARAM_IPV4_GW:
  288. ret = mgmt_set_gateway(phba, iface_param);
  289. break;
  290. case ISCSI_NET_PARAM_IPV4_BOOTPROTO:
  291. if (iface_param->value[0] == ISCSI_BOOTPROTO_DHCP)
  292. ret = mgmt_set_ip(phba, iface_param,
  293. NULL, ISCSI_BOOTPROTO_DHCP);
  294. else if (iface_param->value[0] == ISCSI_BOOTPROTO_STATIC)
  295. ret = beiscsi_set_static_ip(shost, iface_param,
  296. data, dt_len);
  297. else
  298. shost_printk(KERN_ERR, shost, "Invalid BOOTPROTO: %d\n",
  299. iface_param->value[0]);
  300. break;
  301. case ISCSI_NET_PARAM_IFACE_ENABLE:
  302. if (iface_param->value[0] == ISCSI_IFACE_ENABLE)
  303. ret = beiscsi_create_ipv4_iface(phba);
  304. else
  305. iscsi_destroy_iface(phba->ipv4_iface);
  306. break;
  307. case ISCSI_NET_PARAM_IPV4_SUBNET:
  308. case ISCSI_NET_PARAM_IPV4_ADDR:
  309. ret = beiscsi_set_static_ip(shost, iface_param,
  310. data, dt_len);
  311. break;
  312. default:
  313. shost_printk(KERN_ERR, shost, "Param %d not supported\n",
  314. iface_param->param);
  315. }
  316. return ret;
  317. }
  318. static int
  319. beiscsi_set_ipv6(struct Scsi_Host *shost,
  320. struct iscsi_iface_param_info *iface_param,
  321. void *data, uint32_t dt_len)
  322. {
  323. struct beiscsi_hba *phba = iscsi_host_priv(shost);
  324. int ret = 0;
  325. switch (iface_param->param) {
  326. case ISCSI_NET_PARAM_IFACE_ENABLE:
  327. if (iface_param->value[0] == ISCSI_IFACE_ENABLE)
  328. ret = beiscsi_create_ipv6_iface(phba);
  329. else {
  330. iscsi_destroy_iface(phba->ipv6_iface);
  331. ret = 0;
  332. }
  333. break;
  334. case ISCSI_NET_PARAM_IPV6_ADDR:
  335. ret = mgmt_set_ip(phba, iface_param, NULL,
  336. ISCSI_BOOTPROTO_STATIC);
  337. break;
  338. default:
  339. shost_printk(KERN_ERR, shost, "Param %d not supported\n",
  340. iface_param->param);
  341. }
  342. return ret;
  343. }
  344. int be2iscsi_iface_set_param(struct Scsi_Host *shost,
  345. void *data, uint32_t dt_len)
  346. {
  347. struct iscsi_iface_param_info *iface_param = NULL;
  348. struct nlattr *attrib;
  349. uint32_t rm_len = dt_len;
  350. int ret = 0 ;
  351. nla_for_each_attr(attrib, data, dt_len, rm_len) {
  352. iface_param = nla_data(attrib);
  353. if (iface_param->param_type != ISCSI_NET_PARAM)
  354. continue;
  355. /*
  356. * BE2ISCSI only supports 1 interface
  357. */
  358. if (iface_param->iface_num) {
  359. shost_printk(KERN_ERR, shost, "Invalid iface_num %d."
  360. "Only iface_num 0 is supported.\n",
  361. iface_param->iface_num);
  362. return -EINVAL;
  363. }
  364. switch (iface_param->iface_type) {
  365. case ISCSI_IFACE_TYPE_IPV4:
  366. ret = beiscsi_set_ipv4(shost, iface_param,
  367. data, dt_len);
  368. break;
  369. case ISCSI_IFACE_TYPE_IPV6:
  370. ret = beiscsi_set_ipv6(shost, iface_param,
  371. data, dt_len);
  372. break;
  373. default:
  374. shost_printk(KERN_ERR, shost,
  375. "Invalid iface type :%d passed\n",
  376. iface_param->iface_type);
  377. break;
  378. }
  379. if (ret)
  380. return ret;
  381. }
  382. return ret;
  383. }
  384. static int be2iscsi_get_if_param(struct beiscsi_hba *phba,
  385. struct iscsi_iface *iface, int param,
  386. char *buf)
  387. {
  388. struct be_cmd_get_if_info_resp if_info;
  389. int len, ip_type = BE2_IPV4;
  390. memset(&if_info, 0, sizeof(if_info));
  391. if (iface->iface_type == ISCSI_IFACE_TYPE_IPV6)
  392. ip_type = BE2_IPV6;
  393. len = mgmt_get_if_info(phba, ip_type, &if_info);
  394. if (len)
  395. return len;
  396. switch (param) {
  397. case ISCSI_NET_PARAM_IPV4_ADDR:
  398. len = sprintf(buf, "%pI4\n", &if_info.ip_addr.addr);
  399. break;
  400. case ISCSI_NET_PARAM_IPV6_ADDR:
  401. len = sprintf(buf, "%pI6\n", &if_info.ip_addr.addr);
  402. break;
  403. case ISCSI_NET_PARAM_IPV4_BOOTPROTO:
  404. if (!if_info.dhcp_state)
  405. len = sprintf(buf, "static");
  406. else
  407. len = sprintf(buf, "dhcp");
  408. break;
  409. case ISCSI_NET_PARAM_IPV4_SUBNET:
  410. len = sprintf(buf, "%pI4\n", &if_info.ip_addr.subnet_mask);
  411. break;
  412. default:
  413. WARN_ON(1);
  414. }
  415. return len;
  416. }
  417. int be2iscsi_iface_get_param(struct iscsi_iface *iface,
  418. enum iscsi_param_type param_type,
  419. int param, char *buf)
  420. {
  421. struct Scsi_Host *shost = iscsi_iface_to_shost(iface);
  422. struct beiscsi_hba *phba = iscsi_host_priv(shost);
  423. struct be_cmd_get_def_gateway_resp gateway;
  424. int len = -ENOSYS;
  425. switch (param) {
  426. case ISCSI_NET_PARAM_IPV4_ADDR:
  427. case ISCSI_NET_PARAM_IPV4_SUBNET:
  428. case ISCSI_NET_PARAM_IPV4_BOOTPROTO:
  429. case ISCSI_NET_PARAM_IPV6_ADDR:
  430. len = be2iscsi_get_if_param(phba, iface, param, buf);
  431. break;
  432. case ISCSI_NET_PARAM_IFACE_ENABLE:
  433. len = sprintf(buf, "enabled");
  434. break;
  435. case ISCSI_NET_PARAM_IPV4_GW:
  436. memset(&gateway, 0, sizeof(gateway));
  437. len = mgmt_get_gateway(phba, BE2_IPV4, &gateway);
  438. if (!len)
  439. len = sprintf(buf, "%pI4\n", &gateway.ip_addr.addr);
  440. break;
  441. default:
  442. len = -ENOSYS;
  443. }
  444. return len;
  445. }
  446. /**
  447. * beiscsi_ep_get_param - get the iscsi parameter
  448. * @ep: pointer to iscsi ep
  449. * @param: parameter type identifier
  450. * @buf: buffer pointer
  451. *
  452. * returns iscsi parameter
  453. */
  454. int beiscsi_ep_get_param(struct iscsi_endpoint *ep,
  455. enum iscsi_param param, char *buf)
  456. {
  457. struct beiscsi_endpoint *beiscsi_ep = ep->dd_data;
  458. int len = 0;
  459. SE_DEBUG(DBG_LVL_8, "In beiscsi_ep_get_param, param= %d\n", param);
  460. switch (param) {
  461. case ISCSI_PARAM_CONN_PORT:
  462. len = sprintf(buf, "%hu\n", beiscsi_ep->dst_tcpport);
  463. break;
  464. case ISCSI_PARAM_CONN_ADDRESS:
  465. if (beiscsi_ep->ip_type == BE2_IPV4)
  466. len = sprintf(buf, "%pI4\n", &beiscsi_ep->dst_addr);
  467. else
  468. len = sprintf(buf, "%pI6\n", &beiscsi_ep->dst6_addr);
  469. break;
  470. default:
  471. return -ENOSYS;
  472. }
  473. return len;
  474. }
  475. int beiscsi_set_param(struct iscsi_cls_conn *cls_conn,
  476. enum iscsi_param param, char *buf, int buflen)
  477. {
  478. struct iscsi_conn *conn = cls_conn->dd_data;
  479. struct iscsi_session *session = conn->session;
  480. int ret;
  481. SE_DEBUG(DBG_LVL_8, "In beiscsi_conn_set_param, param= %d\n", param);
  482. ret = iscsi_set_param(cls_conn, param, buf, buflen);
  483. if (ret)
  484. return ret;
  485. /*
  486. * If userspace tried to set the value to higher than we can
  487. * support override here.
  488. */
  489. switch (param) {
  490. case ISCSI_PARAM_FIRST_BURST:
  491. if (session->first_burst > 8192)
  492. session->first_burst = 8192;
  493. break;
  494. case ISCSI_PARAM_MAX_RECV_DLENGTH:
  495. if (conn->max_recv_dlength > 65536)
  496. conn->max_recv_dlength = 65536;
  497. break;
  498. case ISCSI_PARAM_MAX_BURST:
  499. if (session->max_burst > 262144)
  500. session->max_burst = 262144;
  501. break;
  502. case ISCSI_PARAM_MAX_XMIT_DLENGTH:
  503. if ((conn->max_xmit_dlength > 65536) ||
  504. (conn->max_xmit_dlength == 0))
  505. conn->max_xmit_dlength = 65536;
  506. default:
  507. return 0;
  508. }
  509. return 0;
  510. }
  511. /**
  512. * beiscsi_get_initname - Read Initiator Name from flash
  513. * @buf: buffer bointer
  514. * @phba: The device priv structure instance
  515. *
  516. * returns number of bytes
  517. */
  518. static int beiscsi_get_initname(char *buf, struct beiscsi_hba *phba)
  519. {
  520. int rc;
  521. unsigned int tag, wrb_num;
  522. unsigned short status, extd_status;
  523. struct be_mcc_wrb *wrb;
  524. struct be_cmd_hba_name *resp;
  525. struct be_queue_info *mccq = &phba->ctrl.mcc_obj.q;
  526. tag = be_cmd_get_initname(phba);
  527. if (!tag) {
  528. SE_DEBUG(DBG_LVL_1, "Getting Initiator Name Failed\n");
  529. return -EBUSY;
  530. } else
  531. wait_event_interruptible(phba->ctrl.mcc_wait[tag],
  532. phba->ctrl.mcc_numtag[tag]);
  533. wrb_num = (phba->ctrl.mcc_numtag[tag] & 0x00FF0000) >> 16;
  534. extd_status = (phba->ctrl.mcc_numtag[tag] & 0x0000FF00) >> 8;
  535. status = phba->ctrl.mcc_numtag[tag] & 0x000000FF;
  536. if (status || extd_status) {
  537. SE_DEBUG(DBG_LVL_1, "MailBox Command Failed with "
  538. "status = %d extd_status = %d\n",
  539. status, extd_status);
  540. free_mcc_tag(&phba->ctrl, tag);
  541. return -EAGAIN;
  542. }
  543. wrb = queue_get_wrb(mccq, wrb_num);
  544. free_mcc_tag(&phba->ctrl, tag);
  545. resp = embedded_payload(wrb);
  546. rc = sprintf(buf, "%s\n", resp->initiator_name);
  547. return rc;
  548. }
  549. /**
  550. * beiscsi_get_host_param - get the iscsi parameter
  551. * @shost: pointer to scsi_host structure
  552. * @param: parameter type identifier
  553. * @buf: buffer pointer
  554. *
  555. * returns host parameter
  556. */
  557. int beiscsi_get_host_param(struct Scsi_Host *shost,
  558. enum iscsi_host_param param, char *buf)
  559. {
  560. struct beiscsi_hba *phba = iscsi_host_priv(shost);
  561. int status = 0;
  562. SE_DEBUG(DBG_LVL_8, "In beiscsi_get_host_param, param= %d\n", param);
  563. switch (param) {
  564. case ISCSI_HOST_PARAM_HWADDRESS:
  565. status = beiscsi_get_macaddr(buf, phba);
  566. if (status < 0) {
  567. SE_DEBUG(DBG_LVL_1, "beiscsi_get_macaddr Failed\n");
  568. return status;
  569. }
  570. break;
  571. case ISCSI_HOST_PARAM_INITIATOR_NAME:
  572. status = beiscsi_get_initname(buf, phba);
  573. if (status < 0) {
  574. SE_DEBUG(DBG_LVL_1,
  575. "Retreiving Initiator Name Failed\n");
  576. return status;
  577. }
  578. break;
  579. default:
  580. return iscsi_host_get_param(shost, param, buf);
  581. }
  582. return status;
  583. }
  584. int beiscsi_get_macaddr(char *buf, struct beiscsi_hba *phba)
  585. {
  586. struct be_cmd_get_nic_conf_resp resp;
  587. int rc;
  588. if (strlen(phba->mac_address))
  589. return strlcpy(buf, phba->mac_address, PAGE_SIZE);
  590. memset(&resp, 0, sizeof(resp));
  591. rc = mgmt_get_nic_conf(phba, &resp);
  592. if (rc)
  593. return rc;
  594. memcpy(phba->mac_address, resp.mac_address, ETH_ALEN);
  595. return sysfs_format_mac(buf, phba->mac_address, ETH_ALEN);
  596. }
  597. /**
  598. * beiscsi_conn_get_stats - get the iscsi stats
  599. * @cls_conn: pointer to iscsi cls conn
  600. * @stats: pointer to iscsi_stats structure
  601. *
  602. * returns iscsi stats
  603. */
  604. void beiscsi_conn_get_stats(struct iscsi_cls_conn *cls_conn,
  605. struct iscsi_stats *stats)
  606. {
  607. struct iscsi_conn *conn = cls_conn->dd_data;
  608. SE_DEBUG(DBG_LVL_8, "In beiscsi_conn_get_stats\n");
  609. stats->txdata_octets = conn->txdata_octets;
  610. stats->rxdata_octets = conn->rxdata_octets;
  611. stats->dataout_pdus = conn->dataout_pdus_cnt;
  612. stats->scsirsp_pdus = conn->scsirsp_pdus_cnt;
  613. stats->scsicmd_pdus = conn->scsicmd_pdus_cnt;
  614. stats->datain_pdus = conn->datain_pdus_cnt;
  615. stats->tmfrsp_pdus = conn->tmfrsp_pdus_cnt;
  616. stats->tmfcmd_pdus = conn->tmfcmd_pdus_cnt;
  617. stats->r2t_pdus = conn->r2t_pdus_cnt;
  618. stats->digest_err = 0;
  619. stats->timeout_err = 0;
  620. stats->custom_length = 0;
  621. strcpy(stats->custom[0].desc, "eh_abort_cnt");
  622. stats->custom[0].value = conn->eh_abort_cnt;
  623. }
  624. /**
  625. * beiscsi_set_params_for_offld - get the parameters for offload
  626. * @beiscsi_conn: pointer to beiscsi_conn
  627. * @params: pointer to offload_params structure
  628. */
  629. static void beiscsi_set_params_for_offld(struct beiscsi_conn *beiscsi_conn,
  630. struct beiscsi_offload_params *params)
  631. {
  632. struct iscsi_conn *conn = beiscsi_conn->conn;
  633. struct iscsi_session *session = conn->session;
  634. AMAP_SET_BITS(struct amap_beiscsi_offload_params, max_burst_length,
  635. params, session->max_burst);
  636. AMAP_SET_BITS(struct amap_beiscsi_offload_params,
  637. max_send_data_segment_length, params,
  638. conn->max_xmit_dlength);
  639. AMAP_SET_BITS(struct amap_beiscsi_offload_params, first_burst_length,
  640. params, session->first_burst);
  641. AMAP_SET_BITS(struct amap_beiscsi_offload_params, erl, params,
  642. session->erl);
  643. AMAP_SET_BITS(struct amap_beiscsi_offload_params, dde, params,
  644. conn->datadgst_en);
  645. AMAP_SET_BITS(struct amap_beiscsi_offload_params, hde, params,
  646. conn->hdrdgst_en);
  647. AMAP_SET_BITS(struct amap_beiscsi_offload_params, ir2t, params,
  648. session->initial_r2t_en);
  649. AMAP_SET_BITS(struct amap_beiscsi_offload_params, imd, params,
  650. session->imm_data_en);
  651. AMAP_SET_BITS(struct amap_beiscsi_offload_params, exp_statsn, params,
  652. (conn->exp_statsn - 1));
  653. }
  654. /**
  655. * beiscsi_conn_start - offload of session to chip
  656. * @cls_conn: pointer to beiscsi_conn
  657. */
  658. int beiscsi_conn_start(struct iscsi_cls_conn *cls_conn)
  659. {
  660. struct iscsi_conn *conn = cls_conn->dd_data;
  661. struct beiscsi_conn *beiscsi_conn = conn->dd_data;
  662. struct beiscsi_endpoint *beiscsi_ep;
  663. struct beiscsi_offload_params params;
  664. SE_DEBUG(DBG_LVL_8, "In beiscsi_conn_start\n");
  665. memset(&params, 0, sizeof(struct beiscsi_offload_params));
  666. beiscsi_ep = beiscsi_conn->ep;
  667. if (!beiscsi_ep)
  668. SE_DEBUG(DBG_LVL_1, "In beiscsi_conn_start , no beiscsi_ep\n");
  669. beiscsi_conn->login_in_progress = 0;
  670. beiscsi_set_params_for_offld(beiscsi_conn, &params);
  671. beiscsi_offload_connection(beiscsi_conn, &params);
  672. iscsi_conn_start(cls_conn);
  673. return 0;
  674. }
  675. /**
  676. * beiscsi_get_cid - Allocate a cid
  677. * @phba: The phba instance
  678. */
  679. static int beiscsi_get_cid(struct beiscsi_hba *phba)
  680. {
  681. unsigned short cid = 0xFFFF;
  682. if (!phba->avlbl_cids)
  683. return cid;
  684. cid = phba->cid_array[phba->cid_alloc++];
  685. if (phba->cid_alloc == phba->params.cxns_per_ctrl)
  686. phba->cid_alloc = 0;
  687. phba->avlbl_cids--;
  688. return cid;
  689. }
  690. /**
  691. * beiscsi_put_cid - Free the cid
  692. * @phba: The phba for which the cid is being freed
  693. * @cid: The cid to free
  694. */
  695. static void beiscsi_put_cid(struct beiscsi_hba *phba, unsigned short cid)
  696. {
  697. phba->avlbl_cids++;
  698. phba->cid_array[phba->cid_free++] = cid;
  699. if (phba->cid_free == phba->params.cxns_per_ctrl)
  700. phba->cid_free = 0;
  701. }
  702. /**
  703. * beiscsi_free_ep - free endpoint
  704. * @ep: pointer to iscsi endpoint structure
  705. */
  706. static void beiscsi_free_ep(struct beiscsi_endpoint *beiscsi_ep)
  707. {
  708. struct beiscsi_hba *phba = beiscsi_ep->phba;
  709. beiscsi_put_cid(phba, beiscsi_ep->ep_cid);
  710. beiscsi_ep->phba = NULL;
  711. }
  712. /**
  713. * beiscsi_open_conn - Ask FW to open a TCP connection
  714. * @ep: endpoint to be used
  715. * @src_addr: The source IP address
  716. * @dst_addr: The Destination IP address
  717. *
  718. * Asks the FW to open a TCP connection
  719. */
  720. static int beiscsi_open_conn(struct iscsi_endpoint *ep,
  721. struct sockaddr *src_addr,
  722. struct sockaddr *dst_addr, int non_blocking)
  723. {
  724. struct beiscsi_endpoint *beiscsi_ep = ep->dd_data;
  725. struct beiscsi_hba *phba = beiscsi_ep->phba;
  726. struct be_queue_info *mccq = &phba->ctrl.mcc_obj.q;
  727. struct be_mcc_wrb *wrb;
  728. struct tcp_connect_and_offload_out *ptcpcnct_out;
  729. unsigned short status, extd_status;
  730. struct be_dma_mem nonemb_cmd;
  731. unsigned int tag, wrb_num;
  732. int ret = -ENOMEM;
  733. SE_DEBUG(DBG_LVL_8, "In beiscsi_open_conn\n");
  734. beiscsi_ep->ep_cid = beiscsi_get_cid(phba);
  735. if (beiscsi_ep->ep_cid == 0xFFFF) {
  736. SE_DEBUG(DBG_LVL_1, "No free cid available\n");
  737. return ret;
  738. }
  739. SE_DEBUG(DBG_LVL_8, "In beiscsi_open_conn, ep_cid=%d\n",
  740. beiscsi_ep->ep_cid);
  741. phba->ep_array[beiscsi_ep->ep_cid -
  742. phba->fw_config.iscsi_cid_start] = ep;
  743. if (beiscsi_ep->ep_cid > (phba->fw_config.iscsi_cid_start +
  744. phba->params.cxns_per_ctrl * 2)) {
  745. SE_DEBUG(DBG_LVL_1, "Failed in allocate iscsi cid\n");
  746. goto free_ep;
  747. }
  748. beiscsi_ep->cid_vld = 0;
  749. nonemb_cmd.va = pci_alloc_consistent(phba->ctrl.pdev,
  750. sizeof(struct tcp_connect_and_offload_in),
  751. &nonemb_cmd.dma);
  752. if (nonemb_cmd.va == NULL) {
  753. SE_DEBUG(DBG_LVL_1,
  754. "Failed to allocate memory for mgmt_open_connection"
  755. "\n");
  756. beiscsi_put_cid(phba, beiscsi_ep->ep_cid);
  757. return -ENOMEM;
  758. }
  759. nonemb_cmd.size = sizeof(struct tcp_connect_and_offload_in);
  760. memset(nonemb_cmd.va, 0, nonemb_cmd.size);
  761. tag = mgmt_open_connection(phba, dst_addr, beiscsi_ep, &nonemb_cmd);
  762. if (!tag) {
  763. SE_DEBUG(DBG_LVL_1,
  764. "mgmt_open_connection Failed for cid=%d\n",
  765. beiscsi_ep->ep_cid);
  766. beiscsi_put_cid(phba, beiscsi_ep->ep_cid);
  767. pci_free_consistent(phba->ctrl.pdev, nonemb_cmd.size,
  768. nonemb_cmd.va, nonemb_cmd.dma);
  769. return -EAGAIN;
  770. } else {
  771. wait_event_interruptible(phba->ctrl.mcc_wait[tag],
  772. phba->ctrl.mcc_numtag[tag]);
  773. }
  774. wrb_num = (phba->ctrl.mcc_numtag[tag] & 0x00FF0000) >> 16;
  775. extd_status = (phba->ctrl.mcc_numtag[tag] & 0x0000FF00) >> 8;
  776. status = phba->ctrl.mcc_numtag[tag] & 0x000000FF;
  777. if (status || extd_status) {
  778. SE_DEBUG(DBG_LVL_1, "mgmt_open_connection Failed"
  779. " status = %d extd_status = %d\n",
  780. status, extd_status);
  781. free_mcc_tag(&phba->ctrl, tag);
  782. pci_free_consistent(phba->ctrl.pdev, nonemb_cmd.size,
  783. nonemb_cmd.va, nonemb_cmd.dma);
  784. goto free_ep;
  785. } else {
  786. wrb = queue_get_wrb(mccq, wrb_num);
  787. free_mcc_tag(&phba->ctrl, tag);
  788. ptcpcnct_out = embedded_payload(wrb);
  789. beiscsi_ep = ep->dd_data;
  790. beiscsi_ep->fw_handle = ptcpcnct_out->connection_handle;
  791. beiscsi_ep->cid_vld = 1;
  792. SE_DEBUG(DBG_LVL_8, "mgmt_open_connection Success\n");
  793. }
  794. pci_free_consistent(phba->ctrl.pdev, nonemb_cmd.size,
  795. nonemb_cmd.va, nonemb_cmd.dma);
  796. return 0;
  797. free_ep:
  798. beiscsi_free_ep(beiscsi_ep);
  799. return -EBUSY;
  800. }
  801. /**
  802. * beiscsi_ep_connect - Ask chip to create TCP Conn
  803. * @scsi_host: Pointer to scsi_host structure
  804. * @dst_addr: The IP address of Target
  805. * @non_blocking: blocking or non-blocking call
  806. *
  807. * This routines first asks chip to create a connection and then allocates an EP
  808. */
  809. struct iscsi_endpoint *
  810. beiscsi_ep_connect(struct Scsi_Host *shost, struct sockaddr *dst_addr,
  811. int non_blocking)
  812. {
  813. struct beiscsi_hba *phba;
  814. struct beiscsi_endpoint *beiscsi_ep;
  815. struct iscsi_endpoint *ep;
  816. int ret;
  817. SE_DEBUG(DBG_LVL_8, "In beiscsi_ep_connect\n");
  818. if (shost)
  819. phba = iscsi_host_priv(shost);
  820. else {
  821. ret = -ENXIO;
  822. SE_DEBUG(DBG_LVL_1, "shost is NULL\n");
  823. return ERR_PTR(ret);
  824. }
  825. if (phba->state != BE_ADAPTER_UP) {
  826. ret = -EBUSY;
  827. SE_DEBUG(DBG_LVL_1, "The Adapter state is Not UP\n");
  828. return ERR_PTR(ret);
  829. }
  830. ep = iscsi_create_endpoint(sizeof(struct beiscsi_endpoint));
  831. if (!ep) {
  832. ret = -ENOMEM;
  833. return ERR_PTR(ret);
  834. }
  835. beiscsi_ep = ep->dd_data;
  836. beiscsi_ep->phba = phba;
  837. beiscsi_ep->openiscsi_ep = ep;
  838. ret = beiscsi_open_conn(ep, NULL, dst_addr, non_blocking);
  839. if (ret) {
  840. SE_DEBUG(DBG_LVL_1, "Failed in beiscsi_open_conn\n");
  841. goto free_ep;
  842. }
  843. return ep;
  844. free_ep:
  845. iscsi_destroy_endpoint(ep);
  846. return ERR_PTR(ret);
  847. }
  848. /**
  849. * beiscsi_ep_poll - Poll to see if connection is established
  850. * @ep: endpoint to be used
  851. * @timeout_ms: timeout specified in millisecs
  852. *
  853. * Poll to see if TCP connection established
  854. */
  855. int beiscsi_ep_poll(struct iscsi_endpoint *ep, int timeout_ms)
  856. {
  857. struct beiscsi_endpoint *beiscsi_ep = ep->dd_data;
  858. SE_DEBUG(DBG_LVL_8, "In beiscsi_ep_poll\n");
  859. if (beiscsi_ep->cid_vld == 1)
  860. return 1;
  861. else
  862. return 0;
  863. }
  864. /**
  865. * beiscsi_close_conn - Upload the connection
  866. * @ep: The iscsi endpoint
  867. * @flag: The type of connection closure
  868. */
  869. static int beiscsi_close_conn(struct beiscsi_endpoint *beiscsi_ep, int flag)
  870. {
  871. int ret = 0;
  872. unsigned int tag;
  873. struct beiscsi_hba *phba = beiscsi_ep->phba;
  874. tag = mgmt_upload_connection(phba, beiscsi_ep->ep_cid, flag);
  875. if (!tag) {
  876. SE_DEBUG(DBG_LVL_8, "upload failed for cid 0x%x\n",
  877. beiscsi_ep->ep_cid);
  878. ret = -EAGAIN;
  879. } else {
  880. wait_event_interruptible(phba->ctrl.mcc_wait[tag],
  881. phba->ctrl.mcc_numtag[tag]);
  882. free_mcc_tag(&phba->ctrl, tag);
  883. }
  884. return ret;
  885. }
  886. /**
  887. * beiscsi_unbind_conn_to_cid - Unbind the beiscsi_conn from phba conn table
  888. * @phba: The phba instance
  889. * @cid: The cid to free
  890. */
  891. static int beiscsi_unbind_conn_to_cid(struct beiscsi_hba *phba,
  892. unsigned int cid)
  893. {
  894. if (phba->conn_table[cid])
  895. phba->conn_table[cid] = NULL;
  896. else {
  897. SE_DEBUG(DBG_LVL_8, "Connection table Not occupied.\n");
  898. return -EINVAL;
  899. }
  900. return 0;
  901. }
  902. /**
  903. * beiscsi_ep_disconnect - Tears down the TCP connection
  904. * @ep: endpoint to be used
  905. *
  906. * Tears down the TCP connection
  907. */
  908. void beiscsi_ep_disconnect(struct iscsi_endpoint *ep)
  909. {
  910. struct beiscsi_conn *beiscsi_conn;
  911. struct beiscsi_endpoint *beiscsi_ep;
  912. struct beiscsi_hba *phba;
  913. unsigned int tag;
  914. unsigned short savecfg_flag = CMD_ISCSI_SESSION_SAVE_CFG_ON_FLASH;
  915. beiscsi_ep = ep->dd_data;
  916. phba = beiscsi_ep->phba;
  917. SE_DEBUG(DBG_LVL_8, "In beiscsi_ep_disconnect for ep_cid = %d\n",
  918. beiscsi_ep->ep_cid);
  919. if (!beiscsi_ep->conn) {
  920. SE_DEBUG(DBG_LVL_8, "In beiscsi_ep_disconnect, no "
  921. "beiscsi_ep\n");
  922. return;
  923. }
  924. beiscsi_conn = beiscsi_ep->conn;
  925. iscsi_suspend_queue(beiscsi_conn->conn);
  926. SE_DEBUG(DBG_LVL_8, "In beiscsi_ep_disconnect ep_cid = %d\n",
  927. beiscsi_ep->ep_cid);
  928. tag = mgmt_invalidate_connection(phba, beiscsi_ep,
  929. beiscsi_ep->ep_cid, 1,
  930. savecfg_flag);
  931. if (!tag) {
  932. SE_DEBUG(DBG_LVL_1,
  933. "mgmt_invalidate_connection Failed for cid=%d\n",
  934. beiscsi_ep->ep_cid);
  935. } else {
  936. wait_event_interruptible(phba->ctrl.mcc_wait[tag],
  937. phba->ctrl.mcc_numtag[tag]);
  938. free_mcc_tag(&phba->ctrl, tag);
  939. }
  940. beiscsi_close_conn(beiscsi_ep, CONNECTION_UPLOAD_GRACEFUL);
  941. beiscsi_free_ep(beiscsi_ep);
  942. beiscsi_unbind_conn_to_cid(phba, beiscsi_ep->ep_cid);
  943. iscsi_destroy_endpoint(beiscsi_ep->openiscsi_ep);
  944. }
  945. umode_t be2iscsi_attr_is_visible(int param_type, int param)
  946. {
  947. switch (param_type) {
  948. case ISCSI_NET_PARAM:
  949. switch (param) {
  950. case ISCSI_NET_PARAM_IFACE_ENABLE:
  951. case ISCSI_NET_PARAM_IPV4_ADDR:
  952. case ISCSI_NET_PARAM_IPV4_SUBNET:
  953. case ISCSI_NET_PARAM_IPV4_BOOTPROTO:
  954. case ISCSI_NET_PARAM_IPV4_GW:
  955. case ISCSI_NET_PARAM_IPV6_ADDR:
  956. return S_IRUGO;
  957. default:
  958. return 0;
  959. }
  960. case ISCSI_HOST_PARAM:
  961. switch (param) {
  962. case ISCSI_HOST_PARAM_HWADDRESS:
  963. return S_IRUGO;
  964. default:
  965. return 0;
  966. }
  967. case ISCSI_PARAM:
  968. switch (param) {
  969. case ISCSI_PARAM_MAX_RECV_DLENGTH:
  970. case ISCSI_PARAM_MAX_XMIT_DLENGTH:
  971. case ISCSI_PARAM_HDRDGST_EN:
  972. case ISCSI_PARAM_DATADGST_EN:
  973. case ISCSI_PARAM_CONN_ADDRESS:
  974. case ISCSI_PARAM_CONN_PORT:
  975. case ISCSI_PARAM_EXP_STATSN:
  976. case ISCSI_PARAM_PERSISTENT_ADDRESS:
  977. case ISCSI_PARAM_PERSISTENT_PORT:
  978. case ISCSI_PARAM_PING_TMO:
  979. case ISCSI_PARAM_RECV_TMO:
  980. case ISCSI_PARAM_INITIAL_R2T_EN:
  981. case ISCSI_PARAM_MAX_R2T:
  982. case ISCSI_PARAM_IMM_DATA_EN:
  983. case ISCSI_PARAM_FIRST_BURST:
  984. case ISCSI_PARAM_MAX_BURST:
  985. case ISCSI_PARAM_PDU_INORDER_EN:
  986. case ISCSI_PARAM_DATASEQ_INORDER_EN:
  987. case ISCSI_PARAM_ERL:
  988. case ISCSI_PARAM_TARGET_NAME:
  989. case ISCSI_PARAM_TPGT:
  990. case ISCSI_PARAM_USERNAME:
  991. case ISCSI_PARAM_PASSWORD:
  992. case ISCSI_PARAM_USERNAME_IN:
  993. case ISCSI_PARAM_PASSWORD_IN:
  994. case ISCSI_PARAM_FAST_ABORT:
  995. case ISCSI_PARAM_ABORT_TMO:
  996. case ISCSI_PARAM_LU_RESET_TMO:
  997. case ISCSI_PARAM_IFACE_NAME:
  998. case ISCSI_PARAM_INITIATOR_NAME:
  999. return S_IRUGO;
  1000. default:
  1001. return 0;
  1002. }
  1003. }
  1004. return 0;
  1005. }