VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/BandwidthControlImpl.cpp@ 94348

Last change on this file since 94348 was 93628, checked in by vboxsync, 3 years ago

VMM/PDMNetShaper,Main,DrvNetShaper: Moved the network shaper data off the hyper heap and into the VM structure. bugref:10093 bugref:5582

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.6 KB
Line 
1/* $Id: BandwidthControlImpl.cpp 93628 2022-02-06 23:44:05Z vboxsync $ */
2/** @file
3 * VirtualBox COM class implementation
4 */
5
6/*
7 * Copyright (C) 2006-2022 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#define LOG_GROUP LOG_GROUP_MAIN_BANDWIDTHCONTROL
19#include "BandwidthControlImpl.h"
20#include "BandwidthGroupImpl.h"
21#include "MachineImpl.h"
22#include "Global.h"
23
24#include "AutoStateDep.h"
25#include "AutoCaller.h"
26#include "LoggingNew.h"
27
28#include <iprt/cpp/utils.h>
29#include <VBox/com/array.h>
30#include <VBox/param.h>
31#include <algorithm>
32
33// defines
34/////////////////////////////////////////////////////////////////////////////
35
36// constructor / destructor
37/////////////////////////////////////////////////////////////////////////////
38DEFINE_EMPTY_CTOR_DTOR(BandwidthControl)
39
40
41HRESULT BandwidthControl::FinalConstruct()
42{
43 return BaseFinalConstruct();
44}
45
46void BandwidthControl::FinalRelease()
47{
48 uninit();
49 BaseFinalRelease();
50}
51
52// public initializer/uninitializer for internal purposes only
53/////////////////////////////////////////////////////////////////////////////
54
55/**
56 * Initializes the bandwidth group object.
57 *
58 * @returns COM result indicator.
59 * @param aParent Pointer to our parent object.
60 */
61HRESULT BandwidthControl::init(Machine *aParent)
62{
63 LogFlowThisFunc(("aParent=%p\n", aParent));
64
65 ComAssertRet(aParent, E_INVALIDARG);
66
67 /* Enclose the state transition NotReady->InInit->Ready */
68 AutoInitSpan autoInitSpan(this);
69 AssertReturn(autoInitSpan.isOk(), E_FAIL);
70
71 m = new Data(aParent);
72
73 /* m->pPeer is left null */
74
75 m->llBandwidthGroups.allocate();
76
77 /* Confirm a successful initialization */
78 autoInitSpan.setSucceeded();
79
80 return S_OK;
81}
82
83/**
84 * Initializes the object given another object
85 * (a kind of copy constructor). This object shares data with
86 * the object passed as an argument.
87 *
88 * @note This object must be destroyed before the original object
89 * it shares data with is destroyed.
90 *
91 * @note Locks @a aThat object for writing if @a aReshare is @c true, or for
92 * reading if @a aReshare is false.
93 */
94HRESULT BandwidthControl::init(Machine *aParent,
95 BandwidthControl *aThat)
96{
97 LogFlowThisFunc(("aParent=%p, aThat=%p\n", aParent, aThat));
98
99 ComAssertRet(aParent && aThat, E_INVALIDARG);
100
101 /* Enclose the state transition NotReady->InInit->Ready */
102 AutoInitSpan autoInitSpan(this);
103 AssertReturn(autoInitSpan.isOk(), E_FAIL);
104
105 m = new Data(aParent);
106
107 /* sanity */
108 AutoCaller thatCaller(aThat);
109 AssertComRCReturnRC(thatCaller.rc());
110
111 unconst(m->pPeer) = aThat;
112 AutoWriteLock thatLock(aThat COMMA_LOCKVAL_SRC_POS);
113
114 /* create copies of all groups */
115 m->llBandwidthGroups.allocate();
116 BandwidthGroupList::const_iterator it;
117 for (it = aThat->m->llBandwidthGroups->begin();
118 it != aThat->m->llBandwidthGroups->end();
119 ++it)
120 {
121 ComObjPtr<BandwidthGroup> group;
122 group.createObject();
123 group->init(this, *it);
124 m->llBandwidthGroups->push_back(group);
125 }
126
127 /* Confirm successful initialization */
128 autoInitSpan.setSucceeded();
129
130 return S_OK;
131}
132
133/**
134 * Initializes the bandwidth control object given another guest object
135 * (a kind of copy constructor). This object makes a private copy of data
136 * of the original object passed as an argument.
137 */
138HRESULT BandwidthControl::initCopy(Machine *aParent, BandwidthControl *aThat)
139{
140 LogFlowThisFunc(("aParent=%p, aThat=%p\n", aParent, aThat));
141
142 ComAssertRet(aParent && aThat, E_INVALIDARG);
143
144 /* Enclose the state transition NotReady->InInit->Ready */
145 AutoInitSpan autoInitSpan(this);
146 AssertReturn(autoInitSpan.isOk(), E_FAIL);
147
148 m = new Data(aParent);
149 /* m->pPeer is left null */
150
151 AutoCaller thatCaller(aThat);
152 AssertComRCReturnRC(thatCaller.rc());
153
154 AutoReadLock thatlock(aThat COMMA_LOCKVAL_SRC_POS);
155
156 /* create copies of all groups */
157 m->llBandwidthGroups.allocate();
158 BandwidthGroupList::const_iterator it;
159 for (it = aThat->m->llBandwidthGroups->begin();
160 it != aThat->m->llBandwidthGroups->end();
161 ++it)
162 {
163 ComObjPtr<BandwidthGroup> group;
164 group.createObject();
165 group->initCopy(this, *it);
166 m->llBandwidthGroups->push_back(group);
167 }
168
169 /* Confirm a successful initialization */
170 autoInitSpan.setSucceeded();
171
172 return S_OK;
173}
174
175
176/**
177 * @note Locks this object for writing, together with the peer object
178 * represented by @a aThat (locked for reading).
179 */
180void BandwidthControl::i_copyFrom(BandwidthControl *aThat)
181{
182 AssertReturnVoid(aThat != NULL);
183
184 /* sanity */
185 AutoCaller autoCaller(this);
186 AssertComRCReturnVoid(autoCaller.rc());
187
188 /* sanity too */
189 AutoCaller thatCaller(aThat);
190 AssertComRCReturnVoid(thatCaller.rc());
191
192 /* even more sanity */
193 AutoAnyStateDependency adep(m->pParent);
194 AssertComRCReturnVoid(adep.rc());
195 /* Machine::copyFrom() may not be called when the VM is running */
196 AssertReturnVoid(!Global::IsOnline(adep.machineState()));
197
198 /* peer is not modified, lock it for reading (aThat is "master" so locked
199 * first) */
200 AutoReadLock rl(aThat COMMA_LOCKVAL_SRC_POS);
201 AutoWriteLock wl(this COMMA_LOCKVAL_SRC_POS);
202
203 /* create private copies of all bandwidth groups */
204 m->llBandwidthGroups.backup();
205 m->llBandwidthGroups->clear();
206 BandwidthGroupList::const_iterator it;
207 for (it = aThat->m->llBandwidthGroups->begin();
208 it != aThat->m->llBandwidthGroups->end();
209 ++it)
210 {
211 ComObjPtr<BandwidthGroup> group;
212 group.createObject();
213 group->initCopy(this, *it);
214 m->llBandwidthGroups->push_back(group);
215 }
216}
217
218/** @note Locks objects for writing! */
219void BandwidthControl::i_rollback()
220{
221 AutoCaller autoCaller(this);
222 AssertComRCReturnVoid(autoCaller.rc());
223
224 /* we need the machine state */
225 AutoAnyStateDependency adep(m->pParent);
226 AssertComRCReturnVoid(adep.rc());
227
228 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
229 BandwidthGroupList::const_iterator it;
230
231 if (!m->llBandwidthGroups.isNull())
232 {
233 if (m->llBandwidthGroups.isBackedUp())
234 {
235 /* unitialize all new groups (absent in the backed up list). */
236 BandwidthGroupList *backedList = m->llBandwidthGroups.backedUpData();
237 for (it = m->llBandwidthGroups->begin();
238 it != m->llBandwidthGroups->end();
239 ++it)
240 {
241 if ( std::find(backedList->begin(), backedList->end(), *it)
242 == backedList->end())
243 (*it)->uninit();
244 }
245
246 /* restore the list */
247 m->llBandwidthGroups.rollback();
248 }
249
250 /* rollback any changes to groups after restoring the list */
251 for (it = m->llBandwidthGroups->begin();
252 it != m->llBandwidthGroups->end();
253 ++it)
254 (*it)->i_rollback();
255 }
256}
257
258void BandwidthControl::i_commit()
259{
260 bool commitBandwidthGroups = false;
261 BandwidthGroupList::const_iterator it;
262
263 if (m->llBandwidthGroups.isBackedUp())
264 {
265 m->llBandwidthGroups.commit();
266
267 if (m->pPeer)
268 {
269 AutoWriteLock peerlock(m->pPeer COMMA_LOCKVAL_SRC_POS);
270
271 /* Commit all changes to new groups (this will reshare data with
272 * peers for those who have peers) */
273 BandwidthGroupList *newList = new BandwidthGroupList();
274 for (it = m->llBandwidthGroups->begin();
275 it != m->llBandwidthGroups->end();
276 ++it)
277 {
278 (*it)->i_commit();
279
280 /* look if this group has a peer group */
281 ComObjPtr<BandwidthGroup> peer = (*it)->i_getPeer();
282 if (!peer)
283 {
284 /* no peer means the device is a newly created one;
285 * create a peer owning data this device share it with */
286 peer.createObject();
287 peer->init(m->pPeer, *it, true /* aReshare */);
288 }
289 else
290 {
291 /* remove peer from the old list */
292 m->pPeer->m->llBandwidthGroups->remove(peer);
293 }
294 /* and add it to the new list */
295 newList->push_back(peer);
296 }
297
298 /* uninit old peer's groups that are left */
299 for (it = m->pPeer->m->llBandwidthGroups->begin();
300 it != m->pPeer->m->llBandwidthGroups->end();
301 ++it)
302 (*it)->uninit();
303
304 /* attach new list of groups to our peer */
305 m->pPeer->m->llBandwidthGroups.attach(newList);
306 }
307 else
308 {
309 /* we have no peer (our parent is the newly created machine);
310 * just commit changes to devices */
311 commitBandwidthGroups = true;
312 }
313 }
314 else
315 {
316 /* the list of groups itself is not changed,
317 * just commit changes to groups themselves */
318 commitBandwidthGroups = true;
319 }
320
321 if (commitBandwidthGroups)
322 {
323 for (it = m->llBandwidthGroups->begin();
324 it != m->llBandwidthGroups->end();
325 ++it)
326 (*it)->i_commit();
327 }
328}
329
330/**
331 * Uninitializes the instance and sets the ready flag to FALSE.
332 * Called either from FinalRelease() or by the parent when it gets destroyed.
333 */
334void BandwidthControl::uninit()
335{
336 LogFlowThisFunc(("\n"));
337
338 /* Enclose the state transition Ready->InUninit->NotReady */
339 AutoUninitSpan autoUninitSpan(this);
340 if (autoUninitSpan.uninitDone())
341 return;
342
343 // uninit all groups on the list (it's a standard std::list not an ObjectsList
344 // so we must uninit() manually)
345 BandwidthGroupList::iterator it;
346 for (it = m->llBandwidthGroups->begin();
347 it != m->llBandwidthGroups->end();
348 ++it)
349 (*it)->uninit();
350
351 m->llBandwidthGroups.free();
352
353 unconst(m->pPeer) = NULL;
354 unconst(m->pParent) = NULL;
355
356 delete m;
357 m = NULL;
358}
359
360/**
361 * Returns a bandwidth group object with the given name.
362 *
363 * @param aName bandwidth group name to find
364 * @param aBandwidthGroup where to return the found bandwidth group
365 * @param aSetError true to set extended error info on failure
366 */
367HRESULT BandwidthControl::i_getBandwidthGroupByName(const com::Utf8Str &aName,
368 ComObjPtr<BandwidthGroup> &aBandwidthGroup,
369 bool aSetError /* = false */)
370{
371 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
372
373 for (BandwidthGroupList::const_iterator it = m->llBandwidthGroups->begin();
374 it != m->llBandwidthGroups->end();
375 ++it)
376 {
377 if ((*it)->i_getName() == aName)
378 {
379 aBandwidthGroup = (*it);
380 return S_OK;
381 }
382 }
383
384 if (aSetError)
385 return setError(VBOX_E_OBJECT_NOT_FOUND,
386 tr("Could not find a bandwidth group named '%s'"),
387 aName.c_str());
388 return VBOX_E_OBJECT_NOT_FOUND;
389}
390// To do
391HRESULT BandwidthControl::createBandwidthGroup(const com::Utf8Str &aName,
392 BandwidthGroupType_T aType,
393 LONG64 aMaxBytesPerSec)
394{
395 /*
396 * Validate input.
397 */
398 if (aMaxBytesPerSec < 0)
399 return setError(E_INVALIDARG, tr("Bandwidth group limit cannot be negative"));
400 switch (aType)
401 {
402 case BandwidthGroupType_Null: /*??*/
403 case BandwidthGroupType_Disk:
404 break;
405 case BandwidthGroupType_Network:
406 if (aName.length() > PDM_NET_SHAPER_MAX_NAME_LEN)
407 return setError(E_INVALIDARG, tr("Bandwidth name is too long: %zu, max %u"),
408 aName.length(), PDM_NET_SHAPER_MAX_NAME_LEN);
409 break;
410 default:
411 AssertFailedReturn(setError(E_INVALIDARG, tr("Invalid group type: %d"), aType));
412 }
413 if (aName.isEmpty())
414 return setError(E_INVALIDARG, tr("Bandwidth group name must not be empty")); /* ConsoleImpl2.cpp fails then */
415
416 /*
417 * The machine needs to be mutable:
418 */
419 AutoMutableOrSavedStateDependency adep(m->pParent);
420 HRESULT hrc = adep.rc();
421 if (SUCCEEDED(hrc))
422 {
423 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
424
425 /*
426 * Check that the group doesn't already exist:
427 */
428 ComObjPtr<BandwidthGroup> group;
429 hrc = i_getBandwidthGroupByName(aName, group, false /* aSetError */);
430 if (FAILED(hrc))
431 {
432 /*
433 * There is an upper limit of the number of network groups imposed by PDM.
434 */
435 size_t cNetworkGroups = 0;
436 if (aType == BandwidthGroupType_Network)
437 for (BandwidthGroupList::const_iterator it = m->llBandwidthGroups->begin();
438 it != m->llBandwidthGroups->end();
439 ++it)
440 if ((*it)->i_getType() == BandwidthGroupType_Network)
441 cNetworkGroups++;
442 if (cNetworkGroups < PDM_NET_SHAPER_MAX_GROUPS)
443 {
444 /*
445 * Create the new group.
446 */
447 hrc = group.createObject();
448 if (SUCCEEDED(hrc))
449 {
450 hrc = group->init(this, aName, aType, aMaxBytesPerSec);
451 if (SUCCEEDED(hrc))
452 {
453 /*
454 * Add it to the settings.
455 */
456 m->pParent->i_setModified(Machine::IsModified_BandwidthControl);
457 m->llBandwidthGroups.backup();
458 m->llBandwidthGroups->push_back(group);
459 hrc = S_OK;
460 }
461 }
462 }
463 else
464 hrc = setError(E_FAIL, tr("Too many network bandwidth groups (max %u)"), PDM_NET_SHAPER_MAX_GROUPS);
465 }
466 else
467 hrc = setError(VBOX_E_OBJECT_IN_USE, tr("Bandwidth group named '%s' already exists"), aName.c_str());
468 }
469 return hrc;
470}
471
472HRESULT BandwidthControl::deleteBandwidthGroup(const com::Utf8Str &aName)
473{
474 /* the machine needs to be mutable */
475 AutoMutableOrSavedStateDependency adep(m->pParent);
476 if (FAILED(adep.rc())) return adep.rc();
477
478 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
479
480 ComObjPtr<BandwidthGroup> group;
481 HRESULT rc = i_getBandwidthGroupByName(aName, group, true /* aSetError */);
482 if (FAILED(rc)) return rc;
483
484 if (group->i_getReferences() != 0)
485 return setError(VBOX_E_OBJECT_IN_USE,
486 tr("The bandwidth group '%s' is still in use"), aName.c_str());
487
488 /* We can remove it now. */
489 m->pParent->i_setModified(Machine::IsModified_BandwidthControl);
490 m->llBandwidthGroups.backup();
491
492 group->i_unshare();
493
494 m->llBandwidthGroups->remove(group);
495
496 /* inform the direct session if any */
497 alock.release();
498 //onStorageControllerChange(); @todo
499
500 return S_OK;
501}
502
503HRESULT BandwidthControl::getNumGroups(ULONG *aNumGroups)
504{
505 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
506
507 *aNumGroups = (ULONG)m->llBandwidthGroups->size();
508
509 return S_OK;
510}
511
512HRESULT BandwidthControl::getBandwidthGroup(const com::Utf8Str &aName, ComPtr<IBandwidthGroup> &aBandwidthGroup)
513{
514 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
515
516 ComObjPtr<BandwidthGroup> group;
517 HRESULT rc = i_getBandwidthGroupByName(aName, group, true /* aSetError */);
518
519 if (SUCCEEDED(rc))
520 group.queryInterfaceTo(aBandwidthGroup.asOutParam());
521
522 return rc;
523}
524
525HRESULT BandwidthControl::getAllBandwidthGroups(std::vector<ComPtr<IBandwidthGroup> > &aBandwidthGroups)
526{
527 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
528 aBandwidthGroups.resize(0);
529 BandwidthGroupList::const_iterator it;
530 for (it = m->llBandwidthGroups->begin();
531 it != m->llBandwidthGroups->end();
532 ++it)
533 aBandwidthGroups.push_back(*it);
534
535 return S_OK;
536}
537
538HRESULT BandwidthControl::i_loadSettings(const settings::IOSettings &data)
539{
540 HRESULT rc = S_OK;
541
542 AutoCaller autoCaller(this);
543 AssertComRCReturnRC(autoCaller.rc());
544 settings::BandwidthGroupList::const_iterator it;
545 for (it = data.llBandwidthGroups.begin();
546 it != data.llBandwidthGroups.end();
547 ++it)
548 {
549 const settings::BandwidthGroup &gr = *it;
550 rc = createBandwidthGroup(gr.strName, gr.enmType, (LONG64)gr.cMaxBytesPerSec);
551 if (FAILED(rc)) break;
552 }
553
554 return rc;
555}
556
557HRESULT BandwidthControl::i_saveSettings(settings::IOSettings &data)
558{
559 AutoCaller autoCaller(this);
560 if (FAILED(autoCaller.rc())) return autoCaller.rc();
561
562 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
563 data.llBandwidthGroups.clear();
564 BandwidthGroupList::const_iterator it;
565 for (it = m->llBandwidthGroups->begin();
566 it != m->llBandwidthGroups->end();
567 ++it)
568 {
569 AutoWriteLock groupLock(*it COMMA_LOCKVAL_SRC_POS);
570 settings::BandwidthGroup group;
571
572 group.strName = (*it)->i_getName();
573 group.enmType = (*it)->i_getType();
574 group.cMaxBytesPerSec = (uint64_t)(*it)->i_getMaxBytesPerSec();
575
576 data.llBandwidthGroups.push_back(group);
577 }
578
579 return S_OK;
580}
581
582Machine * BandwidthControl::i_getMachine() const
583{
584 return m->pParent;
585}
586
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette