ReactOS  0.4.15-dev-5462-g4d0d22a
marea.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1998-2005 ReactOS Team (and the authors from the programmers section)
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  *
19  * PROJECT: ReactOS kernel
20  * FILE: ntoskrnl/mm/marea.c
21  * PURPOSE: Implements memory areas
22  *
23  * PROGRAMMERS: Rex Jolliff
24  * David Welch
25  * Eric Kohl
26  * Philip Susi
27  * Casper Hornstrup
28  * Eric Kohl
29  * Ge van Geldorp
30  * Royce Mitchell III
31  * Aleksey Bragin
32  * Jason Filby
33  * Thomas Weidenmueller
34  * Gunnar Andre' Dalsnes
35  * Mike Nordell
36  * Alex Ionescu
37  * Filip Navara
38  * Herve Poussineau
39  * Steven Edwards
40  */
41 
42 /* INCLUDES *****************************************************************/
43 
44 #include <ntoskrnl.h>
45 #define NDEBUG
46 #include <cache/section/newmm.h>
47 #include <debug.h>
48 
49 #include "ARM3/miarm.h"
50 
53 
56 
57 /* FUNCTIONS *****************************************************************/
58 
62  PVOID Address_)
63 {
64  ULONG_PTR StartVpn = (ULONG_PTR)Address_ / PAGE_SIZE;
70  PMMVAD_LONG Vad;
71 
73  Table = (Process != NULL) ? &Process->VadRoot : &MiRosKernelVadRoot;
74 
75  Result = MiCheckForConflictingNode(StartVpn, StartVpn, Table, &Node);
76  if (Result != TableFoundNode)
77  {
78  return NULL;
79  }
80 
81  Vad = (PMMVAD_LONG)Node;
82  if (Vad->u.VadFlags.Spare == 0)
83  {
84  /* Check if this is VM VAD */
85  if (Vad->ControlArea == NULL)
86  {
87  /* We store the reactos MEMORY_AREA here */
89  }
90  else
91  {
92  /* This is a section VAD. Store the MAREA here for now */
94  }
95  }
96  else
97  {
99  }
100 
101  return MemoryArea;
102 }
103 
105 NTAPI
108  PVOID Address_,
110 {
111  ULONG_PTR StartVpn = (ULONG_PTR)Address_ / PAGE_SIZE;
112  ULONG_PTR EndVpn = ((ULONG_PTR)Address_ + Length - 1) / PAGE_SIZE;
118  PMMVAD_LONG Vad;
119 
121  Table = (Process != NULL) ? &Process->VadRoot : &MiRosKernelVadRoot;
122 
123  Result = MiCheckForConflictingNode(StartVpn, EndVpn, Table, &Node);
124  if (Result != TableFoundNode)
125  {
126  return NULL;
127  }
128 
129  Vad = (PMMVAD_LONG)Node;
130  if (Vad->u.VadFlags.Spare == 0)
131  {
132  /* Check if this is VM VAD */
133  if (Vad->ControlArea == NULL)
134  {
135  /* We store the reactos MEMORY_AREA here */
137  }
138  else
139  {
140  /* This is a section VAD. Store the MAREA here for now */
142  }
143  }
144  else
145  {
147  }
148 
149  ASSERT(MemoryArea != NULL);
150  return MemoryArea;
151 }
152 
153 VOID
154 NTAPI
155 MiInsertVad(IN PMMVAD Vad,
156  IN PMM_AVL_TABLE VadRoot);
157 
158 ULONG
159 NTAPI
162 );
163 
164 
165 static VOID
168  PMEMORY_AREA marea,
169  ULONG Protect)
170 {
172 
173  marea->VadNode.u.VadFlags.Spare = 1;
175 
176  /* Build a lame VAD if this is a user-space allocation */
178  {
179  ASSERT(Process != NULL);
180  if (marea->Type != MEMORY_AREA_OWNED_BY_ARM3)
181  {
182 #ifdef NEWCC
183  ASSERT(marea->Type == MEMORY_AREA_SECTION_VIEW || marea->Type == MEMORY_AREA_CACHE);
184 #else
186 #endif
187 
188  /* Insert the VAD */
190  MiInsertVad(&marea->VadNode, &Process->VadRoot);
192  marea->Vad = &marea->VadNode;
193  }
194  }
195  else
196  {
197  ASSERT(Process == NULL);
198 
200  {
204  }
205 
206  /* Insert the VAD */
210  marea->Vad = NULL;
211  }
212 }
213 
214 PVOID NTAPI
218  ULONG_PTR Granularity,
219  BOOLEAN TopDown)
220 {
222  PMM_AVL_TABLE VadRoot;
225  ULONG_PTR StartingAddress, HighestAddress;
226 
228  VadRoot = Process ? &Process->VadRoot : &MiRosKernelVadRoot;
229  if (TopDown)
230  {
231  /* Find an address top-down */
232  HighestAddress = Process ? (ULONG_PTR)MM_HIGHEST_VAD_ADDRESS : (LONG_PTR)-1;
234  HighestAddress,
235  Granularity,
236  VadRoot,
237  &StartingAddress,
238  &Parent);
239  }
240  else
241  {
243  Granularity,
244  VadRoot,
245  &Parent,
246  &StartingAddress);
247  }
248 
249  if (Result == TableFoundNode)
250  {
251  return NULL;
252  }
253 
254  return (PVOID)StartingAddress;
255 }
256 
257 VOID
258 NTAPI
261 
262 
287  PVOID FreePageContext)
288 {
290  PVOID EndAddress;
291 
292  /* Make sure we own the address space lock! */
293  ASSERT(CONTAINING_RECORD(AddressSpace, EPROCESS, Vm)->AddressCreationLock.Owner == KeGetCurrentThread());
294 
295  /* Check magic */
296  ASSERT(MemoryArea->Magic == 'erAM');
297 
299  {
302 
303  if (Process != NULL &&
305  {
306  KeAttachProcess(&Process->Pcb);
307  }
308 
311  Address < (ULONG_PTR)EndAddress;
312  Address += PAGE_SIZE)
313  {
314  BOOLEAN Dirty = FALSE;
315  SWAPENTRY SwapEntry = 0;
316  PFN_NUMBER Page = 0;
317  BOOLEAN DoFree;
318 
320  {
322  /* We'll have to do some cleanup when we're on the page file */
323  DoFree = TRUE;
324  }
325  else
326  {
327  DoFree = MmDeleteVirtualMapping(Process, (PVOID)Address, &Dirty, &Page);
328  }
329  if (DoFree && (FreePage != NULL))
330  {
331  FreePage(FreePageContext, MemoryArea, (PVOID)Address,
332  Page, SwapEntry, (BOOLEAN)Dirty);
333  }
334  }
335 
336  if (Process != NULL &&
338  {
339  KeDetachProcess();
340  }
341 
342  //if (MemoryArea->VadNode.StartingVpn < (ULONG_PTR)MmSystemRangeStart >> PAGE_SHIFT
343  if (MemoryArea->Vad)
344  {
346 #ifdef NEWCC
347  ASSERT(MemoryArea->Type == MEMORY_AREA_SECTION_VIEW || MemoryArea->Type == MEMORY_AREA_CACHE);
348 #else
350 #endif
351 
352  /* MmCleanProcessAddressSpace might have removed it (and this would be MmDeleteProcessAdressSpace) */
354  if (((PMMVAD)MemoryArea->Vad)->u.VadFlags.Spare == 1)
355  {
357  }
358 
359  MemoryArea->Vad = NULL;
360  }
361  else
362  {
364  }
365  }
366 
367 #if DBG
368  MemoryArea->Magic = 'daeD';
369 #endif
371 
372  DPRINT("MmFreeMemoryArea() succeeded\n");
373 
374  return STATUS_SUCCESS;
375 }
376 
405  ULONG Type,
408  ULONG Protect,
410  ULONG AllocationFlags,
411  ULONG Granularity)
412 {
413  ULONG_PTR tmpLength;
415  ULONG_PTR EndingAddress;
416 
417  DPRINT("MmCreateMemoryArea(Type 0x%lx, BaseAddress %p, "
418  "*BaseAddress %p, Length %p, AllocationFlags %x, "
419  "Result %p)\n",
420  Type, BaseAddress, *BaseAddress, Length, AllocationFlags,
421  Result);
422 
423  /* Is this a static memory area? */
424  if (Type & MEMORY_AREA_STATIC)
425  {
426  /* Use the static array instead of the pool */
429  }
430  else
431  {
432  /* Allocate the memory area from nonpaged pool */
434  sizeof(MEMORY_AREA),
435  TAG_MAREA);
436  }
437 
438  if (!MemoryArea)
439  {
440  DPRINT1("Not enough memory.\n");
441  return STATUS_NO_MEMORY;
442  }
443 
446  MemoryArea->Flags = AllocationFlags;
447  MemoryArea->Magic = 'erAM';
449 
450  if (*BaseAddress == 0)
451  {
452  tmpLength = (ULONG_PTR)MM_ROUND_UP(Length, PAGE_SIZE);
454  tmpLength,
455  Granularity,
456  (AllocationFlags & MEM_TOP_DOWN) == MEM_TOP_DOWN);
457  if ((*BaseAddress) == 0)
458  {
459  DPRINT("No suitable gap\n");
461  return STATUS_NO_MEMORY;
462  }
463 
465  MemoryArea->VadNode.EndingVpn = ((ULONG_PTR)*BaseAddress + tmpLength - 1) >> PAGE_SHIFT;
467  }
468  else
469  {
470  EndingAddress = ((ULONG_PTR)*BaseAddress + Length - 1) | (PAGE_SIZE - 1);
472  tmpLength = EndingAddress + 1 - (ULONG_PTR)*BaseAddress;
473 
475  {
476  ASSERT(FALSE);
479  }
480 
483  {
484  DPRINT("Memory area for user mode address space exceeds MmSystemRangeStart\n");
487  }
488 
489  /* No need to check ARM3 owned memory areas, the range MUST be free */
491  {
493  *BaseAddress,
494  tmpLength) != NULL)
495  {
496  DPRINT("Memory area already occupied\n");
499  }
500  }
501 
503  MemoryArea->VadNode.EndingVpn = ((ULONG_PTR)*BaseAddress + tmpLength - 1) >> PAGE_SHIFT;
505  }
506 
507  *Result = MemoryArea;
508 
509  DPRINT("MmCreateMemoryArea() succeeded (%p)\n", *BaseAddress);
510  return STATUS_SUCCESS;
511 }
512 
513 VOID
514 NTAPI
517  PMMVAD Vad)
518 {
522 
523  /* We must be called from MmCleanupAddressSpace and nowhere else!
524  Make sure things are as expected... */
526  ASSERT(Process->VmDeleted == TRUE);
527  ASSERT(((PsGetCurrentThread()->ThreadsProcess == Process) &&
528  (Process->ActiveThreads == 1)) ||
529  (Process->ActiveThreads == 0));
530 
531  MemoryArea = (PMEMORY_AREA)Vad;
533 
535  {
537  }
538 #ifdef NEWCC
539  else if (MemoryArea->Type == MEMORY_AREA_CACHE)
540  {
542  }
543 #endif
544  else
545  {
546  /* There shouldn't be anything else! */
547  ASSERT(FALSE);
548  }
549 
550  /* Make sure this worked! */
552 }
553 /* EOF */
MM_AVL_TABLE MiRosKernelVadRoot
Definition: marea.c:54
ULONG NTAPI MiMakeProtectionMask(IN ULONG Protect)
Definition: section.c:140
#define PAGE_SHIFT
Definition: env_spec_w32.h:45
#define IN
Definition: typedefs.h:39
ASMGENDATA Table[]
Definition: genincdata.c:61
TABLE_SEARCH_RESULT NTAPI MiFindEmptyAddressRangeDownTree(IN SIZE_T Length, IN ULONG_PTR BoundaryAddress, IN ULONG_PTR Alignment, IN PMM_AVL_TABLE Table, OUT PULONG_PTR Base, OUT PMMADDRESS_NODE *Parent)
Definition: vadnode.c:597
VOID NTAPI MiRemoveNode(IN PMMADDRESS_NODE Node, IN PMM_AVL_TABLE Table)
Definition: vadnode.c:360
_In_ ULONG _In_ ULONG _In_ ULONG Length
Definition: ntddpcm.h:101
ULONG Type
Definition: mm.h:251
struct _MEMORY_AREA * PMEMORY_AREA
FORCEINLINE VOID MiUnlockProcessWorkingSetUnsafe(IN PEPROCESS Process, IN PETHREAD Thread)
Definition: miarm.h:1244
#define PsGetCurrentThread()
Definition: env_spec_w32.h:81
_In_ HANDLE _Outptr_result_bytebuffer_ ViewSize PVOID _In_ ULONG_PTR _In_ SIZE_T _Inout_opt_ PLARGE_INTEGER _Inout_ PSIZE_T _In_ SECTION_INHERIT _In_ ULONG _In_ ULONG Protect
Definition: zwfuncs.h:214
#define TRUE
Definition: types.h:120
_In_ ULONG _In_ PHYSICAL_ADDRESS _Inout_ PULONG AddressSpace
Definition: iofuncs.h:2272
MMVAD_FLAGS VadFlags
Definition: mmtypes.h:760
enum _TABLE_SEARCH_RESULT TABLE_SEARCH_RESULT
LONG NTSTATUS
Definition: precomp.h:26
union _MMADDRESS_NODE::@2567 u1
VOID(* PMM_FREE_PAGE_FUNC)(PVOID Context, PMEMORY_AREA MemoryArea, PVOID Address, PFN_NUMBER Page, SWAPENTRY SwapEntry, BOOLEAN Dirty)
Definition: mm.h:527
ULONG_PTR Unused
Definition: mmtypes.h:661
PCONTROL_AREA ControlArea
Definition: mmtypes.h:762
ULONG_PTR Protection
Definition: mmtypes.h:693
VOID NTAPI MmDeletePageFileMapping(struct _EPROCESS *Process, PVOID Address, SWAPENTRY *SwapEntry)
#define MEMORY_AREA_STATIC
Definition: mm.h:98
MMVAD_FLAGS VadFlags
Definition: mmtypes.h:731
MMADDRESS_NODE BalancedRoot
Definition: mmtypes.h:659
uint32_t ULONG_PTR
Definition: typedefs.h:65
ULONG_PTR EndingVpn
Definition: mmtypes.h:727
ULONG Magic
Definition: mm.h:254
ULONG PFN_NUMBER
Definition: ke.h:9
#define MEMORY_AREA_OWNED_BY_ARM3
Definition: mm.h:97
NTSTATUS(* NTAPI)(IN PFILE_FULL_EA_INFORMATION EaBuffer, IN ULONG EaLength, OUT PULONG ErrorOffset)
Definition: IoEaTest.cpp:117
_In_ PVOID _Out_opt_ BOOLEAN _Out_opt_ PPFN_NUMBER Page
Definition: mm.h:1295
#define FALSE
Definition: types.h:117
union node Node
Definition: types.h:1255
NTSTATUS NTAPI MmCreateMemoryArea(PMMSUPPORT AddressSpace, ULONG Type, PVOID *BaseAddress, ULONG_PTR Length, ULONG Protect, PMEMORY_AREA *Result, ULONG AllocationFlags, ULONG Granularity)
Definition: marea.c:404
#define TAG_MAREA
Definition: tag.h:107
#define PsGetCurrentProcess
Definition: psfuncs.h:17
TABLE_SEARCH_RESULT NTAPI MiFindEmptyAddressRangeInTree(IN SIZE_T Length, IN ULONG_PTR Alignment, IN PMM_AVL_TABLE Table, OUT PMMADDRESS_NODE *PreviousVad, OUT PULONG_PTR Base)
Definition: vadnode.c:502
unsigned char BOOLEAN
MMSUPPORT MmSystemCacheWs
Definition: init.c:55
static WCHAR Address[46]
Definition: ping.c:68
_At_(*)(_In_ PWSK_CLIENT Client, _In_opt_ PUNICODE_STRING NodeName, _In_opt_ PUNICODE_STRING ServiceName, _In_opt_ ULONG NameSpace, _In_opt_ GUID *Provider, _In_opt_ PADDRINFOEXW Hints, _Outptr_ PADDRINFOEXW *Result, _In_opt_ PEPROCESS OwningProcess, _In_opt_ PETHREAD OwningThread, _Inout_ PIRP Irp Result)(Mem)) NTSTATUS(WSKAPI *PFN_WSK_GET_ADDRESS_INFO
Definition: wsk.h:426
PVOID Banked
Definition: mmtypes.h:777
FORCEINLINE VOID MiUnlockWorkingSet(IN PETHREAD Thread, IN PMMSUPPORT WorkingSet)
Definition: miarm.h:1356
void * PVOID
Definition: retypes.h:9
ULONG_PTR StartingVpn
Definition: mmtypes.h:726
#define MEMORY_AREA_SECTION_VIEW
Definition: mm.h:93
TABLE_SEARCH_RESULT NTAPI MiCheckForConflictingNode(IN ULONG_PTR StartVpn, IN ULONG_PTR EndVpn, IN PMM_AVL_TABLE Table, OUT PMMADDRESS_NODE *NodeOrParent)
Definition: vadnode.c:78
PFLT_MESSAGE_WAITER_QUEUE CONTAINING_RECORD(Csq, DEVICE_EXTENSION, IrpQueue)) -> WaiterQ.mLock) _IRQL_raises_(DISPATCH_LEVEL) VOID NTAPI FltpAcquireMessageWaiterLock(_In_ PIO_CSQ Csq, _Out_ PKIRQL Irql)
Definition: Messaging.c:560
#define MM_ROUND_UP(x, s)
Definition: mm.h:128
union _MMVAD::@2569 u
Status
Definition: gdiplustypes.h:24
FORCEINLINE VOID MiLockProcessWorkingSetUnsafe(IN PEPROCESS Process, IN PETHREAD Thread)
Definition: miarm.h:1174
_In_ HANDLE _Outptr_result_bytebuffer_ ViewSize PVOID * BaseAddress
Definition: mmfuncs.h:404
ULONG CurrentProcess
Definition: shell.c:125
VOID NTAPI MmDeleteVirtualMapping(IN PEPROCESS Process, IN PVOID Address, OUT PBOOLEAN WasDirty, OUT PPFN_NUMBER Page)
Definition: page.c:177
#define ASSERT(a)
Definition: mode.c:44
#define NT_SUCCESS(StatCode)
Definition: apphelp.c:32
PMEMORY_AREA NTAPI MmLocateMemoryAreaByAddress(PMMSUPPORT AddressSpace, PVOID Address_)
Definition: marea.c:60
struct _MMVAD_LONG * PMMVAD_LONG
BOOLEAN DeleteInProgress
Definition: mm.h:253
Type
Definition: Type.h:6
#define MA_GetEndingAddress(_MemoryArea)
Definition: mm.h:245
BOOLEAN NTAPI MmIsPageSwapEntry(struct _EPROCESS *Process, PVOID Address)
MMVAD VadNode
Definition: mm.h:249
MEMORY_AREA MiStaticMemoryAreas[MI_STATIC_MEMORY_AREAS]
Definition: marea.c:51
#define ExAllocatePoolWithTag(hernya, size, tag)
Definition: env_spec_w32.h:350
VOID NTAPI KeDetachProcess(VOID)
Definition: procobj.c:621
NTSTATUS NTAPI MmFreeMemoryArea(PMMSUPPORT AddressSpace, PMEMORY_AREA MemoryArea, PMM_FREE_PAGE_FUNC FreePage, PVOID FreePageContext)
Definition: marea.c:283
static VOID MmInsertMemoryArea(PMMSUPPORT AddressSpace, PMEMORY_AREA marea, ULONG Protect)
Definition: marea.c:166
#define PAGE_SIZE
Definition: env_spec_w32.h:49
#define MA_GetStartingAddress(_MemoryArea)
Definition: mm.h:244
NTSTATUS NTAPI MmUnmapViewOfCacheSegment(PMMSUPPORT AddressSpace, PVOID BaseAddress)
VOID NTAPI MiRosCleanupMemoryArea(PEPROCESS Process, PMMVAD Vad)
Definition: marea.c:515
BOOLEAN MiRosKernelVadRootInitialized
Definition: marea.c:55
#define STATUS_ACCESS_VIOLATION
Definition: ntstatus.h:242
VOID NTAPI KeAttachProcess(IN PKPROCESS Process)
Definition: procobj.c:582
ULONG MiStaticMemoryAreaCount
Definition: marea.c:52
ULONG_PTR SWAPENTRY
Definition: mm.h:56
#define STATUS_NO_MEMORY
Definition: ntstatus.h:260
FORCEINLINE PEPROCESS MmGetAddressSpaceOwner(IN PMMSUPPORT AddressSpace)
Definition: mm.h:1673
__int3264 LONG_PTR
Definition: mstsclib_h.h:276
#define NULL
Definition: types.h:112
ACPI_PHYSICAL_ADDRESS ACPI_SIZE BOOLEAN Warn UINT32 *TableIdx UINT32 ACPI_TABLE_HEADER *OutTableHeader ACPI_TABLE_HEADER **OutTable ACPI_HANDLE UINT32 ACPI_WALK_CALLBACK ACPI_WALK_CALLBACK void void **ReturnValue UINT32 ACPI_BUFFER *RetPathPtr ACPI_OBJECT_HANDLER void *Data ACPI_OBJECT_HANDLER void **Data ACPI_STRING ACPI_OBJECT_LIST ACPI_BUFFER *ReturnObjectBuffer ACPI_DEVICE_INFO **ReturnBuffer ACPI_HANDLE Parent
Definition: acpixf.h:732
#define DPRINT1
Definition: precomp.h:8
union _MMVAD_LONG::@2575 u4
#define MM_HIGHEST_VAD_ADDRESS
Definition: mm.h:46
_Must_inspect_result_ _In_ PLARGE_INTEGER _In_ PLARGE_INTEGER _In_ ULONG _In_ PFILE_OBJECT _In_ PVOID Process
Definition: fsrtlfuncs.h:219
#define MI_STATIC_MEMORY_AREAS
Definition: mm.h:90
ULONG_PTR Spare
Definition: mmtypes.h:694
unsigned int ULONG
Definition: retypes.h:1
PMMPTE FirstPrototypePte
Definition: mmtypes.h:763
#define RtlZeroMemory(Destination, Length)
Definition: typedefs.h:262
VOID NTAPI MiInsertVad(IN PMMVAD Vad, IN PMM_AVL_TABLE VadRoot)
Definition: vadnode.c:183
#define ULONG_PTR
Definition: config.h:101
#define STATUS_SUCCESS
Definition: shellext.h:65
FORCEINLINE VOID MiLockWorkingSet(IN PETHREAD Thread, IN PMMSUPPORT WorkingSet)
Definition: miarm.h:1270
#define DPRINT
Definition: sndvol32.h:71
#define KeGetCurrentThread
Definition: hal.h:55
PVOID NTAPI MmFindGap(PMMSUPPORT AddressSpace, ULONG_PTR Length, ULONG_PTR Granularity, BOOLEAN TopDown)
Definition: marea.c:215
PMEMORY_AREA NTAPI MmLocateMemoryAreaByRegion(PMMSUPPORT AddressSpace, PVOID Address_, ULONG_PTR Length)
Definition: marea.c:106
#define MEM_TOP_DOWN
Definition: nt_native.h:1321
#define STATUS_CONFLICTING_ADDRESSES
Definition: ntstatus.h:261
#define ExFreePoolWithTag(_P, _T)
Definition: module.h:1099
#define ALIGN_DOWN_POINTER_BY(ptr, align)
Definition: umtypes.h:82
NTSTATUS NTAPI MiRosUnmapViewOfSection(IN PEPROCESS Process, IN PVOID BaseAddress, IN BOOLEAN SkipDebuggerNotify)
Definition: section.c:3567
struct _MEMORY_AREA * MemoryArea
Definition: newmm.h:37
PVOID Vad
Definition: mm.h:255
#define MmSystemRangeStart
Definition: mm.h:32
ULONG Flags
Definition: mm.h:252
struct _MMADDRESS_NODE * Parent
Definition: mmtypes.h:646
union _MMVAD_LONG::@2572 u
Definition: dlist.c:348