このページを編集する際は、編集に関する方針に従ってください。

概要

  • mm/slab.cにて定義
  • 引数で渡されたサイズの特定用途のキャッシュ(kmem_cache_s)を作成する

引数

  • name--キャッシュ名。/proc/slabinfo でこのキャッシュを識別するために使われる
  • size--作成するキャッシュのサイズ
  • align--位置合わせ
  • flags--slabのフラグ
  • ctor--コンストラクタ
  • dtor--デストラクタ

実装

/**

* kmem_cache_create - Create a cache.
* @name: A string which is used in /proc/slabinfo to identify this cache.
* @size: The size of objects to be created in this cache.
* @align: The required alignment for the objects.
* @flags: SLAB flags
* @ctor: A constructor for the objects.
* @dtor: A destructor for the objects.
*
* Returns a ptr to the cache on success, NULL on failure.
* Cannot be called within a int, but can be interrupted.
* The @ctor is run when new pages are allocated by the cache
* and the @dtor is run before the pages are handed back.
*
* @name must be valid until the cache is destroyed. This implies that
* the module calling this has to destroy the cache before getting 
* unloaded.
* 
* The flags are
*
* %SLAB_POISON - Poison the slab with a known test pattern (a5a5a5a5)
* to catch references to uninitialised memory.
*
* %SLAB_RED_ZONE - Insert `Red' zones around the allocated memory to check
* for buffer overruns.
*
* %SLAB_NO_REAP - Don't automatically reap this cache when we're under
* memory pressure.
*
* %SLAB_HWCACHE_ALIGN - Align the objects in this cache to a hardware
* cacheline.  This can be beneficial if you're counting cycles as closely
* as davem.
*/

kmem_cache_t * kmem_cache_create (const char *name, size_t size, size_t align,

	unsigned long flags, void (*ctor)(void*, kmem_cache_t *, unsigned long),
	void (*dtor)(void*, kmem_cache_t *, unsigned long))

{

	size_t left_over, slab_size;
	kmem_cache_t *cachep = NULL;
	/*
	 * Sanity checks... these are all serious usage bugs.
	 */
	if ((!name) ||
		in_interrupt() ||
		(size < BYTES_PER_WORD) ||
		(size > (1<<MAX_OBJ_ORDER)*PAGE_SIZE) ||
		(dtor && !ctor)) {
  • 引数で渡された値のチェックなど、この関数の使用に関するチェック:
  • ソフトウェア割り込み、またはハードウェア割り込みが禁止された、回数を返す
			printk(KERN_ERR "%s: Early error in slab %s\n",
					__FUNCTION__, name);
			BUG();
		}

#if DEBUG

  • DEBUGが有効である場合:
	WARN_ON(strchr(name, ' '));	/* It confuses parsers */
  • 文字列から空白を探す
    • 見つかった場合、見つかった場所へのポインタを返す
    • 見つからなかった場合は、0を返す
    • 詳細はstrchr()/linux2.6を参照
	if ((flags & SLAB_DEBUG_INITIAL) && !ctor) {
  • SLAB_DEBUG_INITIALは(検査機構として)コンストラクタを呼び出すことを意味する
    • しかしコンストラクタが引数で渡されていない場合は、このフラグをクリアする
		/* No constructor, but inital state check requested */
		printk(KERN_ERR "%s: No con, but init state check "
				"requested - %s\n", __FUNCTION__, name);
		flags &= ~SLAB_DEBUG_INITIAL;
	}

#if FORCED_DEBUG

	/*
	 * Enable redzoning and last user accounting, except for caches with
	 * large objects, if the increased size would increase the object size
	 * above the next power of two: caches with object sizes just above a
	 * power of two have a significant amount of internal fragmentation.
	 */
	if ((size < 4096 || fls(size-1) == fls(size-1+3*BYTES_PER_WORD)))
		flags |= SLAB_RED_ZONE|SLAB_STORE_USER;
  • 引数で与えられた対象の最後のセットビット位置を返す
	if (!(flags & SLAB_DESTROY_BY_RCU))
		flags |= SLAB_POISON;

#endif

	if (flags & SLAB_DESTROY_BY_RCU)
		BUG_ON(flags & SLAB_POISON);

#endif

	if (flags & SLAB_DESTROY_BY_RCU)
		BUG_ON(dtor);
  • flagsにSLAB_DESTROY_BY_RCUフラグが立っている場合で、かつデストラクタdtorが設定されている場合はバグである
    • SLAB_DESTROY_BY_RCUはRCUによりページの解放を遅延することを意味する
	/*
	 * Always checks flags, a caller might be expecting debug
	 * support which isn't available.
	 */
	if (flags & ~CREATE_MASK)
		BUG();
	if (align) {
		/* combinations of forced alignment and advanced debugging is
		 * not yet implemented.
		 */
		flags &= ~(SLAB_RED_ZONE|SLAB_STORE_USER);
  • SLAB_RED_ZONEはキャッシュの危険領域にオブジェクトがあることを意味する
  • SLAB_STORE_USERはデバッグの為に最後のオーナーを保存することを意味する
	} else {
		if (flags & SLAB_HWCACHE_ALIGN) {
  • SLAB_HWCACHE_ALIGNはハードウェアキャッシュラインにオブジェクトをアラインメントすることを意味する
			/* Default alignment: as specified by the arch code.
			 * Except if an object is really small, then squeeze multiple
			 * into one cacheline.
			 */
			align = cache_line_size();
			while (size <= align/2)
				align /= 2;
		} else {
			align = BYTES_PER_WORD;
		}
	}
	/* Get cache's description obj. */
	cachep = (kmem_cache_t *) kmem_cache_alloc(&cache_cache, SLAB_KERNEL);
  • cache_cacheからキャッシュヘッダを確保する
    • cache_cacheはカーネルが使用するキャッシュヘッダを格納するキャッシュヘッダ
    • 詳細はkmem_cache_alloc()/linux2.6を参照
	if (!cachep)
		goto opps;
  • キャッシュの確保に失敗した場合はoppsにジャンプ
	memset(cachep, 0, sizeof(kmem_cache_t));
  • キャッシュの初期化
  • 0でcachepオブジェクトを埋める
	/* Check that size is in terms of words.  This is needed to avoid
	 * unaligned accesses for some archs when redzoning is used, and makes
	 * sure any on-slab bufctl's are also correctly aligned.
	 */
	if (size & (BYTES_PER_WORD-1)) {
		size += (BYTES_PER_WORD-1);
		size &= ~(BYTES_PER_WORD-1);
	}
	

#if DEBUG

  • DEBUGが有効である場合:
	cachep->reallen = size;
	if (flags & SLAB_RED_ZONE) {
		/* redzoning only works with word aligned caches */
		align = BYTES_PER_WORD;
		/* add space for red zone words */
		cachep->dbghead += BYTES_PER_WORD;
		size += 2*BYTES_PER_WORD;
	}
	if (flags & SLAB_STORE_USER) {
		/* user store requires word alignment and
		 * one word storage behind the end of the real
		 * object.
		 */
		align = BYTES_PER_WORD;
		size += BYTES_PER_WORD;
	}

#if FORCED_DEBUG && defined(CONFIG_DEBUG_PAGEALLOC)

	if (size > 128 && cachep->reallen > cache_line_size() && size < PAGE_SIZE) {
		cachep->dbghead += PAGE_SIZE - size;
		size = PAGE_SIZE;
	}

#endif

#endif

	/* Determine if the slab management is 'on' or 'off' slab. */
	if (size >= (PAGE_SIZE>>3))
		/*
		 * Size is large, assume best to place the slab management obj
		 * off-slab (should allow better packing of objs).
		 */
		flags |= CFLGS_OFF_SLAB;
  • スラブがオフスラブであるか調べ、オフスラブである場合はスラブにCFLGS_OFF_SLABフラグを立てる
	size = ALIGN(size, align);
	if ((flags & SLAB_RECLAIM_ACCOUNT) && size <= PAGE_SIZE) {
  • SLAB_RECLAIM_ACCOUNTはページを解放した後に記録を残すことを意味する
		/*
		 * A VFS-reclaimable slab tends to have most allocations
		 * as GFP_NOFS and we really don't want to have to be allocating
		 * higher-order pages when we are unable to shrink dcache.
		 */
		cachep->gfporder = 0;
		cache_estimate(cachep->gfporder, size, align, flags,
					&left_over, &cachep->num);
	} else {
		/*
		 * Calculate size (in pages) of slabs, and the num of objs per
		 * slab.  This could be made much more intelligent.  For now,
		 * try to avoid using high page-orders for slabs.  When the
		 * gfp() funcs are more friendly towards high-order requests,
		 * this should be changed.
		 */
		do {
			unsigned int break_flag = 0;

cal_wastage:

			cache_estimate(cachep->gfporder, size, align, flags,
						&left_over, &cachep->num);
			if (break_flag)
				break;
			if (cachep->gfporder >= MAX_GFP_ORDER)
				break;
			if (!cachep->num)
				goto next;
			if (flags & CFLGS_OFF_SLAB &&
					cachep->num > offslab_limit) {
				/* This num of objs will cause problems. */
				cachep->gfporder--;
				break_flag++;
				goto cal_wastage;
			}
			/*
			 * Large num of objs is good, but v. large slabs are
			 * currently bad for the gfp()s.
			 */
			if (cachep->gfporder >= slab_break_gfp_order)
				break;
			if ((left_over*8) <= (PAGE_SIZE<<cachep->gfporder))
				break;	/* Acceptable internal fragmentation. */

next:

			cachep->gfporder++;
		} while (1);
	}
	if (!cachep->num) {
  • キャッシュcachepの1スラブあたりのオブジェクト数が0である場合:
    • キャッシュを解放してoppsへジャンプ
		printk("kmem_cache_create: couldn't create cache %s.\n", name);
		kmem_cache_free(&cache_cache, cachep);
		cachep = NULL;
		goto opps;
	}
	slab_size = ALIGN(cachep->num*sizeof(kmem_bufctl_t)
				+ sizeof(struct slab), align);
  • (cachep->num*sizeof(kmem_bufctl_t)+ sizeof(struct slab) )をalign境界に合わせる
	/*
	 * If the slab has been placed off-slab, and we have enough space then
	 * move it on-slab. This is at the expense of any extra colouring.
	 */
	if (flags & CFLGS_OFF_SLAB && left_over >= slab_size) {
		flags &= ~CFLGS_OFF_SLAB;
		left_over -= slab_size;
	}
	if (flags & CFLGS_OFF_SLAB) {
		/* really off slab. No need for manual alignment */
		slab_size = cachep->num*sizeof(kmem_bufctl_t)+sizeof(struct slab);
	}
	cachep->colour_off = cache_line_size();
	/* Offset must be a multiple of the alignment. */
	if (cachep->colour_off < align)
		cachep->colour_off = align;
	cachep->colour = left_over/cachep->colour_off;
	cachep->slab_size = slab_size;
	cachep->flags = flags;
	cachep->gfpflags = 0;
	if (flags & SLAB_CACHE_DMA)
		cachep->gfpflags |= GFP_DMA;
	spin_lock_init(&cachep->spinlock);
	cachep->objsize = size;
	/* NUMA */
	INIT_LIST_HEAD(&cachep->lists.slabs_full);
	INIT_LIST_HEAD(&cachep->lists.slabs_partial);
	INIT_LIST_HEAD(&cachep->lists.slabs_free);
	if (flags & CFLGS_OFF_SLAB)
		cachep->slabp_cache = kmem_find_general_cachep(slab_size,0);
	cachep->ctor = ctor;
	cachep->dtor = dtor;
	cachep->name = name;
	/* Don't let CPUs to come and go */
	lock_cpu_hotplug();
	if (g_cpucache_up == FULL) {
		enable_cpucache(cachep);
	} else {
		if (g_cpucache_up == NONE) {
			/* Note: the first kmem_cache_create must create
			 * the cache that's used by kmalloc(24), otherwise
			 * the creation of further caches will BUG().
			 */
			cachep->array[smp_processor_id()] = &initarray_generic.cache;
			g_cpucache_up = PARTIAL;
		} else {
			cachep->array[smp_processor_id()] = kmalloc(sizeof(struct arraycache_init),GFP_KERNEL);
		}
		BUG_ON(!ac_data(cachep));
  • カレントCPUのオブジェクトキャッシュを返す
		ac_data(cachep)->avail = 0;
		ac_data(cachep)->limit = BOOT_CPUCACHE_ENTRIES;
		ac_data(cachep)->batchcount = 1;
		ac_data(cachep)->touched = 0;
		cachep->batchcount = 1;
		cachep->limit = BOOT_CPUCACHE_ENTRIES;
		cachep->free_limit = (1+num_online_cpus())*cachep->batchcount
					+ cachep->num;
	} 
	cachep->lists.next_reap = jiffies + REAPTIMEOUT_LIST3 +
					((unsigned long)cachep)%REAPTIMEOUT_LIST3;
	/* Need the semaphore to access the chain. */
	down(&cache_chain_sem);
  • セマフォcache_chain_semのカウンタをデクリメント(down)して新たなタスクを待ちキューへ追加する
    • 既に保留中のタスクが待ちキューに存在する場合は、それらの保留中のタスクが実行されるようにスケジューラを呼び出し続ける
    • 保留中のタスクが無くなった場合、自分のタスクを待ちキューから取り出し、起床させる
    • 詳細はdown()/linux2.6を参照
	{
		struct list_head *p;
		mm_segment_t old_fs;
		old_fs = get_fs();
  • カレントスレッドのアドレス空間の上限を返す
    • 0-0xBFFFFFFF:ユーザスレッド
    • 0-0xFFFFFFFF:カーネルスレッド
    • 詳細はget_fs()/linux2.6を参照
		set_fs(KERNEL_DS);
  • カレントスレッドのアドレス空間の上限にKERNEL_DS(カーネルデータセグメント)を設定する
		list_for_each(p, &cache_chain) {
			kmem_cache_t *pc = list_entry(p, kmem_cache_t, next);
  • list_headのポインタpから、それが組み込まれているオブジェクトkmem_cache_tのポインタを割り出す
			char tmp;
			/* This happens when the module gets unloaded and doesn't
			   destroy its slab cache and noone else reuses the vmalloc
			   area of the module. Print a warning. */
			if (__get_user(tmp,pc->name)) { 
  • ユーザアドレスpc->nameの値を、引数で与えられたカーネルアドレスtmpに読み込む
				printk("SLAB: cache with size %d has lost its name\n", 
					pc->objsize); 
				continue; 
			} 	
			if (!strcmp(pc->name,name)) { 
  • 2つの文字列を比較する
				printk("kmem_cache_create: duplicate cache %s\n",name); 
				up(&cache_chain_sem); 
  • セマフォカウンタcache_chain_semをインクリメント(up)して、待ちキューのタスクを起床させる
				unlock_cpu_hotplug();
				BUG(); 
			}	
		}
		set_fs(old_fs);
  • old_fsをカレントスレッドのアドレス空間の上限に設定する
	}
	/* cache setup completed, link it into the list */
	list_add(&cachep->next, &cache_chain);
  • リストcache_chainにエントリcachep->nextを追加する
	up(&cache_chain_sem);
  • セマフォカウンタcache_chain_semをインクリメント(up)して、待ちキューのタスクを起床させる
	unlock_cpu_hotplug();

opps:

	if (!cachep && (flags & SLAB_PANIC))
		panic("kmem_cache_create(): failed to create slab `%s'\n",
			name);
	return cachep;

}

EXPORT_SYMBOL(kmem_cache_create);

呼出元


履歴

  • 作者:ひら
  • 日付:2005/9/21
  • 対象:2.6.10
    更新日更新者更新内容

コメント



トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2014-03-29 (土) 09:07:14 (1333d)