Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

795 lines
21 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. sh_put.c
  5. Abstract:
  6. This source file contains those functions of the Stream Head Driver that
  7. deal with sending messages down a stream.
  8. It is based on the SpiderSTREAMS source, stremul\msgsrvr.c.
  9. Author:
  10. Eric Chin (ericc) August 16, 1991
  11. Revision History:
  12. Notes:
  13. The write error state of a stream is represented by ms->e_werror. Once
  14. set, this is never reset. This corresponds to the STREAMS semantics as
  15. defined by AT&T. Once a user is notified of a write error on a stream,
  16. about the only recourse is to close the stream.
  17. --*/
  18. #include "sh_inc.h"
  19. /*
  20. * Private Functions
  21. */
  22. STATIC VOID
  23. cancel_put(
  24. IN PDEVICE_OBJECT device,
  25. IN PIRP irp
  26. );
  27. STATIC NTSTATUS
  28. do_putmsg(
  29. IN PIRP irp,
  30. IN BOOLEAN from_queue,
  31. IN int *spl_levelp,
  32. OUT BOOLEAN *pmore
  33. );
  34. STATIC queue_t *
  35. handle_to_queue (
  36. IN HANDLE handle
  37. );
  38. NTSTATUS
  39. SHDispFdInsert(
  40. IN PIRP irp,
  41. IN PIO_STACK_LOCATION irpsp
  42. )
  43. /*++
  44. Routine Description:
  45. This routine is called to put a message down a stream. It is based on
  46. the SpiderStreams emulator's msgserver() routine.
  47. This routine merely peels open the IRP, checks the putmsg() arguments
  48. for consistency, locks the appropriate stream, and then calls
  49. do_putmsg(), which does the bulk of the work.
  50. Arguments:
  51. irp - pointer to the IRP representing this request
  52. irpsp - pointer to the IRP stack location for this request
  53. Return Value:
  54. An NT status code. Whatever the return value, this function will arrange
  55. for the IRP to be completed.
  56. --*/
  57. {
  58. int spl_level;
  59. queue_t *iq = NULL;
  60. PSTREAM_ENDPOINT ms;
  61. PPUTMSG_ARGS_IN inbuf;
  62. struct strbuf *ctrlptr, *dataptr;
  63. ASSERT((irpsp->Parameters.DeviceIoControl.IoControlCode & 0x3) ==
  64. METHOD_BUFFERED);
  65. ms = (STREAM_ENDPOINT *) irpsp->FileObject->FsContext;
  66. if (irpsp->Parameters.DeviceIoControl.InputBufferLength <
  67. sizeof(PUTMSG_ARGS_IN) - 1) {
  68. IF_STRMDBG(TERSE) {
  69. STRMTRACE(("SHEAD: SHDispFdInsert(%lx) insufficient nbytes = %lx\n",
  70. irp, irpsp->Parameters.DeviceIoControl.InputBufferLength));
  71. }
  72. shortreply(irp, STATUS_INVALID_PARAMETER, 0);
  73. return(STATUS_INVALID_PARAMETER);
  74. }
  75. //
  76. // the caller marshalled the input arguments contiguously thus:
  77. //
  78. // typedef struct _PUTMSG_ARGS_IN_ {
  79. // int a_iocode; // I_FDINSERT
  80. // long a_flags; // 0 | RS_HIPRI
  81. // struct strbuf a_ctlbuf; // (required)
  82. // struct strbuf a_databuf; // (required)
  83. // HANDLE a_insert.i_fildes; // (required)
  84. // int a_offset; // (required)
  85. // char a_stuff[1]; // s_ctlbuf.buf (required)
  86. // // s_databuf.buf (optional)
  87. // } PUTMSG_ARGS_IN, *PPUTMSG_ARGS_IN;
  88. //
  89. // When the message has no data part, the caller must have set
  90. // a_databuf.len = -1 !!
  91. //
  92. //
  93. inbuf = (PPUTMSG_ARGS_IN) irp->AssociatedIrp.SystemBuffer;
  94. ctrlptr = &(inbuf->a_ctlbuf);
  95. dataptr = &(inbuf->a_databuf);
  96. IF_STRMDBG(CALL) {
  97. STRMTRACE(("SHEAD: SHDispFdInsert(flags, clen, dlen = %d, %lx, %lx)\n",
  98. inbuf->a_flags, ctrlptr->len, dataptr->len));
  99. }
  100. if (((inbuf->a_flags != 0) && (inbuf->a_flags != RS_HIPRI)) ||
  101. (ctrlptr->len <= 0)) {
  102. SHpGenReply(irp, -1, EINVAL);
  103. return(STATUS_SUCCESS);
  104. }
  105. if ((inbuf->a_offset < 0) ||
  106. (inbuf->a_offset % sizeof(char *)) ||
  107. (ctrlptr->len < (signed) (inbuf->a_offset + sizeof(char *)))) {
  108. SHpGenReply(irp, -1, EINVAL);
  109. return(STATUS_SUCCESS);
  110. }
  111. if (inbuf->a_insert.i_fildes &&
  112. (inbuf->a_insert.i_fildes != INVALID_HANDLE_VALUE)) {
  113. iq = handle_to_queue(inbuf->a_insert.i_fildes);
  114. }
  115. if (!iq) {
  116. SHpGenReply(irp, -1, EINVAL);
  117. return(STATUS_SUCCESS);
  118. }
  119. inbuf->a_insert.i_targetq = iq; // this is a union !!
  120. //
  121. // if no data part is to be sent, specify it unambiguously for
  122. // irptomp()'s sake,
  123. //
  124. if (dataptr->len == 0) {
  125. dataptr->len = -1;
  126. }
  127. spl_level = lock_strm(ms->e_strm);
  128. if (shrange(ms->e_strm, ctrlptr->len, dataptr->len) < 0) {
  129. unlock_strm(ms->e_strm, spl_level);
  130. SHpGenReply(irp, -1, ERANGE);
  131. return(STATUS_SUCCESS);
  132. }
  133. /*
  134. * do_putmsg() has, or will arrange to, complete the IRP.
  135. */
  136. do_putmsg(irp, FALSE, &spl_level, NULL);
  137. IF_STRMDBG(CALL) {
  138. STRMTRACE(("SHEAD: SHDispFdInsert(ms = %lx) completed ok\n", ms));
  139. }
  140. return(STATUS_PENDING);
  141. } // SHDispFdInsert
  142. NTSTATUS
  143. SHDispPutMsg(
  144. IN PIRP irp,
  145. IN PIO_STACK_LOCATION irpsp
  146. )
  147. /*++
  148. Routine Description:
  149. This routine is called to put a message down a stream. It is based on
  150. the SpiderStreams emulator's msgserver() routine.
  151. This routine merely peels open the IRP, checks the putmsg() arguments
  152. for consistency, locks the appropriate stream, and then calls
  153. do_putmsg(), which does the bulk of the work.
  154. Arguments:
  155. irp - pointer to the IRP representing this request
  156. irpsp - pointer to the IRP stack location for this request
  157. Return Value:
  158. An NT status code. Whatever the return value, this function will arrange
  159. for the IRP to be completed.
  160. --*/
  161. {
  162. int spl_level;
  163. PSTREAM_ENDPOINT ms;
  164. PPUTMSG_ARGS_IN inbuf;
  165. struct strbuf *ctrlptr, *dataptr;
  166. ASSERT((irpsp->Parameters.DeviceIoControl.IoControlCode & 0x3) ==
  167. METHOD_BUFFERED);
  168. ms = (STREAM_ENDPOINT *) irpsp->FileObject->FsContext;
  169. if (irpsp->Parameters.DeviceIoControl.InputBufferLength <
  170. sizeof(PUTMSG_ARGS_IN) - 1) {
  171. IF_STRMDBG(TERSE) {
  172. STRMTRACE(("SHEAD: SHDispPutMsg(%lx) insufficient nbytes = %lx\n",
  173. irp, irpsp->Parameters.DeviceIoControl.InputBufferLength));
  174. }
  175. shortreply(irp, STATUS_INVALID_PARAMETER, 0);
  176. return(STATUS_INVALID_PARAMETER);
  177. }
  178. //
  179. // the caller marshalled the input arguments contiguously thus:
  180. //
  181. // typedef struct _PUTMSG_ARGS_IN_ {
  182. // int a_iocode; // 0
  183. // long a_flags; // 0 | RS_HIPRI
  184. // struct strbuf a_ctlbuf; // (required)
  185. // struct strbuf a_databuf; // (required)
  186. // HANDLE a_fildes; // -1
  187. // int a_offset; // 0
  188. // char a_stuff[1]; // s_ctlbuf.buf (optional)
  189. // // s_databuf.buf (optional)
  190. // } PUTMSG_ARGS_IN, *PPUTMSG_ARGS_IN;
  191. //
  192. // When the message has no control part, the caller must have set
  193. // a_ctlbuf.len = -1 !! Ditto for a_databuf.len.
  194. //
  195. //
  196. inbuf = (PPUTMSG_ARGS_IN) irp->AssociatedIrp.SystemBuffer;
  197. ctrlptr = &(inbuf->a_ctlbuf);
  198. dataptr = &(inbuf->a_databuf);
  199. ASSERT(inbuf->a_insert.i_fildes == INVALID_HANDLE_VALUE);
  200. inbuf->a_insert.i_targetq = NULL;
  201. IF_STRMDBG(CALL) {
  202. STRMTRACE(("SHEAD: SHDispPutMsg(flags, clen, dlen = %d, %lx, %lx)\n",
  203. inbuf->a_flags, ctrlptr->len, dataptr->len));
  204. }
  205. switch (inbuf->a_flags) {
  206. case 0:
  207. if ((ctrlptr->len < 0) && (dataptr->len < 0)) {
  208. SHpGenReply(irp, 0, 0);
  209. return(STATUS_SUCCESS);
  210. }
  211. break;
  212. case RS_HIPRI:
  213. if (ctrlptr->len >= 0) {
  214. break;
  215. }
  216. /* fall through */
  217. default:
  218. SHpGenReply(irp, -1, EINVAL);
  219. return(STATUS_SUCCESS);
  220. }
  221. spl_level = lock_strm(ms->e_strm);
  222. /*
  223. * ms->e_wropt may be changed by ioctl(I_SWROPT). However, the current
  224. * state of ms->e_wropt applies to this put operation.
  225. */
  226. if ((ctrlptr->len <= 0) &&
  227. (dataptr->len <= 0) &&
  228. !(ms->e_wropt & SNDZERO)) {
  229. unlock_strm(ms->e_strm, spl_level);
  230. SHpGenReply(irp, 0, 0);
  231. return(STATUS_SUCCESS);
  232. }
  233. if (shrange(ms->e_strm, ctrlptr->len, dataptr->len) < 0) {
  234. unlock_strm(ms->e_strm, spl_level);
  235. SHpGenReply(irp, -1, ERANGE);
  236. return(STATUS_SUCCESS);
  237. }
  238. /*
  239. * do_putmsg() has, or will arrange to, complete the IRP.
  240. */
  241. do_putmsg(irp, FALSE, &spl_level, NULL);
  242. IF_STRMDBG(CALL) {
  243. STRMTRACE(("SHEAD: SHDispPutMsg(ms = %lx) completed ok\n", ms));
  244. }
  245. return(STATUS_PENDING);
  246. } // SHDispPutMsg
  247. STATIC VOID
  248. cancel_put(
  249. IN PDEVICE_OBJECT device,
  250. IN PIRP irp
  251. )
  252. /*++
  253. Routine Description:
  254. This routine is called when an put operation on a stream is cancelled.
  255. It must release the cancel spinlock before returning !! The caller
  256. has already acquired the cancel spinlock. ref: IoCancelIrp().
  257. Arguments:
  258. device - pointer to the device object
  259. irp - pointer to the irp of this request
  260. Return Value:
  261. none.
  262. --*/
  263. {
  264. int spl_level;
  265. PLIST_ENTRY tmp;
  266. PWAITING_IRP item;
  267. PSTREAM_ENDPOINT ms;
  268. PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation(irp);
  269. ASSERT(device == (PDEVICE_OBJECT) StreamDevice);
  270. ASSERT(irpsp->MajorFunction == IRP_MJ_DEVICE_CONTROL);
  271. ASSERT(irpsp->Parameters.DeviceIoControl.IoControlCode ==
  272. IOCTL_STREAMS_PUTMSG);
  273. IF_STRMDBG(CALL) {
  274. STRMTRACE(("SHEAD: cancel_put(irp = %lx) entered\n", irp));
  275. }
  276. IoSetCancelRoutine(irp, NULL); /* unnecessary, but cheap */
  277. IoReleaseCancelSpinLock(irp->CancelIrql);
  278. ms = (PSTREAM_ENDPOINT) irpsp->FileObject->FsContext;
  279. spl_level = lock_strm(ms->e_strm);
  280. for (tmp = ms->e_writers.Flink; tmp != &ms->e_writers; tmp = tmp->Flink) {
  281. item = CONTAINING_RECORD(tmp,
  282. WAITING_IRP,
  283. w_list);
  284. if (irp != item->w_irp) {
  285. continue;
  286. }
  287. RemoveEntryList(&(item->w_list));
  288. unlock_strm(ms->e_strm, spl_level);
  289. ExFreePool(item);
  290. shortreply(irp, STATUS_CANCELLED, 0);
  291. IF_STRMDBG(CALL) {
  292. STRMTRACE(("SHEAD: cancel_put(irp = %lx) cancelled ok\n", irp));
  293. }
  294. return;
  295. }
  296. unlock_strm(ms->e_strm, spl_level);
  297. IF_STRMDBG(CALL) {
  298. STRMTRACE(("SHEAD: cancel_put(irp = %lx) not found\n", irp));
  299. }
  300. } // cancel_put
  301. STATIC NTSTATUS
  302. do_putmsg(
  303. IN PIRP irp,
  304. IN BOOLEAN from_queue,
  305. IN int *spl_levelp,
  306. OUT BOOLEAN *pmore OPTIONAL
  307. )
  308. /*++
  309. Routine Description:
  310. This function is called to put a message down a stream. It either
  311. completes the irp or chains it to ms->e_writers. In any case,
  312. once this function is called, it will arrange for the IRP to be
  313. completed.
  314. Call this function with the stream locked !!!
  315. This function is based on the SpiderStreams emulator's function, do_req().
  316. Arguments:
  317. irp - pointer to the IRP representing this request
  318. from_queue - TRUE if this is an IRP just unchained from ms->e_writers
  319. spl_levelp - pointer to the interrupt priority level at which the
  320. stream was locked
  321. pmore - if not the stream is not flow-controlled when this
  322. function returns, this will be TRUE. Otherwise, it
  323. will be set to false.
  324. *pmore is basically set to the value of !canput():
  325. This is to accommodate the logic in shwsrv(), the primary caller of this
  326. function. shwsrv() is most interested in the state of stream's write
  327. queue: should it call this function again ?
  328. Return Value:
  329. an NT status code.
  330. --*/
  331. {
  332. mblk_t *mp;
  333. queue_t *iq;
  334. NTSTATUS status;
  335. int MyErrno, flags;
  336. STREAM_ENDPOINT *ms;
  337. PPUTMSG_ARGS_IN inbuf;
  338. PIO_STACK_LOCATION irpsp;
  339. struct strbuf *ctrlptr, *dataptr;
  340. IF_STRMDBG(CALL) {
  341. STRMTRACE(("SHEAD: do_putmsg(irp = %lx) entered\n", irp));
  342. }
  343. irpsp = IoGetCurrentIrpStackLocation(irp);
  344. ms = (STREAM_ENDPOINT *) irpsp->FileObject->FsContext;
  345. //
  346. // this was already verified by SHDispFdInsert() or SHDispPutMsg().
  347. //
  348. ASSERT(irpsp->Parameters.DeviceIoControl.InputBufferLength >=
  349. sizeof(PUTMSG_ARGS_IN) - 1);
  350. //
  351. // the caller marshalled the input arguments contiguously thus:
  352. //
  353. // typedef struct _PUTMSG_ARGS_IN_ {
  354. // int a_iocode; // I_FDINSERT or 0
  355. // long a_flags; // 0 or RS_HIPRI
  356. // struct strbuf a_ctlbuf; // (required)
  357. // struct strbuf a_databuf; // (required)
  358. // struct queue *a_insert.i_targetq; // (optional)
  359. // int a_offset; // (optional)
  360. // char a_stuff[1]; // s_ctlbuf.buf (optional)
  361. // // s_databuf.buf (optional)
  362. // } PUTMSG_ARGS_IN, *PPUTMSG_ARGS_IN;
  363. //
  364. // When the message has no control part, the caller must have set
  365. // ctrlbuf->len = -1 !! Ditto for databuf->len.
  366. //
  367. inbuf = (PPUTMSG_ARGS_IN) irp->AssociatedIrp.SystemBuffer;
  368. flags = inbuf->a_flags;
  369. ctrlptr = &(inbuf->a_ctlbuf);
  370. dataptr = &(inbuf->a_databuf);
  371. IF_STRMDBG(VERBOSE) {
  372. STRMTRACE(("SHEAD: do_putmsg(flags, clen, dlen = %d, %lx, %lx)\n",
  373. flags, ctrlptr->len, dataptr->len));
  374. }
  375. if (pmore) {
  376. *pmore = TRUE;
  377. }
  378. if (ms->e_werror) {
  379. MyErrno = ms->e_werror;
  380. }
  381. else if (ms->e_linked) {
  382. MyErrno = EINVAL;
  383. }
  384. else {
  385. MyErrno = 0;
  386. }
  387. if (MyErrno) {
  388. IF_STRMDBG(TERSE) {
  389. STRMTRACE(("SHEAD: do_putmsg(%lx) error = %d\n", ms, MyErrno));
  390. }
  391. unlock_strm(ms->e_strm, *spl_levelp);
  392. SHpGenReply(irp, -1, MyErrno);
  393. return(STATUS_SUCCESS);
  394. }
  395. //
  396. // if downstream flow control is being exerted, enqueue this request in
  397. // the waiting list of writers.
  398. //
  399. // High-priority messages are not subject to flow control.
  400. //
  401. // check whether this stream is nonblocking !!!
  402. //
  403. //
  404. if ((flags != RS_HIPRI) &&
  405. (!IsListEmpty( &ms->e_writers ) && !from_queue) ||
  406. shblocked(ms->e_strm)) {
  407. IF_STRMDBG(VERBOSE) {
  408. STRMTRACE((
  409. "SHEAD: do_putmsg(irp = %lx) flow-ctrl, (!%x && !%x) || %x\n", irp,
  410. IsListEmpty( &ms->e_writers ), from_queue, shblocked(ms->e_strm)));
  411. }
  412. if (pmore) {
  413. *pmore = FALSE;
  414. }
  415. IoMarkIrpPending(irp);
  416. unlock_strm(ms->e_strm, *spl_levelp);
  417. IoAcquireCancelSpinLock(&irp->CancelIrql);
  418. if (irp->Cancel) {
  419. IoReleaseCancelSpinLock(irp->CancelIrql);
  420. shortreply(irp, STATUS_CANCELLED, 0);
  421. return(STATUS_CANCELLED);
  422. }
  423. *spl_levelp = lock_strm(ms->e_strm);
  424. status = SHAddPendingIrp(
  425. &(ms->e_writers),
  426. from_queue,
  427. irp,
  428. do_putmsg);
  429. unlock_strm(ms->e_strm, *spl_levelp);
  430. if (status != STATUS_SUCCESS) {
  431. IoReleaseCancelSpinLock(irp->CancelIrql);
  432. shortreply(irp, status, 0);
  433. return(status);
  434. }
  435. IoSetCancelRoutine(irp, cancel_put);
  436. IoReleaseCancelSpinLock(irp->CancelIrql);
  437. return(STATUS_PENDING);
  438. }
  439. mp = irptomp(irp, BPRI_LO, ctrlptr->len, dataptr->len, inbuf->a_stuff);
  440. if (!mp) {
  441. unlock_strm(ms->e_strm, *spl_levelp);
  442. shortreply(irp, STATUS_NO_MEMORY, 0);
  443. return(STATUS_NO_MEMORY);
  444. }
  445. //
  446. // Both SHDispFdInsert() and SHDispPutMsg() verified that if RS_HIPRI
  447. // was set, a control part exists.
  448. //
  449. if (flags == RS_HIPRI) {
  450. ASSERT(mp->b_datap->db_type == M_PROTO);
  451. mp->b_datap->db_type = M_PCPROTO;
  452. }
  453. //
  454. // The stream of a_insert.i_targetq should be locked in
  455. // SHDispFdInsert(), and unlocked after the shput() ?
  456. //
  457. if (inbuf->a_insert.i_targetq) {
  458. iq = inbuf->a_insert.i_targetq;
  459. //
  460. // Spider's do_req() has the line below before the while loop:
  461. //
  462. // iq = WR(iq);
  463. //
  464. // This is incorrect because:
  465. //
  466. // a) iq already points to the write queue,
  467. // b) it causes iq to be traversed upstream, instead of downstream.
  468. //
  469. while (iq->q_next) {
  470. iq = iq->q_next;
  471. }
  472. * ((queue_t **) (mp->b_rptr + inbuf->a_offset)) = RD(iq);
  473. }
  474. //
  475. // since irptomp() made a copy, we can complete the putmsg() now.
  476. //
  477. SHpGenReply(irp, 0, 0);
  478. //
  479. // shput() does the unlock_strm(ms->e_strm) for us.
  480. //
  481. shput(ms->e_strm, mp, 0, spl_levelp);
  482. IF_STRMDBG(CALL) {
  483. STRMTRACE(("SHEAD: do_putmsg(irp = %lx) completed ok\n", irp));
  484. }
  485. return(STATUS_SUCCESS);
  486. } // do_putmsg
  487. STATIC queue_t *
  488. handle_to_queue (
  489. IN HANDLE handle
  490. )
  491. /*++
  492. Routine Description:
  493. This routine returns a pointer to a queue structure, given the NT handle
  494. of its stream. It is based on the SpiderStreams Emulator function,
  495. fd_to_queue().
  496. Do not call this function at DISPATCH_LEVEL !! ObReferenceObjectByHandle()
  497. must be called from either LOW_LEVEL or APC_LEVEL.
  498. Arguments:
  499. handle - handle relevant only in the current process' context
  500. Return Value:
  501. pointer to the queue structure associated with that handle
  502. --*/
  503. {
  504. NTSTATUS status;
  505. STREAM_ENDPOINT *ms;
  506. PFILE_OBJECT pfileobj;
  507. IF_STRMDBG(CALL) {
  508. STRMTRACE(("SHEAD: handle_to_queue(%lx) \n", handle));
  509. }
  510. status = ObReferenceObjectByHandle(
  511. handle, // Handle
  512. FILE_READ_DATA, // DesiredAccess
  513. *IoFileObjectType, // ObjectType
  514. KernelMode, // AccessMode
  515. (PVOID *) &pfileobj, // *object
  516. NULL // HandleInformation
  517. );
  518. if (!NT_SUCCESS(status) ||
  519. !((STREAM_ENDPOINT *) pfileobj->FsContext) ||
  520. !((STREAM_ENDPOINT *) pfileobj->FsContext)->e_strm) {
  521. return((queue_t *) NULL);
  522. }
  523. ms = (STREAM_ENDPOINT *) pfileobj->FsContext;
  524. ObDereferenceObject(pfileobj);
  525. return(ms->e_strm->str_sq->q_next);
  526. } // handle_to_queue
  527. void
  528. shwsrv(
  529. IN struct msg_strm *ms
  530. )
  531. /*++
  532. Routine Description:
  533. This function is called from two places: from the Stream Head's write
  534. service procedure, headwsrv(), and when the Stream Head driver's
  535. bufcall() and esbbcall() are triggered.
  536. It is based on the SpiderStreams function of the same name.
  537. Arguments:
  538. ms - pointer to the stream endpoint
  539. Return Value:
  540. none.
  541. --*/
  542. {
  543. PIRP irp;
  544. int spl_level;
  545. BOOLEAN carryon;
  546. PLIST_ENTRY tmp;
  547. PWAITING_IRP item;
  548. START_FUNCTION function;
  549. PTPI_OBJECT ObjectPtr;
  550. PTPI_CONNECTION_OBJECT ConnectionPtr;
  551. //
  552. // ensure that the stream has not gone away.
  553. //
  554. if ( !(ms->e_strm) ) {
  555. IF_STRMDBG(TERSE) {
  556. STRMTRACE(("SHEAD: shwsrv(%lx) called on null stream\n", ms));
  557. }
  558. return;
  559. }
  560. spl_level = lock_strm(ms->e_strm);
  561. //
  562. // handle anyone waiting to put a message down this stream.
  563. //
  564. while (!IsListEmpty( &(ms->e_writers) )) {
  565. tmp = RemoveHeadList( &(ms->e_writers) );
  566. item = CONTAINING_RECORD(tmp,
  567. WAITING_IRP,
  568. w_list);
  569. irp = item->w_irp;
  570. function = item->w_function;
  571. ExFreePool(item);
  572. carryon = TRUE;
  573. (void) (*function)(irp, TRUE, &spl_level, &carryon);
  574. if (!carryon) {
  575. return;
  576. }
  577. spl_level = lock_strm(ms->e_strm);
  578. }
  579. if (ms->e_strm_flags & POLLOUT) {
  580. ms->e_strm_flags &= ~POLLOUT;
  581. KeReleaseSemaphore(
  582. &Poll_fired, // semaphore
  583. SEMAPHORE_INCREMENT, // priority increment
  584. 1, // adjustment
  585. FALSE // wait
  586. );
  587. }
  588. //
  589. // If this is a TdiStream and it has become unblocked, I need to do a
  590. // SEND_POSSIBLE indication to the user.
  591. //
  592. if ((ObjectPtr = ms->TdiStreamPtr) &&
  593. (ObjectPtr->Tag == TPI_CONNECTION_OBJECT_TYPE)) {
  594. ConnectionPtr = &ObjectPtr->Object.TpiConnection;
  595. //
  596. // The Blocked flag is always accessed under the StreamLock
  597. //
  598. if (ConnectionPtr->Blocked) {
  599. ConnectionPtr->Blocked = FALSE;
  600. SHTdiEventSendPossible(ObjectPtr);
  601. }
  602. }
  603. unlock_strm(ms->e_strm, spl_level);
  604. } // shwsrv