Skip to content

Commit

Permalink
Add KMP purge check.
Browse files Browse the repository at this point in the history
KMPs are clustered by kernel versions matching their requirements.
Within a cluster the highest KMP version per kernel is needed.
  • Loading branch information
mlandres committed Nov 28, 2024
1 parent 8afcf0d commit e1ffc69
Show file tree
Hide file tree
Showing 2 changed files with 186 additions and 1 deletion.
2 changes: 1 addition & 1 deletion tools/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ FILE( GLOB ALLCC RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*.cc" )
STRING( REPLACE ".cc" ";" APLLPROG ${ALLCC} )

# make sure not to statically linked installed tools
SET( LINKALLSYM CalculateReusableBlocks DownloadFiles )
SET( LINKALLSYM CalculateReusableBlocks DownloadFiles zypp-runpurge)

FOREACH( loop_var ${APLLPROG} )
ADD_EXECUTABLE( ${loop_var}
Expand Down
185 changes: 185 additions & 0 deletions tools/zypp-runpurge.cc
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#include <zypp/PoolQuery.h>
#include <zypp/sat/WhatProvides.h>
#include <zypp/PurgeKernels.h>
#include "argparse.h"

Expand All @@ -22,6 +24,186 @@ int usage( const argparse::Options & options_r, int return_r = 0 )
return return_r;
}


// Helper for version sorted containers
struct NVR : public sat::Solvable {
NVR( const sat::Solvable & slv )
: sat::Solvable { slv }
{}
};

inline std::ostream & operator<<( std::ostream & str, const NVR & obj )
{ return str << obj.ident() << " " << obj.edition() << (obj.isSystem()?" (i)":""); }

inline bool operator<( const NVR & lhs, const NVR & rhs )
{ return compareByNVRA( lhs, rhs ) < 0; }


// Cluster KMPs by kernel and fitting KMP versions (check either installed or available versions)
// (see also bsc#1232399 etc.)
void KMPcheck( const bool checkSystem = true )
{
using SolvableSet =std::unordered_set<sat::Solvable>;

// Whether slv is on container
auto contains = []( const SolvableSet & container, const sat::Solvable & slv ) -> bool {
return container.find( slv ) != container.end();
};

// Remove items from fitting which do not occur in matching
auto intersect = [&]( SolvableSet & fitting, const SolvableSet & matching ) {
for ( auto it = fitting.begin(); it != fitting.end(); ) {
if ( contains( matching, *it ) )
++it;
else
it = fitting.erase( it );
}
};

SolvableSet kernels; // multiversion kernels
SolvableSet kmps; // multiversion kmps
SolvableSet others; // multiversion others
{
const Capability idxcap { "kernel-uname-r" };
sat::WhatProvides idx { idxcap };
for ( const auto & i : idx ) {
if ( i.isSystem() != checkSystem )
continue;
kernels.insert( i );
}
}
{
const Capability idxcap { "multiversion(kernel)" };
sat::WhatProvides idx { idxcap };

StrMatcher matchMod { "kmod(*)", Match::GLOB };
auto isKmp = [&matchMod]( const sat::Solvable & slv ) -> bool {
for ( const auto & prov : slv.provides() ) {
if ( matchMod.doMatch( prov.detail().name().c_str() ) )
return true;
}
return false;
};

for ( const auto & i : idx ) {
if ( i.isSystem() != checkSystem )
continue;
if ( not contains( kernels, i ) ) {
( isKmp( i ) ? kmps : others ).insert( i );
}
}
}
cout << "K " << kernels.size() << ", M " << kmps.size() << ", O " << others.size() << endl;

// Caching which KMP requirement is provided by what kernels.
std::unordered_map<Capability,SolvableSet> whatKernelProvidesCache;
auto whatKernelProvides = [&]( const Capability & req ) -> const SolvableSet & {
auto iter = whatKernelProvidesCache.find( req );
if ( iter != whatKernelProvidesCache.end() )
return iter->second; // hit
// miss:
SolvableSet & providingKernels = whatKernelProvidesCache[req];
sat::WhatProvides idx { req };
for ( const auto & i : idx ) {
if ( contains( kernels, i ) )
providingKernels.insert( i );
}
return providingKernels;
};

#if 0
using Bucket = SolvableSet; // fitting KMP versions
using KmpBuckets = std::unordered_map<sat::Solvable,Bucket>; // kernel : fitting KMP versions
using KernelKmps = std::unordered_map<IdString,KmpBuckets>; // KMP name : kernel : fitting KMP versions
#else
// sorted containers:
using Bucket = std::set<NVR>; // fitting KMP versions
using KmpBuckets = std::map<NVR,Bucket>; // kernel : fitting KMP versions
using KernelKmps = std::map<IdString,KmpBuckets>; // KMP name : kernel : fitting KMP versions
#endif
KernelKmps kernelKmps;

// Cluster the KMPs....
for ( const auto & kmp : kmps ) {
//cout << endl << "==== " << kmp << endl;
std::optional<SolvableSet> fittingKernels; // kernel satisfying all kernel related requirements
for ( const auto & req : kmp.requires() ) {
const SolvableSet & providingKernels { whatKernelProvides( req ) };
if ( providingKernels.size() == 0 )
continue; // a not kernel related requirement
if ( not fittingKernels ) {
fittingKernels = providingKernels; // initial set
} else {
intersect( *fittingKernels, providingKernels );
}
//cout << "?? " << req << " provided by " << providingKernels.size() << endl;
//cout << " - fit " << fittingKernels->size() << endl;
}
if ( fittingKernels ) {
for ( const auto & kernel : *fittingKernels ) {
kernelKmps[kmp.ident()][kernel].insert( kmp );
}
}
else {
cout << endl << "==== " << kmp << endl;
cout << " No fitting kernels!" << endl;
}
}

// Print the clusters...
if ( checkSystem ) {

// For the installed/prunned system cluster per kernel is IMO easier to check
// ==== kernel-default 5.14.21-150400.24.136.1 (i)
// drbd-kmp-default fit 3 versions(s)
// - drbd-kmp-default 9.0.30~1+git.10bee2d5_k5.14.21_150400.22-150400.1.75 (i)
// - drbd-kmp-default 9.0.30~1+git.10bee2d5_k5.14.21_150400.24.11-150400.3.2.9 (i)
// - drbd-kmp-default 9.0.30~1+git.10bee2d5_k5.14.21_150400.24.46-150400.3.4.1 (i)
std::map<NVR,std::map<IdString,Bucket>> reordered;
for ( const auto & [kmp,bucket] : kernelKmps ) {
for ( const auto & [kernel,versions] : bucket ) {
reordered[kernel][kmp] = versions;
}
}
for ( const auto & [kernel,bucket] : reordered ) {
cout << endl << "==== " << kernel << endl;
for ( const auto & [kmp,versions] : bucket ) {
cout << " " << kmp << " fit " << versions.size() << " versions(s)" << endl;
for ( const auto & version : versions )
cout << " - " << version << endl;
}
}

} else {

// ==== drbd-kmp-default
// ABI kernel-default 5.14.21-150400.24.136.1 (i)
// fit 3 versions(s)
// - drbd-kmp-default 9.0.30~1+git.10bee2d5_k5.14.21_150400.22-150400.1.75 (i)
// - drbd-kmp-default 9.0.30~1+git.10bee2d5_k5.14.21_150400.24.11-150400.3.2.9 (i)
// - drbd-kmp-default 9.0.30~1+git.10bee2d5_k5.14.21_150400.24.46-150400.3.4.1 (i)
for ( const auto & [kmp,bucket] : kernelKmps ) {
cout << endl << "==== " << kmp << endl;
const Bucket * lastBucket = nullptr;
for ( const auto & [kernel,versions] : bucket ) {
if ( lastBucket && *lastBucket != versions ) {
cout << " fit " << lastBucket->size() << " versions(s)" << endl;
for ( const auto & version : *lastBucket )
cout << " - " << version << endl;
}
cout << " ABI " << kernel << endl;
lastBucket = &versions;
}
if ( lastBucket ) {
cout << " fit " << lastBucket->size() << " versions(s)" << endl;
for ( const auto & version : *lastBucket )
cout << " - " << version << endl;
}
}

}
}

int main ( int argc, char *argv[] )
{

Expand Down Expand Up @@ -102,6 +284,9 @@ int main ( int argc, char *argv[] )
std::cout << "Removing " << it->asString() + (it->status().isByUser() ? " (by user)" : " (autoremoved)") << std::endl;
}

std::cout << "KMPcheck: " << std::endl;
KMPcheck();

return 0;

}

0 comments on commit e1ffc69

Please sign in to comment.