|
1 // Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
2 // All rights reserved. |
|
3 // This component and the accompanying materials are made available |
|
4 // under the terms of the License "Eclipse Public License v1.0" |
|
5 // which accompanies this distribution, and is available |
|
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 // |
|
8 // Initial Contributors: |
|
9 // Nokia Corporation - initial contribution. |
|
10 // |
|
11 // Contributors: |
|
12 // |
|
13 // Description: |
|
14 // e32test\examples\defrag\d_defrag_ref.cpp |
|
15 // Reference LDD for invoking defrag APIs. |
|
16 // |
|
17 // |
|
18 |
|
19 #include <kernel/kern_priv.h> |
|
20 #include "platform.h" |
|
21 #include "nk_priv.h" |
|
22 #include "d_defrag_ref.h" |
|
23 |
|
24 const TInt KMajorVersionNumber=0; |
|
25 const TInt KMinorVersionNumber=1; |
|
26 const TInt KBuildVersionNumber=1; |
|
27 |
|
28 #if 1 // Set true for tracing |
|
29 #define TRACE(x) x |
|
30 #else |
|
31 #define TRACE(x) |
|
32 #endif |
|
33 |
|
34 const TInt KDefragCompleteThreadPriority = 27; |
|
35 const TInt KDefragRamThreadPriority = 1; |
|
36 _LIT(KDefragCompleteThread,"DefragCompleteThread"); |
|
37 |
|
38 class DDefragChannel; |
|
39 |
|
40 /** |
|
41 Clean up item responsible for ensuring all memory commmited to a chunk is |
|
42 freed once the chunk is destroyed |
|
43 */ |
|
44 class TChunkCleanup : public TDfc |
|
45 { |
|
46 public: |
|
47 TChunkCleanup(DDefragChannel* aDevice, TPhysAddr* aBufAddrs, TUint aBufPages); |
|
48 TChunkCleanup(DDefragChannel* aDevice, TPhysAddr aBufBase, TUint aBufBytes); |
|
49 static void ChunkDestroyed(TChunkCleanup* aSelf); |
|
50 void RemoveDevice(); |
|
51 |
|
52 private: |
|
53 void DoChunkDestroyed(); |
|
54 |
|
55 private: |
|
56 TPhysAddr* iBufAddrs; /**< Pointer to an array of the addresses of discontiguous buffer pages*/ |
|
57 TPhysAddr iBufBase; /**< Physical base address of a physically contiguous the buffer*/ |
|
58 TUint iBufSize; /**< The number of pages or bytes in the buffer depending if this is |
|
59 discontiguous or contiguous buffer, repsectively*/ |
|
60 TBool iBufContiguous; /**< ETrue when the memory to be freed is contiguous, EFalse otherwise*/ |
|
61 DDefragChannel* iDevice; /**< The device to be informed when the chunk is destroyed */ |
|
62 }; |
|
63 |
|
64 |
|
65 /** |
|
66 Reference defrag LDD factory. |
|
67 */ |
|
68 class DDefragChannelFactory : public DLogicalDevice |
|
69 { |
|
70 public: |
|
71 DDefragChannelFactory(); |
|
72 ~DDefragChannelFactory(); |
|
73 virtual TInt Install(); //overriding pure virtual |
|
74 virtual void GetCaps(TDes8& aDes) const; //overriding pure virtual |
|
75 virtual TInt Create(DLogicalChannelBase*& aChannel);//overriding pure virtual |
|
76 |
|
77 TDynamicDfcQue* iDfcQ; |
|
78 }; |
|
79 |
|
80 |
|
81 /** |
|
82 Reference defrag logical channel. |
|
83 */ |
|
84 class DDefragChannel : public DLogicalChannelBase |
|
85 { |
|
86 public: |
|
87 DDefragChannel(TDfcQue* aDfcQ); |
|
88 ~DDefragChannel(); |
|
89 void ChunkDestroyed(); |
|
90 protected: |
|
91 virtual TInt DoCreate(TInt aUnit, const TDesC8* anInfo, const TVersion& aVer); |
|
92 virtual TInt Request(TInt aFunction, TAny* a1, TAny* a2); |
|
93 |
|
94 TInt DoAllocLowestZone(); |
|
95 TInt DoClaimLowestZone(); |
|
96 TInt DoChunkClose(); |
|
97 TInt FindLowestPrefZone(); |
|
98 |
|
99 static void DefragCompleteDfc(TAny* aSelf); |
|
100 void DefragComplete(); |
|
101 |
|
102 private: |
|
103 TInt iPageShift; /**< The system's page shift */ |
|
104 DSemaphore* iDefragSemaphore;/**< Semaphore to ensure only one defrag operation is active per channel*/ |
|
105 TClientRequest* iCompleteReq;/**< Pointer to a request status that will signal to the user side client once the defrag has completed*/ |
|
106 DThread* iRequestThread; /**< Pointer to the thread that made the defrag request*/ |
|
107 TRamDefragRequest iDefragReq;/**< The defrag request used to queue defrag operations*/ |
|
108 DChunk* iBufChunk; /**< Pointer to a chunk that can be mapped to a physical RAM area*/ |
|
109 TChunkCleanup* iChunkCleanup;/**< Pointer to iBufChunk's cleanup object */ |
|
110 TDfcQue* iDfcQ; /**< The DFC queue used for driver functions */ |
|
111 TDfc iDefragCompleteDfc; /**< DFC to be queued once a defrag operation has completed */ |
|
112 TBool iDefragDfcFree; /**< Set to fase whenever a dfc defrag operation is still pending*/ |
|
113 TUint iLowestPrefZoneId; /**< The ID of the least preferable RAM zone*/ |
|
114 TUint iLowestPrefZonePages; /**< The number of pages in the least preferable RAM zone*/ |
|
115 TUint iLowestPrefZoneIndex; /**< The test HAL function index of the least preferable RAM zone*/ |
|
116 }; |
|
117 |
|
118 /** |
|
119 Utility functions to wait for chunk clean dfc to be queued by waiting for the |
|
120 idle thread to be queued. |
|
121 */ |
|
122 void signal_sem(TAny* aPtr) |
|
123 { |
|
124 NKern::FSSignal((NFastSemaphore*)aPtr); |
|
125 } |
|
126 |
|
127 TInt WaitForIdle() |
|
128 {// Wait for chunk to be destroyed and then for the chunk cleanup dfc to run. |
|
129 for (TUint i = 0; i < 2; i++) |
|
130 { |
|
131 NFastSemaphore s(0); |
|
132 TDfc idler(&signal_sem, &s, Kern::SvMsgQue(), 0); // supervisor thread, priority 0, so will run after destroyed DFC |
|
133 NTimer timer(&signal_sem, &s); |
|
134 idler.QueueOnIdle(); |
|
135 timer.OneShot(NKern::TimerTicks(5000), ETrue); // runs in DFCThread1 |
|
136 NKern::FSWait(&s); // wait for either idle DFC or timer |
|
137 TBool timeout = idler.Cancel(); // cancel idler, return TRUE if it hadn't run |
|
138 TBool tmc = timer.Cancel(); // cancel timer, return TRUE if it hadn't expired |
|
139 if (!timeout && !tmc) |
|
140 NKern::FSWait(&s); // both the DFC and the timer went off - wait for the second one |
|
141 if (timeout) |
|
142 return KErrTimedOut; |
|
143 } |
|
144 return KErrNone; |
|
145 } |
|
146 |
|
147 /** |
|
148 Standard logical device driver entry point. |
|
149 Called the first time this device driver is loaded. |
|
150 */ |
|
151 DECLARE_STANDARD_LDD() |
|
152 { |
|
153 DDefragChannelFactory* factory = new DDefragChannelFactory; |
|
154 if (factory) |
|
155 { |
|
156 // Allocate a kernel thread to run the DFC |
|
157 TInt r = Kern::DynamicDfcQCreate(factory->iDfcQ, KDefragCompleteThreadPriority, KDefragCompleteThread); |
|
158 |
|
159 if (r != KErrNone) |
|
160 { |
|
161 // Must close rather than delete factory as it is a DObject object. |
|
162 factory->AsyncClose(); |
|
163 return NULL; |
|
164 } |
|
165 } |
|
166 return factory; |
|
167 } |
|
168 |
|
169 |
|
170 /** |
|
171 Constructor |
|
172 */ |
|
173 DDefragChannelFactory::DDefragChannelFactory() |
|
174 { |
|
175 iVersion=TVersion(KMajorVersionNumber,KMinorVersionNumber,KBuildVersionNumber); |
|
176 } |
|
177 |
|
178 |
|
179 /** |
|
180 Destructor |
|
181 */ |
|
182 DDefragChannelFactory::~DDefragChannelFactory() |
|
183 { |
|
184 if (iDfcQ != NULL) |
|
185 {// Destroy the DFC queue created when this device drvier was loaded. |
|
186 iDfcQ->Destroy(); |
|
187 } |
|
188 } |
|
189 |
|
190 |
|
191 /** |
|
192 Create a new DDefragChannel on this logical device. |
|
193 |
|
194 @param aChannel On successful return this will point to the new channel. |
|
195 @return KErrNone on success or KErrNoMemory if the channel couldn't be created. |
|
196 */ |
|
197 TInt DDefragChannelFactory::Create(DLogicalChannelBase*& aChannel) |
|
198 { |
|
199 aChannel = new DDefragChannel(iDfcQ); |
|
200 return (aChannel)? KErrNone : KErrNoMemory; |
|
201 } |
|
202 |
|
203 |
|
204 /** |
|
205 Install the LDD - overriding pure virtual |
|
206 |
|
207 @return KErrNone on success or one of the system wide error codes. |
|
208 */ |
|
209 TInt DDefragChannelFactory::Install() |
|
210 { |
|
211 return SetName(&KLddName); |
|
212 } |
|
213 |
|
214 |
|
215 /** |
|
216 Get capabilities - overriding pure virtual |
|
217 |
|
218 @param aDes A descriptor to be loaded with the capabilities. |
|
219 */ |
|
220 void DDefragChannelFactory::GetCaps(TDes8& aDes) const |
|
221 { |
|
222 TCapsDefragTestV01 b; |
|
223 b.iVersion=TVersion(KMajorVersionNumber,KMinorVersionNumber,KBuildVersionNumber); |
|
224 Kern::InfoCopy(aDes,(TUint8*)&b,sizeof(b)); |
|
225 } |
|
226 |
|
227 |
|
228 /** |
|
229 Constructor |
|
230 |
|
231 @param aDfcQ The DFC queue to use for defrag completion DFCs. |
|
232 */ |
|
233 DDefragChannel::DDefragChannel(TDfcQue* aDfcQ) |
|
234 : |
|
235 iDefragSemaphore(NULL), |
|
236 iCompleteReq(NULL), |
|
237 iBufChunk(NULL), |
|
238 iChunkCleanup(NULL), |
|
239 iDfcQ(aDfcQ), |
|
240 iDefragCompleteDfc(DefragCompleteDfc, (TAny*)this, 1) // DFC is priority '1', it is the only type of dfc on this queue. |
|
241 { |
|
242 } |
|
243 |
|
244 |
|
245 /** |
|
246 Create channel. |
|
247 |
|
248 @param aVer The version number required. |
|
249 @return KErrNone on success, KErrNotSupported if the device doesn't support defragmentation. |
|
250 */ |
|
251 TInt DDefragChannel::DoCreate(TInt /*aUnit*/, const TDesC8* /*anInfo*/, const TVersion& aVer) |
|
252 { |
|
253 // Check the client has ECapabilityPowerMgmt capability. |
|
254 if(!Kern::CurrentThreadHasCapability(ECapabilityPowerMgmt, __PLATSEC_DIAGNOSTIC_STRING("Checked by DDefragChannel"))) |
|
255 { |
|
256 return KErrPermissionDenied; |
|
257 } |
|
258 TInt pageSize; |
|
259 TInt r = Kern::HalFunction(EHalGroupKernel, EKernelHalPageSizeInBytes, &pageSize, 0); |
|
260 if (r != KErrNone) |
|
261 { |
|
262 TRACE(Kern::Printf("ERROR - Unable to determine page size")); |
|
263 return r; |
|
264 } |
|
265 TUint32 pageMask = pageSize; |
|
266 TUint i = 0; |
|
267 for (; i < 32; i++) |
|
268 { |
|
269 if (pageMask & 1) |
|
270 { |
|
271 if (pageMask & ~1u) |
|
272 { |
|
273 TRACE(Kern::Printf("ERROR - page size not a power of 2")); |
|
274 return KErrNotSupported; |
|
275 } |
|
276 iPageShift = i; |
|
277 break; |
|
278 } |
|
279 pageMask >>= 1; |
|
280 } |
|
281 |
|
282 // Check the client is a supported version. |
|
283 if (!Kern::QueryVersionSupported(TVersion(KMajorVersionNumber,KMinorVersionNumber,KBuildVersionNumber),aVer)) |
|
284 { |
|
285 return KErrNotSupported; |
|
286 } |
|
287 |
|
288 // Check this system has more than one RAM zone defined. |
|
289 // A real driver shouldn't need to do this as any driver that uses defrag should |
|
290 // only be loaded on devices that support it. |
|
291 TInt ret = FindLowestPrefZone(); |
|
292 if (ret != KErrNone) |
|
293 {// Only one zone so can't move pages anywhere or empty a zone |
|
294 return KErrNotSupported; |
|
295 } |
|
296 |
|
297 // Create a semaphore to protect defrag invocation. OK to just use one name as |
|
298 // the semaphore is not global so it's name doesn't need to be unique. |
|
299 ret = Kern::SemaphoreCreate(iDefragSemaphore, _L("DefragRefSem"), 1); |
|
300 if (ret != KErrNone) |
|
301 { |
|
302 return ret; |
|
303 } |
|
304 |
|
305 // Create a client request for completing dfc defrag requests. |
|
306 ret = Kern::CreateClientRequest(iCompleteReq); |
|
307 if (ret != KErrNone) |
|
308 { |
|
309 iDefragSemaphore->Close(NULL); |
|
310 return ret; |
|
311 } |
|
312 |
|
313 // Setup a DFC to be invoked when a defrag operation completes. |
|
314 iDefragCompleteDfc.SetDfcQ(iDfcQ); |
|
315 iDefragDfcFree = ETrue; |
|
316 |
|
317 return KErrNone; |
|
318 } |
|
319 |
|
320 |
|
321 /** |
|
322 Destructor |
|
323 */ |
|
324 DDefragChannel::~DDefragChannel() |
|
325 { |
|
326 // Clean up any heap objects. |
|
327 if (iDefragSemaphore != NULL) |
|
328 { |
|
329 iDefragSemaphore->Close(NULL); |
|
330 } |
|
331 |
|
332 // Unregister from any chunk cleanup object as we are to be deleted. |
|
333 if (iChunkCleanup != NULL) |
|
334 { |
|
335 iChunkCleanup->RemoveDevice(); |
|
336 } |
|
337 // Clean up any client request object. |
|
338 if (iCompleteReq) |
|
339 { |
|
340 Kern::DestroyClientRequest(iCompleteReq); |
|
341 } |
|
342 // Free any existing chunk. |
|
343 DoChunkClose(); |
|
344 } |
|
345 |
|
346 |
|
347 /** |
|
348 Handle the requests for this channel. |
|
349 |
|
350 @param aFunction The operation the LDD should perform. |
|
351 @param a1 The first argument for the operation. |
|
352 @param a2 The second argument for the operation. |
|
353 @return KErrNone on success or one of the system wide error codes. |
|
354 */ |
|
355 TInt DDefragChannel::Request(TInt aFunction, TAny* a1, TAny* a2) |
|
356 { |
|
357 TInt r = KErrNone; |
|
358 NKern::ThreadEnterCS(); |
|
359 |
|
360 Kern::SemaphoreWait(*iDefragSemaphore); |
|
361 if (!iDefragDfcFree && aFunction != RDefragChannel::EControlGeneralDefragDfcComplete) |
|
362 {// Only allow a single defrag operation at a time. |
|
363 r = KErrInUse; |
|
364 goto exit; |
|
365 } |
|
366 |
|
367 switch (aFunction) |
|
368 { |
|
369 case RDefragChannel::EControlGeneralDefragDfc: |
|
370 // Queue a defrag operation so that on completion it queues a |
|
371 // DFC on this driver. |
|
372 iRequestThread = &Kern::CurrentThread(); |
|
373 iRequestThread->Open(); |
|
374 |
|
375 // Open a reference on this channel to stop the destructor running before |
|
376 // the defrag request has completed. |
|
377 Open(); |
|
378 r = iCompleteReq->SetStatus((TRequestStatus*)a1); |
|
379 if (r == KErrNone) |
|
380 r = iDefragReq.DefragRam(&iDefragCompleteDfc, KDefragRamThreadPriority); |
|
381 if (r != KErrNone) |
|
382 {// defrag operation didn't start so close all openned handles |
|
383 AsyncClose(); |
|
384 iRequestThread->AsyncClose(); |
|
385 iRequestThread = NULL; |
|
386 } |
|
387 else |
|
388 iDefragDfcFree = EFalse; |
|
389 break; |
|
390 |
|
391 case RDefragChannel::EControlGeneralDefragDfcComplete: |
|
392 if (iRequestThread != NULL) |
|
393 {// The defrag dfc hasn't completed so this shouldn't have been invoked. |
|
394 r = KErrGeneral; |
|
395 } |
|
396 else |
|
397 { |
|
398 iDefragDfcFree = ETrue; |
|
399 } |
|
400 break; |
|
401 |
|
402 case RDefragChannel::EControlGeneralDefragSem: |
|
403 {// Queue a defrag operation so that it will signal a fast mutex once |
|
404 // it has completed. |
|
405 NFastSemaphore sem; |
|
406 NKern::FSSetOwner(&sem, 0); |
|
407 r = iDefragReq.DefragRam(&sem, KDefragRamThreadPriority); |
|
408 |
|
409 if (r != KErrNone) |
|
410 {// Error occurred attempting to queue the defrag operation. |
|
411 break; |
|
412 } |
|
413 |
|
414 // Defrag operation has now been queued so wait for it to finish. |
|
415 // Could do some extra kernel side work here before waiting on the |
|
416 // semaphore. |
|
417 NKern::FSWait(&sem); |
|
418 r = iDefragReq.Result(); |
|
419 } |
|
420 break; |
|
421 |
|
422 case RDefragChannel::EControlGeneralDefrag: |
|
423 // Synchronously perform a defrag. |
|
424 { |
|
425 r = iDefragReq.DefragRam(KDefragRamThreadPriority); |
|
426 } |
|
427 break; |
|
428 |
|
429 case RDefragChannel::EControlAllocLowestZone: |
|
430 // Allocate from the lowest preference zone |
|
431 r = DoAllocLowestZone(); |
|
432 break; |
|
433 |
|
434 case RDefragChannel::EControlClaimLowestZone: |
|
435 // Claims the lowest preference zone |
|
436 r = DoClaimLowestZone(); |
|
437 break; |
|
438 |
|
439 case RDefragChannel::EControlCloseChunk: |
|
440 // Have finished with the chunk so close it then free the RAM mapped by it |
|
441 r = DoChunkClose(); |
|
442 TRACE( if (r != KErrNone) {Kern::Printf("ChunkClose returns %d", r);}); |
|
443 break; |
|
444 |
|
445 default: |
|
446 r=KErrNotSupported; |
|
447 break; |
|
448 } |
|
449 exit: |
|
450 Kern::SemaphoreSignal(*iDefragSemaphore); |
|
451 NKern::ThreadLeaveCS(); |
|
452 TRACE(if (r!=KErrNone) {Kern::Printf("DDefragChannel::Request returns %d", r); }); |
|
453 return r; |
|
454 } |
|
455 |
|
456 |
|
457 /** |
|
458 Allocates RAM from the lowest preference zone and maps it to a shared chunk. |
|
459 |
|
460 Real drivers would not need to determine which zone to allocate from as they |
|
461 will know the zone's ID. |
|
462 |
|
463 @return KErrNone on success, otherwise one of the system wide error codes. |
|
464 */ |
|
465 TInt DDefragChannel::DoAllocLowestZone() |
|
466 { |
|
467 TInt r = KErrNone; |
|
468 TLinAddr chunkAddr = NULL; |
|
469 TUint32 mapAttr = NULL; |
|
470 TChunkCreateInfo createInfo; |
|
471 TLinAddr bufBaseAddr; |
|
472 TUint bufPages; |
|
473 TPhysAddr* bufAddrs; |
|
474 |
|
475 if (iBufChunk != NULL) |
|
476 {// The buffer chunk is already mapped so can't use again until it is |
|
477 // freed/closed. Wait a short while for it to be freed as it may be in the |
|
478 // process of being destroyed. |
|
479 if (WaitForIdle() != KErrNone || iBufChunk != NULL) |
|
480 {// chunk still hasn't been freed so can't proceed. |
|
481 r = KErrInUse; |
|
482 goto exit; |
|
483 } |
|
484 } |
|
485 |
|
486 // Attempt to allocate all the pages it should be possible to allocate. |
|
487 // Real device drivers will now how much they need to allocate so they |
|
488 // wouldn't determine it here. |
|
489 SRamZoneUtilisation zoneUtil; |
|
490 Kern::HalFunction(EHalGroupRam, ERamHalGetZoneUtilisation, (TAny*)iLowestPrefZoneIndex, (TAny*)&zoneUtil); |
|
491 bufPages = iLowestPrefZonePages - (zoneUtil.iAllocFixed + zoneUtil.iAllocUnknown + zoneUtil.iAllocOther); |
|
492 bufAddrs = new TPhysAddr[bufPages]; |
|
493 if (!bufAddrs) |
|
494 { |
|
495 TRACE(Kern::Printf("Failed to allocate an array for bufAddrs")); |
|
496 r = KErrNoMemory; |
|
497 goto exit; |
|
498 } |
|
499 |
|
500 // Update the page count as bufAddrs allocation may have caused the kernel |
|
501 // heap to grow. |
|
502 Kern::HalFunction(EHalGroupRam, ERamHalGetZoneUtilisation, (TAny*)iLowestPrefZoneIndex, (TAny*)&zoneUtil); |
|
503 bufPages = iLowestPrefZonePages - (zoneUtil.iAllocFixed + zoneUtil.iAllocUnknown + zoneUtil.iAllocOther); |
|
504 |
|
505 // Allocate discontiguous pages from the zone |
|
506 r = Epoc::ZoneAllocPhysicalRam(iLowestPrefZoneId, bufPages, bufAddrs); |
|
507 if (r != KErrNone && r != KErrNoMemory) |
|
508 { |
|
509 TRACE(Kern::Printf("Zone Alloc returns %d bufPages %x", r, bufPages)); |
|
510 goto exit; |
|
511 } |
|
512 // If we couldn't allocate all the required pages then empty the zone |
|
513 // and retry. |
|
514 if (r == KErrNoMemory) |
|
515 { |
|
516 r = iDefragReq.EmptyRamZone(iLowestPrefZoneId, TRamDefragRequest::KInheritPriority); |
|
517 if (r != KErrNone) |
|
518 { |
|
519 TRACE(Kern::Printf("Empty returns %d", r)); |
|
520 goto exit; |
|
521 } |
|
522 r = Epoc::ZoneAllocPhysicalRam(iLowestPrefZoneId, bufPages, bufAddrs); |
|
523 if (r != KErrNone) |
|
524 { |
|
525 TRACE(Kern::Printf("ZoneAlloc1 returns %d bufPages %x", r, bufPages)); |
|
526 goto exit; |
|
527 } |
|
528 } |
|
529 |
|
530 // Create a chunk cleanup object which will free the physical RAM when the |
|
531 // chunk is detroyed |
|
532 iChunkCleanup = new TChunkCleanup(this, bufAddrs, bufPages); |
|
533 if (!iChunkCleanup) |
|
534 { |
|
535 TRACE(Kern::Printf("iChunkCleanup creation failed")); |
|
536 r = Epoc::FreePhysicalRam(bufPages, bufAddrs); |
|
537 if (r != KErrNone) |
|
538 { |
|
539 TRACE(Kern::Printf("ERROR - freeing physical memory when chunkCleanup create failed")); |
|
540 } |
|
541 else |
|
542 { |
|
543 r = KErrNoMemory; |
|
544 } |
|
545 goto exit; |
|
546 } |
|
547 |
|
548 // Map the allocated buffer pages to a chunk so we can use it. |
|
549 createInfo.iType = TChunkCreateInfo::ESharedKernelSingle; // could also be ESharedKernelMultiple |
|
550 createInfo.iMaxSize = bufPages << iPageShift; |
|
551 createInfo.iMapAttr = EMapAttrFullyBlocking; // Non-cached - See TMappingAttributes for all options |
|
552 createInfo.iOwnsMemory = EFalse; // Must be false as the physical RAM has already been allocated |
|
553 createInfo.iDestroyedDfc = iChunkCleanup; |
|
554 r = Kern::ChunkCreate(createInfo, iBufChunk, chunkAddr, mapAttr); |
|
555 if (r != KErrNone) |
|
556 { |
|
557 TRACE(Kern::Printf("ChunkCreate returns %d size %x pages %x", r, createInfo.iMaxSize, bufPages)); |
|
558 goto exit; |
|
559 } |
|
560 |
|
561 // Map the physical memory to the chunk |
|
562 r = Kern::ChunkCommitPhysical(iBufChunk, 0, createInfo.iMaxSize, bufAddrs); |
|
563 if (r != KErrNone) |
|
564 { |
|
565 TRACE(Kern::Printf("CommitPhys returns %d", r)); |
|
566 goto exit; |
|
567 } |
|
568 |
|
569 // Now that the RAM is mapped into a chunk get the kernel-side virtual |
|
570 // base address of the buffer. |
|
571 r = Kern::ChunkAddress(iBufChunk, 0, createInfo.iMaxSize, bufBaseAddr); |
|
572 |
|
573 // Using bufBaseAddr a real driver may now do something with the buffer. We'll just return. |
|
574 |
|
575 exit: |
|
576 return r; |
|
577 } |
|
578 |
|
579 |
|
580 /** |
|
581 Claims the lowest preference zone and maps it to a shared chunk. |
|
582 |
|
583 Real drivers would not need to determine which zone to allocate from as they |
|
584 will know the zone's ID. |
|
585 |
|
586 @return KErrNone on success, otherwise one of the system wide error codes. |
|
587 */ |
|
588 TInt DDefragChannel::DoClaimLowestZone() |
|
589 { |
|
590 TInt r = KErrNone; |
|
591 TChunkCreateInfo createInfo; |
|
592 TLinAddr bufBaseAddr; |
|
593 TLinAddr chunkAddr; |
|
594 TUint32 mapAttr = NULL; |
|
595 TPhysAddr bufBase; |
|
596 TUint bufBytes; |
|
597 |
|
598 if (iBufChunk != NULL) |
|
599 {// The buffer chunk is already mapped so can't use again until it is |
|
600 // freed/closed. Wait a short while for it to be freed as it may be in the |
|
601 // process of being destroyed. |
|
602 if (WaitForIdle() != KErrNone || iBufChunk != NULL) |
|
603 {// chunk still hasn't been freed so can't proceed. |
|
604 r = KErrInUse; |
|
605 goto exit; |
|
606 } |
|
607 } |
|
608 |
|
609 // Claim the zone the base address of which will be stored in iBufBase. |
|
610 r = iDefragReq.ClaimRamZone(iLowestPrefZoneId, bufBase, TRamDefragRequest::KInheritPriority); |
|
611 if (r != KErrNone) |
|
612 { |
|
613 TRACE(Kern::Printf("Claim returns %d", r)); |
|
614 goto exit; |
|
615 } |
|
616 |
|
617 // Create a chunk cleanup object which will free the physical RAM when the |
|
618 // chunk is detroyed |
|
619 bufBytes = iLowestPrefZonePages << iPageShift; |
|
620 iChunkCleanup = new TChunkCleanup(this, bufBase, bufBytes); |
|
621 if (!iChunkCleanup) |
|
622 { |
|
623 TRACE(Kern::Printf("chunkCleanup creation failed")); |
|
624 r = Epoc::FreePhysicalRam(bufBytes, bufBase); |
|
625 if (r != KErrNone) |
|
626 { |
|
627 TRACE(Kern::Printf("ERROR - freeing physical memory when chunkCleanup create failed")); |
|
628 } |
|
629 else |
|
630 { |
|
631 r = KErrNoMemory; |
|
632 } |
|
633 goto exit; |
|
634 } |
|
635 |
|
636 // Map the allocated buffer pages to a chunk so we can use it. |
|
637 createInfo.iType = TChunkCreateInfo::ESharedKernelSingle; // could also be ESharedKernelMultiple |
|
638 createInfo.iMaxSize = bufBytes; |
|
639 createInfo.iMapAttr = EMapAttrFullyBlocking; // Non-cached - See TMappingAttributes for all options |
|
640 createInfo.iOwnsMemory = EFalse; // Must be false as the physical RAM has already been allocated |
|
641 createInfo.iDestroyedDfc = iChunkCleanup; |
|
642 r = Kern::ChunkCreate(createInfo, iBufChunk, chunkAddr, mapAttr); |
|
643 if (r != KErrNone) |
|
644 { |
|
645 TRACE(Kern::Printf("ChunkCreate returns %d size %x bytes %x", r, createInfo.iMaxSize, bufBytes)); |
|
646 goto exit; |
|
647 } |
|
648 |
|
649 // Map the physically contiguous memory to the chunk |
|
650 r = Kern::ChunkCommitPhysical(iBufChunk, 0, createInfo.iMaxSize, bufBase); |
|
651 if (r != KErrNone) |
|
652 { |
|
653 TRACE(Kern::Printf("CommitPhys returns %d", r)); |
|
654 goto exit; |
|
655 } |
|
656 |
|
657 // Now that the RAM is mapped into a chunk get the kernel-side virtual |
|
658 // base address of the buffer. |
|
659 r = Kern::ChunkAddress(iBufChunk, 0, createInfo.iMaxSize, bufBaseAddr); |
|
660 |
|
661 // Using bufBaseAddr a real driver may now do something with the buffer. We'll just return. |
|
662 |
|
663 exit: |
|
664 return r; |
|
665 } |
|
666 |
|
667 |
|
668 /** |
|
669 Determine the lowest preference zone. |
|
670 |
|
671 @return KErrNone on success or KErrNotFound if there is only one zone. |
|
672 */ |
|
673 TInt DDefragChannel::FindLowestPrefZone() |
|
674 { |
|
675 TUint zoneCount; |
|
676 TInt r = Kern::HalFunction(EHalGroupRam, ERamHalGetZoneCount, (TAny*)&zoneCount, NULL); |
|
677 if(r!=KErrNone) |
|
678 return r; |
|
679 |
|
680 if (zoneCount == 1) |
|
681 {// Only one zone so can't move pages anywhere or empty a zone |
|
682 return KErrNotFound; |
|
683 } |
|
684 |
|
685 SRamZoneConfig zoneConfig; |
|
686 SRamZoneUtilisation zoneUtil; |
|
687 Kern::HalFunction(EHalGroupRam, ERamHalGetZoneConfig, (TAny*)0, (TAny*)&zoneConfig); |
|
688 Kern::HalFunction(EHalGroupRam, ERamHalGetZoneUtilisation, (TAny*)0, (TAny*)&zoneUtil); |
|
689 TUint lowestPref = zoneConfig.iPref; |
|
690 TUint lowestFreePages = zoneUtil.iFreePages; |
|
691 iLowestPrefZoneIndex = 0; |
|
692 iLowestPrefZoneId = zoneConfig.iZoneId; |
|
693 TUint i = 1; |
|
694 for (; i < zoneCount; i++) |
|
695 { |
|
696 Kern::HalFunction(EHalGroupRam, ERamHalGetZoneConfig, (TAny*)i, (TAny*)&zoneConfig); |
|
697 Kern::HalFunction(EHalGroupRam, ERamHalGetZoneUtilisation, (TAny*)i, (TAny*)&zoneUtil); |
|
698 // When zones have the same preference the zone higher in the zone list is picked. |
|
699 if (zoneConfig.iPref > lowestPref || |
|
700 (zoneConfig.iPref == lowestPref && zoneUtil.iFreePages >= lowestFreePages)) |
|
701 { |
|
702 lowestPref = zoneConfig.iPref; |
|
703 lowestFreePages = zoneUtil.iFreePages; |
|
704 iLowestPrefZoneIndex = i; |
|
705 iLowestPrefZoneId = zoneConfig.iZoneId; |
|
706 } |
|
707 } |
|
708 // Now that we know the current least preferable zone store its size. |
|
709 Kern::HalFunction(EHalGroupRam, ERamHalGetZoneConfig, (TAny*)iLowestPrefZoneIndex, (TAny*)&zoneConfig); |
|
710 iLowestPrefZonePages = zoneConfig.iPhysPages; |
|
711 TRACE(Kern::Printf("LowestPrefZone %x size %x", iLowestPrefZoneId, iLowestPrefZonePages)); |
|
712 return KErrNone; |
|
713 } |
|
714 |
|
715 |
|
716 /** |
|
717 DFC callback called when a defrag operation has completed. |
|
718 |
|
719 @param aSelf A pointer to the DDefragChannel that requested the defrag operation |
|
720 */ |
|
721 void DDefragChannel::DefragCompleteDfc(TAny* aSelf) |
|
722 { |
|
723 // Just call non-static method |
|
724 ((DDefragChannel*)aSelf)->DefragComplete(); |
|
725 } |
|
726 |
|
727 |
|
728 /** |
|
729 Invoked by the DFC callback which is called when a defrag |
|
730 operation has completed. |
|
731 */ |
|
732 void DDefragChannel::DefragComplete() |
|
733 { |
|
734 TRACE(Kern::Printf(">DDefragChannel::DefragComplete")); |
|
735 TInt result = iDefragReq.Result(); |
|
736 TRACE(Kern::Printf("complete code %d", result)); |
|
737 |
|
738 Kern::SemaphoreWait(*iDefragSemaphore); |
|
739 |
|
740 Kern::QueueRequestComplete(iRequestThread, iCompleteReq, result); |
|
741 iRequestThread->AsyncClose(); |
|
742 iRequestThread = NULL; |
|
743 |
|
744 Kern::SemaphoreSignal(*iDefragSemaphore); |
|
745 |
|
746 TRACE(Kern::Printf("<DDefragChannel::DefragComplete")); |
|
747 // Close the handle on this channel - WARNING this channel may be |
|
748 // deleted immmediately after this call so don't access any members |
|
749 AsyncClose(); |
|
750 } |
|
751 |
|
752 |
|
753 /** |
|
754 Close the chunk. |
|
755 |
|
756 @return KErrNone on success or one of the system wide error codes. |
|
757 */ |
|
758 TInt DDefragChannel::DoChunkClose() |
|
759 { |
|
760 if (iBufChunk == NULL) |
|
761 {// Someone tried to close the chunk before using it |
|
762 return KErrNotFound; |
|
763 } |
|
764 |
|
765 // Rely on the chunk cleanup object being called as that |
|
766 // is what will actually free the physical RAM commited to the chunk. |
|
767 Kern::ChunkClose(iBufChunk); |
|
768 return KErrNone; |
|
769 } |
|
770 |
|
771 |
|
772 /** |
|
773 The chunk has now been destroyed so reset the pointers to allow a new |
|
774 chunk to be created. |
|
775 */ |
|
776 void DDefragChannel::ChunkDestroyed() |
|
777 { |
|
778 __e32_atomic_store_ord_ptr(&iBufChunk, 0); |
|
779 __e32_atomic_store_ord_ptr(&iChunkCleanup, 0); |
|
780 } |
|
781 |
|
782 |
|
783 /** |
|
784 Contruct a Shared Chunk cleanup object which will free the chunk's discontiguous |
|
785 physical memory when a chunk is destroyed. |
|
786 |
|
787 @param aDevice The device to inform when the chunk is destroyed. |
|
788 @param aBufBase The physical base addresses of each of the chunk's memory pages. |
|
789 @param aBufPages The total number of the chunk's pages. |
|
790 */ |
|
791 TChunkCleanup::TChunkCleanup(DDefragChannel* aDevice, TPhysAddr* aBufAddrs, TUint aBufPages) |
|
792 : TDfc((TDfcFn)TChunkCleanup::ChunkDestroyed,this,Kern::SvMsgQue(),0), |
|
793 iBufAddrs(aBufAddrs), |
|
794 iBufSize(aBufPages), |
|
795 iBufContiguous(EFalse), |
|
796 iDevice(aDevice) |
|
797 {} |
|
798 |
|
799 |
|
800 /** |
|
801 Contruct a Shared Chunk cleanup object which will free the chunk's contiguous |
|
802 physical memory when a chunk is destroyed. |
|
803 |
|
804 @param aDevice The device to inform when the chunk is destroyed. |
|
805 @param aBufBase The physical base address of the chunk's memory. |
|
806 @param aBufBytes The total number of the chunk's bytes. |
|
807 */ |
|
808 TChunkCleanup::TChunkCleanup(DDefragChannel* aDevice, TPhysAddr aBufBase, TUint aBufBytes) |
|
809 : TDfc((TDfcFn)TChunkCleanup::ChunkDestroyed,this,Kern::SvMsgQue(),0), |
|
810 iBufBase(aBufBase), |
|
811 iBufSize(aBufBytes), |
|
812 iBufContiguous(ETrue), |
|
813 iDevice(aDevice) |
|
814 {} |
|
815 |
|
816 /** |
|
817 Callback function which is called the DFC runs, i.e. when a chunk is destroyed |
|
818 and frees the physical memory allocated when the chunk was created. |
|
819 |
|
820 @param aSelf Pointer to the cleanup object associated with the chunk that has |
|
821 been destroyed. |
|
822 */ |
|
823 void TChunkCleanup::ChunkDestroyed(TChunkCleanup* aSelf) |
|
824 { |
|
825 aSelf->DoChunkDestroyed(); |
|
826 |
|
827 // We've finished so now delete ourself |
|
828 delete aSelf; |
|
829 } |
|
830 |
|
831 |
|
832 /** |
|
833 The chunk has been destroyed so free the physical RAM that was allocated |
|
834 for its use and inform iDevice that it has been destroyed. |
|
835 */ |
|
836 void TChunkCleanup::DoChunkDestroyed() |
|
837 { |
|
838 if (iBufContiguous) |
|
839 { |
|
840 __NK_ASSERT_ALWAYS(Epoc::FreePhysicalRam(iBufBase, iBufSize) == KErrNone); |
|
841 } |
|
842 else |
|
843 { |
|
844 __NK_ASSERT_ALWAYS(Epoc::FreePhysicalRam(iBufSize, iBufAddrs) == KErrNone); |
|
845 } |
|
846 |
|
847 if (iDevice != NULL) |
|
848 {// Allow iDevice to perform any cleanup it requires for this chunk. |
|
849 iDevice->ChunkDestroyed(); |
|
850 } |
|
851 } |
|
852 |
|
853 |
|
854 /** |
|
855 Remove the device so its ChunkDestroyed() method isn't invoked when the chunk is |
|
856 destroyed. |
|
857 */ |
|
858 void TChunkCleanup::RemoveDevice() |
|
859 { |
|
860 __e32_atomic_store_ord_ptr(&iDevice, 0); |
|
861 } |